public async Task TestReplaceAndRemove() { var key1 = "key1"; var value1 = "value1"; var fallback1 = "fallback1"; var s1 = new InMemoryKeyValueStore(); var s2 = NewFileBasedKeyValueStore("TestReplaceAndRemoveDir").WithFallbackStore(s1); var s3 = new InMemoryKeyValueStore().WithFallbackStore(s2); await s1.Set(key1, value1); // Test that replace with same value via s3 returns the old value (which is also value1): Assert.Equal(value1, await s3.Set(key1, value1)); // s3 will ask s2 which will ask s1 so the value will be returned correctly: Assert.Equal(value1, await s3.Get(key1, fallback1)); Assert.Single(await s3.GetAllKeys()); Assert.True(await s3.Remove(key1)); // Setting it again will return null since it was removed from all stores: Assert.Null(await s3.Set(key1, value1)); Assert.True(await s1.Remove(key1)); // Remove it only from s1 Assert.Equal(value1, await s3.Get(key1, fallback1)); // Still cached in s3 and s2 // s1 had the key already removed, so the combined remove result will be false: Assert.False(await s3.Remove(key1)); }
public async Task TestDualStore() { var firstStore = new InMemoryKeyValueStore(); var secondStor = new InMemoryKeyValueStore(); var dualStore = new DualStore(firstStore, secondStor); var key1 = "key1"; var key2 = "key2"; var value1 = "v1"; var fallb1 = "f1"; Assert.False(await dualStore.ContainsKey(key1)); Assert.Null(await dualStore.Set(key1, value1)); Assert.True(await dualStore.ContainsKey(key1)); Assert.True(await firstStore.ContainsKey(key1)); Assert.False(await secondStor.ContainsKey(key1)); Assert.Single(await dualStore.GetAllKeys()); Assert.Single(await firstStore.GetAllKeys()); Assert.Empty(await secondStor.GetAllKeys()); Assert.Equal(value1, await dualStore.Get(key1, fallb1)); Assert.Equal(value1, await firstStore.Get(key1, fallb1)); Assert.Equal(fallb1, await secondStor.Get(key1, fallb1)); Assert.Equal(fallb1, await dualStore.Get(key2, fallb1)); Assert.True(await dualStore.Remove(key1)); Assert.Equal(fallb1, await dualStore.Get(key1, fallb1)); Assert.Equal(fallb1, await firstStore.Get(key1, fallb1)); Assert.False(await dualStore.Remove(key2)); }
public async Task ExtendedTest1() { // Get your key from https://console.developers.google.com/apis/credentials var apiKey = "AIzaSyCtcFQMgRIUHhSuXggm4BtXT4eZvUrBWN0"; // See https://docs.google.com/spreadsheets/d/1KBamVmgEUX-fyogMJ48TT6h2kAMKyWU1uBL5skCGRBM var sheetId = "1KBamVmgEUX-fyogMJ48TT6h2kAMKyWU1uBL5skCGRBM"; var sheetName = "MySheet1"; // Has to match the sheet name var googleSheetsStore = new GoogleSheetsKeyValueStore(new InMemoryKeyValueStore(), apiKey, sheetId, sheetName); var localStore = new InMemoryKeyValueStore(); var testStore = new FeatureFlagStore(localStore, googleSheetsStore); IoC.inject.SetSingleton <FeatureFlagManager <FeatureFlag> >(new FeatureFlagManager <FeatureFlag>(testStore)); var key1 = "MyFlag1"; var key2 = "MyFlag2"; var key3 = "MyFlag3"; Assert.NotNull(await googleSheetsStore.Get <FeatureFlag>(key1, null)); Assert.NotNull(await googleSheetsStore.Get <FeatureFlag>(key2, null)); Assert.NotNull(await testStore.Get(key2, null)); Assert.NotNull(await FeatureFlagManager <FeatureFlag> .instance.GetFeatureFlag(key2)); Assert.False(await FeatureFlag.IsEnabled(key1)); Assert.True(await FeatureFlag.IsEnabled(key2)); var flag3_1 = await FeatureFlagManager <FeatureFlag> .instance.GetFeatureFlag(key3); Assert.Equal(40, flag3_1.rolloutPercentage); var localState3_1 = await localStore.Get <IFeatureFlagLocalState>(key3, null); var percent3_1 = localState3_1.randomPercentage; Assert.NotEqual(0, percent3_1); if (percent3_1 < flag3_1.rolloutPercentage) { Assert.True(await FeatureFlag.IsEnabled(key3)); } else { Assert.False(await FeatureFlag.IsEnabled(key3)); } localState3_1.randomPercentage = flag3_1.rolloutPercentage - 1; await localStore.Set(key3, localState3_1); var localState3_2 = await localStore.Get <IFeatureFlagLocalState>(key3, null); Assert.NotEqual(percent3_1, localState3_2.randomPercentage); var flag3_2 = await FeatureFlagManager <FeatureFlag> .instance.GetFeatureFlag(key3); Assert.Equal(localState3_2.randomPercentage, flag3_2.localState.randomPercentage); Assert.True(await flag3_2.IsEnabled()); Assert.True(await FeatureFlag.IsEnabled(key3)); }
public async Task ExampleUsage2() { var storeDir = EnvironmentV2.instance.GetOrAddTempFolder("KeyValueStoreTests").GetChildDir("ExampleUsage2Dir"); storeDir.DeleteV2(); // Cleanup before tests if the test file exists string myKey1 = "test123"; MyClass1 x1 = new MyClass1() { myString1 = "Abc", myString2 = "Abc2" }; { // Create a fast memory store and combine it with a LiteDB store that is persisted to disk: IKeyValueStore store = new InMemoryKeyValueStore().WithFallbackStore(new FileBasedKeyValueStore(storeDir)); await store.Set(myKey1, x1); MyClass1 x2 = await store.Get <MyClass1>(myKey1, null); Assert.Equal(x1.myString1, x2.myString1); Assert.Equal(x1.myString2, x2.myString2); } { // Create a second store and check that the changes were persisted: IKeyValueStore store2 = new FileBasedKeyValueStore(storeDir); Assert.True(await store2.ContainsKey(myKey1)); MyClass1 x2 = await store2.Get <MyClass1>(myKey1, null); Assert.Equal(x1.myString1, x2.myString1); Assert.Equal(x1.myString2, x2.myString2); Assert.True(await store2.Remove(myKey1)); Assert.False(await store2.ContainsKey(myKey1)); } }
public async Task ExampleUsage1() { // Get your key from https://console.developers.google.com/apis/credentials var apiKey = "AIzaSyCtcFQMgRIUHhSuXggm4BtXT4eZvUrBWN0"; // See https://docs.google.com/spreadsheets/d/1Hwu4ZtRR0iXD65Wuj_XyJxLw4PN8SE0sRgnBKeVoq3A var sheetId = "1Hwu4ZtRR0iXD65Wuj_XyJxLw4PN8SE0sRgnBKeVoq3A"; var sheetName = "MySheet1"; // Has to match the sheet name IKeyValueStore newsStore = new GoogleSheetsKeyValueStore(new InMemoryKeyValueStore(), apiKey, sheetId, sheetName); var newsLocalDataStore = new InMemoryKeyValueStore().GetTypeAdapter <News.LocalData>(); NewsManager manager = new NewsManager(newsLocalDataStore, newsStore.GetTypeAdapter <News>()); IEnumerable <News> allNews = await manager.GetAllNews(); var news1 = allNews.First(); Assert.NotNull(news1); Assert.Equal("New", news1.type); Assert.Equal(News.NewsType.New, news1.GetNewsType()); Assert.True(news1.GetDate(true).IsUtc()); // Mark that the user has read the news: await manager.MarkNewsAsRead(news1); Assert.True(allNews.First().localData.isRead); Assert.True((await manager.GetAllNews()).First().localData.isRead); Assert.True((await newsLocalDataStore.Get(news1.key, null)).isRead); IEnumerable <News> unreadNews = await manager.GetAllUnreadNews(); Assert.Contains(news1, allNews); Assert.DoesNotContain(news1, unreadNews); }
public async Task TestDelayStoreWithExponentialBackoffRetry() { // Simulates the DB on the server: var innerStore = new InMemoryKeyValueStore(); // Simulates the connection to the server: var simulatedRemoteConnection = new MockDekayKeyValueStore().WithFallbackStore(innerStore); var requestRetry = new RetryKeyValueStore(simulatedRemoteConnection, maxNrOfRetries: 5); var outerStore = new InMemoryKeyValueStore().WithFallbackStore(requestRetry); var key1 = "key1"; var value1 = "value1"; var key2 = "key2"; var value2 = "value2"; var fallback2 = "fallback2"; { var delayedSetTask = outerStore.Set(key1, value1); Assert.Equal(value1, await outerStore.Get(key1, "")); // The outer store already has the update Assert.NotEqual(value1, await innerStore.Get(key1, "")); // The inner store did not get the update yet // After waiting for set to fully finish the inner store has the update too: await delayedSetTask; Assert.Equal(value1, await innerStore.Get(key1, "")); } // Now simulate that the remote DB/server never can be reached: simulatedRemoteConnection.throwTimeoutError = true; { var timeoutErrorCounter = 0; // In the retry listen to any error if the wrapped store: requestRetry.onError = (e) => { Assert.IsType <TimeoutException>(e); // thrown by the simulatedRemoteConnection timeoutErrorCounter++; }; var delayedSetTask = outerStore.Set(key2, value2); Assert.Equal(value2, await outerStore.Get(key2, fallback2)); // In the outer store the value was set Assert.False(await innerStore.ContainsKey(key2)); // The inner store never got the update // The delayedSetTask was canceled after 5 retries: await Assert.ThrowsAsync <OperationCanceledException>(async() => await delayedSetTask); // There will be 5 TimeoutException in the simulatedRemoteConnection: Assert.Equal(requestRetry.maxNrOfRetries, timeoutErrorCounter); } }
private async Task TestPlayerPrefsFromBackgroundThreadTasks() { var myKey1 = "myKey1"; var myVal1 = "myVal1"; var myFallback1 = "myFallback1"; var innerStore = new ExceptionWrapperKeyValueStore(new PlayerPrefsStore()); await innerStore.Remove(myKey1); // Cleanup prefs from previous tests var outerStore = new InMemoryKeyValueStore().WithFallbackStore(innerStore); var task = TaskRunner.instance.RunInBackground(async(cancel) => { cancel.ThrowIfCancellationRequested(); Assert.IsFalse(MainThread.isMainThread); var innerStoreThrewAnError = false; // Set and Get from a background thread will throw an exception in the innerStore innerStore.onError = (e) => { innerStoreThrewAnError = true; }; // So only the outerStore will be updated when calling Set and Get from the background: await outerStore.Set(myKey1, myVal1); Assert.IsTrue(innerStoreThrewAnError); var x = await outerStore.Get(myKey1, myFallback1); // The value returned by Get was cached in the outer store so it will be correct: Assert.AreEqual(myVal1, x); // Check that the Set request never reached the real pref. store: Assert.AreEqual(myFallback1, await innerStore.Get(myKey1, myFallback1)); }).task; await task; Assert.IsTrue(task.IsCompleted); Assert.IsNull(task.Exception); Assert.IsTrue(MainThread.isMainThread); // There should not be any errors when working on the main thread so // throw any errors that happen in the inner store: innerStore.onError = (e) => { throw e; }; // In the main thread Set and Get will not throw errors: await outerStore.Set(myKey1, myVal1); Assert.AreEqual(myVal1, await outerStore.Get(myKey1, myFallback1)); // Check that the Set request never reached the real pref. store: Assert.AreEqual(myVal1, await innerStore.Get(myKey1, myFallback1)); }
public async Task TestCachingValuesFromFallbackStores() { var key1 = "key1"; var value1 = "value1"; var fallback1 = "fallback1"; var s1 = new InMemoryKeyValueStore(); var s2 = NewFileBasedKeyValueStore("TestCachingValuesFromFallbackStoresDir").WithFallbackStore(s1); var s3 = new InMemoryKeyValueStore().WithFallbackStore(s2); await s1.Set(key1, value1); // s3 will ask s2 which will ask s1 so the value will be returned correctly: Assert.Equal(value1, await s3.Get(key1, fallback1)); // Now the value should also be cached in the other stores, so s1 is not needed anymore: s2.fallbackStore = null; s3.fallbackStore = null; Assert.Equal(value1, await s2.Get(key1, fallback1)); Assert.Equal(value1, await s3.Get(key1, fallback1)); }
public async Task TestStoreWithDelay() { // Simulates the DB on the server: var innerStore = new InMemoryKeyValueStore(); // Simulates the connection to the server: var simulatedDelayStore = new MockDekayKeyValueStore().WithFallbackStore(innerStore); // Handles connection problems to the server: var exWrapperStore = new ExceptionWrapperKeyValueStore(simulatedDelayStore); // Represents the local cache in case the server cant be reached: var outerStore = new InMemoryKeyValueStore().WithFallbackStore(exWrapperStore); var key1 = "key1"; var value1 = "value1"; var key2 = "key2"; var value2 = "value2"; { var delayedSetTask = outerStore.Set(key1, value1); Assert.Equal(value1, await outerStore.Get(key1, "")); // The outer store already has the update Assert.NotEqual(value1, await innerStore.Get(key1, "")); // The inner store did not get the update yet // After waiting for set to fully finish the inner store has the update too: await delayedSetTask; Assert.Equal(value1, await innerStore.Get(key1, "")); } simulatedDelayStore.throwTimeoutError = true; var simulatedErrorCatched = false; exWrapperStore.onError = (Exception e) => { simulatedErrorCatched = true; }; { await outerStore.Set(key2, value2); // This will cause a timeout error in the "delayed" store Assert.True(simulatedErrorCatched); Assert.Contains(key2, await outerStore.GetAllKeys()); // In the outer store the value was set Assert.False(await innerStore.ContainsKey(key2)); // The inner store never got the update Assert.False(await exWrapperStore.ContainsKey(key2)); // The exc. wrapper returns false if an error is thrown Assert.Null(await exWrapperStore.GetAllKeys()); // Will throw another error and return null } }
public async Task ExampleUsage3() { // Simulate the DB on the server var simulatedDb = new InMemoryKeyValueStore(); // Simulate the connection (with a delay) to the server: var simulatedRemoteConnection = new MockDelayKeyValueStore(simulatedDb); // The connection to the server is wrapped by a automatic retry for failing requests: var requestRetry = new RetryKeyValueStore(simulatedRemoteConnection, maxNrOfRetries: 5); // Any errors in the inner layers like connection errors, DB errors are catched by default: var errorHandler = new ExceptionWrapperKeyValueStore(requestRetry); // The outer store is a local in memory cache and the main point of contact: var outerStore = new InMemoryKeyValueStore().WithFallbackStore(errorHandler); var key1 = "key1"; var value1 = "value1"; var fallback1 = "fallback1"; await outerStore.Set(key1, value1); Assert.Equal(value1, await outerStore.Get(key1, fallback1)); Assert.Equal(value1, await simulatedDb.Get(key1, fallback1)); // Simmulate connection problems to the remote DB: simulatedRemoteConnection.throwTimeoutError = true; var key2 = "key2"; var value2 = "value2"; var fallback2 = "fallback2"; // Awaiting a set will take some time since there will be 5 retries: await outerStore.Set(key2, value2); // The outer store has the set value cached: Assert.Equal(value2, await outerStore.Get(key2, fallback2)); // But the request never reached the simulated DB: Assert.False(await simulatedDb.ContainsKey(key2)); }
public void ExampleUsage1() { IKeyValueStore store = new InMemoryKeyValueStore(); string myKey1 = "myKey1"; MyClass1 x1 = new MyClass1() { myString1 = "Abc", myString2 = "Abc2" }; store.Set(myKey1, x1); MyClass1 x2 = store.Get <MyClass1>(myKey1, defaultValue: null).Result; Assert.Equal(x1.myString1, x2.myString1); Assert.Equal(x1.myString2, x2.myString2); }
public void PutKeyExist() { var serdes = new StringSerDes(); byte[] key = serdes.Serialize("key", new SerializationContext()), value = serdes.Serialize("value", new SerializationContext()), value2 = serdes.Serialize("value2", new SerializationContext()); var store = new InMemoryKeyValueStore("store"); store.Put(new Bytes(key), value); store.Put(new Bytes(key), value2); Assert.AreEqual(1, store.ApproximateNumEntries()); var v = store.Get(new Bytes(key)); Assert.AreEqual("value2", serdes.Deserialize(v, new SerializationContext())); }
public async Task TestExceptionCatching() { var myKey1 = "key1"; int myValue1 = 1; string myDefaultString = "myDefaultValue"; var innerStore = new InMemoryKeyValueStore(); var exHandlerStore = new ExceptionWrapperKeyValueStore(innerStore, new HashSet <Type>()); await innerStore.Set(myKey1, myValue1); // Cause an InvalidCastException: await Assert.ThrowsAsync <InvalidCastException>(() => innerStore.Get <string>(myKey1, myDefaultString)); // Cause an InvalidCastException which is then catched and instead the default is returned: string x = await exHandlerStore.Get <string>(myKey1, myDefaultString); Assert.Equal(myDefaultString, x); // Add the InvalidCastException to the list of errors that should not be ignored: exHandlerStore.errorTypeBlackList.Add(typeof(InvalidCastException)); // Now the same Get request passes the InvalidCastException on: await Assert.ThrowsAsync <InvalidCastException>(() => exHandlerStore.Get <string>(myKey1, myDefaultString)); }