public async Task Load_WhenValidToggleAndEtagFilesExist_ReturnsExpectedResult(
            [Frozen] UnleashSettings settings,
            [Frozen] Mock <IFileSystem> fileSystem,
            [Frozen] Mock <IJsonSerializer> jsonSerializer,
            FileSystemToggleCollectionCache cache,
            ToggleCollection toggleCollection,
            string etag
            )
        {
            var toggleFilePath = settings.GetFeatureToggleFilePath();
            var etagFilePath   = settings.GetFeatureToggleETagFilePath();

            fileSystem.Setup(fs => fs.FileExists(toggleFilePath)).Returns(true);
            fileSystem.Setup(fs => fs.FileExists(etagFilePath)).Returns(true);

            var ms = new MemoryStream();

            fileSystem.Setup(fs => fs.FileOpenRead(toggleFilePath)).Returns(ms);
            fileSystem.Setup(fs => fs.ReadAllText(etagFilePath)).Returns(etag);

            jsonSerializer.Setup(js => js.Deserialize <ToggleCollection>(ms)).Returns(toggleCollection);

            var result = await cache.Load(CancellationToken.None);

            Assert.Equal(etag, result.InitialETag);
            Assert.Same(toggleCollection, result.InitialToggleCollection);

            fileSystem.VerifyAll();
            jsonSerializer.VerifyAll();
        }
        public async Task Load_WhenTogglesOrEtagFileDoesNotExists_ReturnsEmptyResult(
            bool toggleCollectionExists,
            bool etagExists,
            [Frozen] MemoryToggleCollectionCacheSettings settings,
            [Frozen] MemoryCache memoryCache,
            MemoryToggleCollectionCache cache
            )
        {
            settings.EtagKeyName             = "Etag";
            settings.ToggleCollectionKeyName = "Toggles";

            if (toggleCollectionExists)
            {
                var toggleCollection = new ToggleCollection();
                memoryCache.Set(settings.ToggleCollectionKeyName, toggleCollection);
            }

            if (toggleCollectionExists && etagExists)
            {
                var etag = Guid.NewGuid().ToString();
                memoryCache.Set(settings.EtagKeyName, etag);
            }

            var result = await cache.Load(CancellationToken.None);

            Assert.Equal(string.Empty, result.InitialETag);
            Assert.Null(result.InitialToggleCollection);
        }
 public async Task Save_WhenEtagIsNull_ThrowsArgumentNullException(
     DistributedToggleCollectionCache cache,
     ToggleCollection toggleCollection
     )
 {
     await Assert.ThrowsAsync <ArgumentNullException>(() => cache.Save(toggleCollection, null, CancellationToken.None));
 }
示例#4
0
        internal async Task FetchToggles_WhenServerReturnsExpectedToggles_ReturnsValidResult(
            UnleashSettings settings,
            [Frozen] Mock <FakeHttpMessageHandler> httpMessageHandler,
            [Frozen] UnleashApiClientRequestHeaders requestHeaders,
            string etag
            )
        {
            var jsonSerializerSettings = new NewtonsoftJsonSerializerSettings();
            var jsonSerializer         = new NewtonsoftJsonSerializer(jsonSerializerSettings);

            var expectedToggles = new ToggleCollection();

            httpMessageHandler.SetupGetFeaturesRequestForSuccess(jsonSerializer, expectedToggles, requestHeaders);

            var httpClient = new HttpClient(httpMessageHandler.Object)
            {
                BaseAddress = settings.UnleashApi
            };
            var apiClient = new UnleashApiClient(httpClient, jsonSerializer, requestHeaders);

            var actualFetchTogglesResult = await apiClient.FetchToggles(etag, CancellationToken.None);

            var actualToggles = actualFetchTogglesResult.ToggleCollection;

            Assert.Equal(expectedToggles.Features, actualToggles.Features);
            Assert.True(actualFetchTogglesResult.HasChanged);
            Assert.NotEqual(etag, actualFetchTogglesResult.Etag);

            httpMessageHandler.VerifyAll();
        }
示例#5
0
        public void Dispose()
        {
            if (!cancellationTokenSource.IsCancellationRequested)
            {
                cancellationTokenSource.Cancel();
            }

            scheduledTaskManager?.Dispose();
            ToggleCollection?.Dispose();
        }
示例#6
0
        public void Serialize_SameAsNewtonSoft(Type type)
        {
            Console.WriteLine(type.FullName);
            Console.WriteLine();

            var collection = new ToggleCollection(new List <FeatureToggle>()
            {
                new FeatureToggle("one", true, new List <ActivationStrategy>()
                {
                    new ActivationStrategy("userByName", new Dictionary <string, string>()
                    {
                        { "Demo", "Demo" }
                    })
                }),
                new FeatureToggle("two", false, new List <ActivationStrategy>()
                {
                    new ActivationStrategy("userByName2", new Dictionary <string, string>()
                    {
                        { "demo", "demo" }
                    })
                })
            });

            var serializer = (IDynamicJsonSerializer)Activator.CreateInstance(type);

            serializer.TryLoad()
            .Should().BeTrue();

            var expected = JsonConvert.SerializeObject(collection, new JsonSerializerSettings()
            {
                ContractResolver = new CamelCasePropertyNamesContractResolver()
                {
                    NamingStrategy = new CamelCaseNamingStrategy()
                    {
                        ProcessDictionaryKeys = false,
                    }
                }
            });

            expected.Should().Contain("\"Demo\":\"Demo\"");
            expected.Should().Contain("\"demo\":\"demo\"");

            using (var ms = new MemoryStream())
            {
                serializer.Serialize(ms, collection);

                var resultingJson = ms.ConvertToString();
                Console.WriteLine(resultingJson);

                resultingJson.Should().Contain("\"Demo\":\"Demo\"");
                resultingJson.Should().Contain("\"demo\":\"demo\"");

                resultingJson.Should().Be(expected);
            }
        }
        public async Task Save_WhenCancellationTokenIsCanceled_ThrowsOperationCancelledException(
            DistributedToggleCollectionCache cache,
            ToggleCollection toggleCollection,
            string etag
            )
        {
            var cts = new CancellationTokenSource();

            cts.Cancel();

            await Assert.ThrowsAsync <OperationCanceledException>(() => cache.Save(toggleCollection, etag, cts.Token));
        }
示例#8
0
        public void test_read_without_file()
        {
            UnleashConfig config = UnleashConfig.TheBuilder()
                                   .AppName("test")
                                   .UnleashAPI("http://unleash.org")
                                   .BackupFile("/does/not/exist.json")
                                   .Build();

            ToggleBackupHandlerFile toggleBackupHandlerFile = new ToggleBackupHandlerFile(config);
            ToggleCollection        toggleCollection        = toggleBackupHandlerFile.Read();

            Assert.Null(toggleCollection.GetToggle("presentFeature"));
        }
        protected string SerializeToggleCollection(ToggleCollection toggleCollection)
        {
            var serializer = CreateSerializer();

            using (var memoryStream = new MemoryStream())
            {
                serializer.Serialize(memoryStream, toggleCollection);
                memoryStream.Seek(0, SeekOrigin.Begin);

                using (var streamReader = new StreamReader(memoryStream))
                {
                    return(streamReader.ReadToEnd());
                }
            }
        }
示例#10
0
        /// <inheritdoc />
        public Task Save(ToggleCollection toggleCollection, string etag, CancellationToken cancellationToken)
        {
            if (toggleCollection == null)
            {
                throw new ArgumentNullException(nameof(toggleCollection));
            }
            if (etag == null)
            {
                throw new ArgumentNullException(nameof(etag));
            }

            cancellationToken.ThrowIfCancellationRequested();

            MemoryCache.Set(Settings.ToggleCollectionKeyName, toggleCollection, Settings.ToggleCollectionEntryOptions);
            MemoryCache.Set(Settings.EtagKeyName, etag, Settings.EtagEntryOptions);

            return(Task.CompletedTask);
        }
        public async Task Load_WhenValidToggleAndEtagFilesExist_ReturnsExpectedResult(
            [Frozen] MemoryToggleCollectionCacheSettings settings,
            [Frozen] MemoryCache memoryCache,
            ToggleCollection toggleCollection,
            string etag
            )
        {
            settings.EtagKeyName             = "Etag";
            settings.ToggleCollectionKeyName = "Toggles";

            memoryCache.Set(settings.ToggleCollectionKeyName, toggleCollection);
            memoryCache.Set(settings.EtagKeyName, etag);

            var cache  = new MemoryToggleCollectionCache(settings, memoryCache);
            var result = await cache.Load(CancellationToken.None);

            Assert.Equal(etag, result.InitialETag);
            Assert.Same(toggleCollection, result.InitialToggleCollection);
        }
示例#12
0
        public async Task WithHttpClientFactory_RegistersNecessaryServices(
            [Frozen] Mock <FakeHttpMessageHandler> httpMessageHandler,
            [Frozen] ToggleCollection expectedToggleCollection,
            string etag
            )
        {
            var serviceCollection = new ServiceCollection();

            serviceCollection.AddUnleash(s =>
            {
                s.UnleashApi  = new Uri("http://localhost:4242/");
                s.AppName     = "Test";
                s.InstanceTag = "Test";
            })
            .WithNewtonsoftJsonSerializer()
            .WithHttpClientFactory(
                cb =>
            {
                cb.ConfigurePrimaryHttpMessageHandler(() => httpMessageHandler.Object);
            });

            var serviceProvider = serviceCollection.BuildServiceProvider(true);

            var unleashApiClientFactory = serviceProvider.GetRequiredService <IUnleashApiClientFactory>();

            Assert.IsType <HttpClientFactoryApiClientFactory>(unleashApiClientFactory);

            var unleashApiClient = unleashApiClientFactory.CreateClient();

            Assert.IsType <UnleashApiClient>(unleashApiClient);

            var concreteUnleashApiClient = (UnleashApiClient)unleashApiClient;

            var clientRequestHeaders = concreteUnleashApiClient.ClientRequestHeaders;

            httpMessageHandler.SetupGetFeaturesRequestForSuccess(concreteUnleashApiClient.JsonSerializer, expectedToggleCollection, clientRequestHeaders);
            var fetchTogglesResult = await unleashApiClient.FetchToggles(etag, CancellationToken.None);

            var actualToggleCollection = fetchTogglesResult.ToggleCollection;

            AssertionUtils.AssertToggleCollectionsEquivalent(expectedToggleCollection, actualToggleCollection);
        }
        public void Serialize_WhenValidObject_StringReturned()
        {
            //Arrange
            var value = new ToggleCollection()
            {
                Features = new List <FeatureToggle>()
                {
                    new FeatureToggle()
                    {
                        Name = "example-feature-flag", Enabled = true
                    }
                }
            };

            //Act
            var result = serializer.Serialize(value);

            //Assert
            Assert.Contains("example-feature-flag", result);
            Assert.Contains("true", result);
        }
示例#14
0
        /// <inheritdoc />
        public Task Save(ToggleCollection toggleCollection, string etag, CancellationToken cancellationToken)
        {
            if (toggleCollection == null)
            {
                throw new ArgumentNullException(nameof(toggleCollection));
            }
            if (etag == null)
            {
                throw new ArgumentNullException(nameof(etag));
            }

            cancellationToken.ThrowIfCancellationRequested();

            using (var fs = fileSystem.FileOpenCreate(toggleFile))
            {
                jsonSerializer.Serialize(fs, toggleCollection);
            }

            fileSystem.WriteAllText(etagFile, etag);
            return(Task.FromResult <object>(null));
        }
        public async Task Load_WhenValidToggleAndEtagFilesExist_ReturnsExpectedResult(
            [Frozen] MemoryDistributedCache distributedCache,
            ToggleCollection toggleCollection,
            string etag
            )
        {
            var jsonSerializerSettings = new NewtonsoftJsonSerializerSettings();
            var jsonSerializer         = new NewtonsoftJsonSerializer(jsonSerializerSettings);

            var settings = new DistributedToggleCollectionCacheSettings
            {
                EtagEntryOptions =
                {
                    AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1)
                },
                ToggleCollectionEntryOptions =
                {
                    AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1)
                }
            };

            using (var ms = new MemoryStream())
            {
                jsonSerializer.Serialize(ms, toggleCollection);
                ms.Seek(0, SeekOrigin.Begin);

                await distributedCache.SetAsync(settings.ToggleCollectionKeyName, ms.ToArray(), settings.ToggleCollectionEntryOptions, CancellationToken.None).ConfigureAwait(false);

                await distributedCache.SetStringAsync(settings.EtagKeyName, etag, settings.EtagEntryOptions, CancellationToken.None).ConfigureAwait(false);
            }

            var cache  = new DistributedToggleCollectionCache(settings, distributedCache, jsonSerializer);
            var result = await cache.Load(CancellationToken.None);

            Assert.Equal(etag, result.InitialETag);

            AssertionUtils.AssertToggleCollectionsEquivalent(toggleCollection, result.InitialToggleCollection);
        }
        public async Task Load_WhenTogglesOrEtagFileDoesNotExists_ReturnsEmptyResult(
            bool toggleCollectionExists,
            bool etagExists,
            [Frozen] DistributedToggleCollectionCacheSettings settings,
            [Frozen] MemoryDistributedCache distributedCache,
            DistributedToggleCollectionCache cache
            )
        {
            var jsonSerializerSettings = new NewtonsoftJsonSerializerSettings();
            var jsonSerializer         = new NewtonsoftJsonSerializer(jsonSerializerSettings);

            settings.EtagKeyName             = "Etag";
            settings.ToggleCollectionKeyName = "Toggles";

            if (toggleCollectionExists)
            {
                var toggleCollection = new ToggleCollection();

                using (var ms = new MemoryStream())
                {
                    jsonSerializer.Serialize(ms, toggleCollection);
                    ms.Seek(0, SeekOrigin.Begin);

                    await distributedCache.SetAsync(settings.ToggleCollectionKeyName, ms.ToArray(), settings.ToggleCollectionEntryOptions, CancellationToken.None).ConfigureAwait(false);
                }
            }

            if (toggleCollectionExists && etagExists)
            {
                var etag = Guid.NewGuid().ToString();
                distributedCache.SetString(settings.EtagKeyName, etag);
            }

            var result = await cache.Load(CancellationToken.None);

            Assert.Equal(string.Empty, result.InitialETag);
            Assert.Null(result.InitialToggleCollection);
        }
        /// <inheritdoc />
        public async Task Save(ToggleCollection toggleCollection, string etag, CancellationToken cancellationToken)
        {
            if (toggleCollection == null)
            {
                throw new ArgumentNullException(nameof(toggleCollection));
            }
            if (etag == null)
            {
                throw new ArgumentNullException(nameof(etag));
            }

            cancellationToken.ThrowIfCancellationRequested();

            using (var ms = new MemoryStream())
            {
                jsonSerializer.Serialize(ms, toggleCollection);
                ms.Seek(0, SeekOrigin.Begin);

                await distributedCache.SetAsync(Settings.ToggleCollectionKeyName, ms.ToArray(), Settings.ToggleCollectionEntryOptions, cancellationToken).ConfigureAwait(false);

                await distributedCache.SetStringAsync(Settings.EtagKeyName, etag, Settings.EtagEntryOptions, CancellationToken.None).ConfigureAwait(false);
            }
        }
示例#18
0
 private ToggleCollectionCacheResult(ToggleCollection initialToggleCollection, string initialETag, bool isCacheMiss)
 {
     InitialToggleCollection = initialToggleCollection;
     IsCacheMiss             = isCacheMiss;
     InitialETag             = initialETag ?? string.Empty;
 }
示例#19
0
 public static ToggleCollectionCacheResult FromResult(ToggleCollection initialToggleCollection, string initialETag)
 => new ToggleCollectionCacheResult(initialToggleCollection, initialETag, false);
示例#20
0
 /// <summary>
 /// Initializes a new instance of the <see cref="DrinkToggle" /> class.
 /// </summary>
 /// <param name="toggleCollection">The toggle collection.</param>
 public DrinkToggle(ToggleCollection toggleCollection)
     : base(toggleCollection)
 {
 }
示例#21
0
        private static HttpResponseMessage SerializedMessageResponse(IJsonSerializer jsonSerializer, ToggleCollection toggleCollectionResult)
        {
            var(payload, etag) = SerializePayloadAndCreateEtag(jsonSerializer, toggleCollectionResult);

            var response = new HttpResponseMessage
            {
                StatusCode = HttpStatusCode.OK,
                Version    = new Version(1, 1),
                Content    = new ByteArrayContent(payload)
            };

            response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
            response.Headers.ETag = new EntityTagHeaderValue($"\"{etag}\"");

            return(response);
        }
示例#22
0
        public static void AssertToggleCollectionsEquivalent(ToggleCollection expectedToggleCollection, ToggleCollection actualToggleCollection)
        {
            Assert.Equal(expectedToggleCollection.Version, actualToggleCollection.Version);

            AssertFeaturesEquivalent(expectedToggleCollection.Features, actualToggleCollection.Features);
        }
示例#23
0
 /// <summary>
 /// Initializes a new instance of the <see cref="FoodToggle" /> class.
 /// </summary>
 /// <param name="toggleCollection">The toggle collection.</param>
 public FoodToggle(ToggleCollection toggleCollection)
     : base(toggleCollection)
 {
 }
示例#24
0
 internal static void SetupGetFeaturesRequestForSuccess(this Mock <FakeHttpMessageHandler> mock,
                                                        IJsonSerializer jsonSerializer, ToggleCollection toggleCollectionResult,
                                                        UnleashApiClientRequestHeaders requestHeaders)
 {
     mock
     .Setup(mh => mh.Send(It.Is(ApiRequest(HttpMethod.Get, "api/client/features", requestHeaders))))
     .Returns(() => SerializedMessageResponse(jsonSerializer, toggleCollectionResult));
 }
示例#25
0
 /// <summary>
 /// Initializes a new instance of the <see cref="RescueToggle"/> class.
 /// </summary>
 /// <param name="toggleCollection">The toggle collection.</param>
 public RescueToggle(ToggleCollection toggleCollection)
     : base(toggleCollection)
 {
 }