From 8267ab7f7d3e058287fea6dd12a8414a99ddfac6 Mon Sep 17 00:00:00 2001 From: Ninja Date: Thu, 8 Dec 2022 23:53:18 +0000 Subject: [PATCH] - Refactor code --- README.md | 175 +++++++++--------- src/FeatureOne/Configuration.cs | 10 - src/FeatureOne/Core/ICondition.cs | 2 +- src/FeatureOne/Core/Stores/FeatureStore.cs | 23 ++- .../Core/Stores/IStorageProvider.cs | 19 ++ src/FeatureOne/Core/Stores/IStoreProvider.cs | 32 ---- .../Core/Stores/NullStoreProvder.cs | 2 +- src/FeatureOne/Core/Toggle.cs | 2 + src/FeatureOne/FeatureOne.csproj | 6 +- src/FeatureOne/Features.cs | 30 +-- src/FeatureOne/ServiceRegistrations.cs | 34 ---- .../E2E Tests/ConsoleLogger.cs | 25 +++ test/FeatureOne.Tests/E2E Tests/E2ETests.cs | 29 +++ .../E2E Tests/StorageProvider.cs | 16 ++ test/FeatureOne.Tests/FeatureOne.Tests.csproj | 4 - test/FeatureOne.Tests/FeaturesTests.cs | 5 +- .../Registeration/RegistrationTests.cs | 59 ------ .../Stores/FeatureStoreTests.cs | 11 +- 18 files changed, 220 insertions(+), 264 deletions(-) delete mode 100644 src/FeatureOne/Configuration.cs create mode 100644 src/FeatureOne/Core/Stores/IStorageProvider.cs delete mode 100644 src/FeatureOne/Core/Stores/IStoreProvider.cs delete mode 100644 src/FeatureOne/ServiceRegistrations.cs create mode 100644 test/FeatureOne.Tests/E2E Tests/ConsoleLogger.cs create mode 100644 test/FeatureOne.Tests/E2E Tests/E2ETests.cs create mode 100644 test/FeatureOne.Tests/E2E Tests/StorageProvider.cs delete mode 100644 test/FeatureOne.Tests/Registeration/RegistrationTests.cs diff --git a/README.md b/README.md index 082fd47..89b1692 100644 --- a/README.md +++ b/README.md @@ -18,121 +18,117 @@ How to use FeatureOne -- -### Step 1. +### Step 1. Add Feature IsEnabled Check in Code. In order to release a new functionality or feature - say eg. Dashboard Widget. Add logical check in codebase to wrap the functionality under a `feature toggle`. > the logical check evaluates status of the toggle configured for the feature in store at runtime. ``` var featureName = "dashboard_widget"; // Name of functionality or feature to toggle. - if(Features.IsEnable(featureName, claimsPrincipal){ + if(Features.Current.IsEnable(featureName, claimsPrincipal){ // See other IsEnable() overloads showDashboardWidget(); } ``` -### Step 2. -Add `toggle` to the store (ie. a store in database or file or other medium) in order to conditionally enable/disable the feature. -A toggle constitutes a collection of `conditions` that evaluate separately when the toggle is run. You can additionally specify an `operator` on the toggle to determine the overall success to include success of `any` constituent condition or success of `all` consituent conditions. +### Step 2. Add Feature Toggle Definition to Storage +Add a `toggle` definition to storage ie. a store in database or file or other storage medium. +A toggle constitutes a collection of `conditions` that evaluate separately when the toggle is run. You can additionally specify an `operator` in the toggle definition to determine the overall success to include success of `any` constituent condition or success of `all` consituent conditions. > Toggles run at runtime based on consitituent conditions that evaluate separately against user claims (generally logged in user principal). - -There are two types of conditions that can be used out of box for the toggles. +JSON Syntax for Feature Toggle is below +``` +{ + "feature_name":{ -- Feature name + "toggle":{ -- Toggle details for the feature + + "operator":"any|all", -- Evaluate overall toggle to true + -- when `any` condition is met or `all` conditions are met. + + "conditions":[{ -- collection of conditions + "type":"simple|regex" -- type of condition + + .... other type specific properties, See below for details. + }] + } + } +} +``` +### Condition Types +There are two types of toggle conditions that can be used out of box. #### i. Simple Condition -`Simple` condition allows simple toggle to enable or disable of the feature. User claims are not taken into account for this condition. +`Simple` condition allows toggle with simple enable or disable of the given feature. User claims are not taken into account for this condition. -Below is the serialized representation of feature toggle with simple condition to enable or disable a given feature. +Below is the serialized representation of toggle with simple condition. ``` { - "dashboard_widget":{ -- Feature name - "toggle"{ -- Toggle details for the feature + "dashboard_widget":{ + "toggle"{ "conditions":[{ - "type":"Simple", - "isEnabled":true|false -- enabled or disable the feature. + "type":"Simple", -- Simple Condition. + "isEnabled":true|false -- Enabled or disable the feature. }] } } } ``` #### ii. Regex Condition -`Regex` condition allows evaluating a user claim against a regex expression. +`Regex` condition allows evaluating a regex expression against specified user claim to enable a given feature. -Below is the serialized representation of feature toggle with regex conditions to enable or disable a given feature. +Below is the serialized representation of toggle with regex condition. ``` { - "dashboard_widget":{ -- Feature name - "toggle"{ -- Toggle details for the feature - - "operator":"any|all", -- evaluate overall toggle to true - -- when `any` condition is met or `all` conditions are met. + "dashboard_widget":{ + "toggle"{ + "conditions":[{ - "type":"Regex", - "claim":"email", -- email claim to be used for evaluation. - "expression":"*@gbk.com" -- Regex expression for evaulation. - }, - { - "type":"Regex", - "claim":"user_role", -- user_role claim to be used for evaluation. - expression":"*@gbk.com" -- Regex expression for evaulation. - }] + "type":"Regex", -- Regex Condition + "claim":"email", -- Claim 'email' to be used for evaluation. + "expression":"*@gbk.com" -- Regex expression to be used for evaulation. + }] } } } ``` -### Step 3. -Implement `IStoreProvider` interface to return all configured feature toggles from custom store. -Return a collection of key-value pairs with key mapping to `featureName` and value mapping to string representation of json serialized `toggle` +### Step 3. Provide Storage Implementation. +Implement `IStorageProvider` interface to get configured feature toggles from storage. +The interface has `Get()` method that returns a collection of `KeyValuePair` with `key` mapping to `featureName` and `value` mapping to json string representation of the `toggle` ``` /// - /// Interface to implement feature store provider. + /// Interface to implement storage provider. /// - public interface IStoreProvider + public interface IStorageProvider { /// - /// Implement this method to return all features from store provider. + /// Implement this method to get all feature toggles from storage. /// /// /// Example: - /// Key - dashboard_widget - /// Value - Json string as - /// { - /// "operator":"all", - /// "conditions":[{ - /// "type":"Regex", - /// "claim":"email", - /// "expression":"*@gbk.com" - /// }, - /// { - /// "type":"RegexCondition", - /// "claim":"user_role", - /// "expression":"^administrator$" - /// }] - /// } + /// Key - "dashboard_widget" + /// Value - "{\"conditions\":[{\"type\":\"Simple\",\"isEnabled\": true}]}" /// /// KeyValuePair Array IEnumerable> Get(); } ``` -### Step 4. -Example - IoC Container Registrations +### Step 4. Bootstrap Initialialize +In startup code, initialize the Features class with dependencies as shown below. ``` - services.UseFeatureOne(new Configuration - { - // Optional logger implementation - Logger = new CustomLoggerImpl(), + var logger = new CustomLoggerImpl(); + var storageProvider = new SQlStoreProviderImpl(); + + Features.Initialize(() => new Features(new FeatureStore(storageProvider, logger), logger)); - // Custom store provider implementation. - StoreProvider = new SQlStoreProviderImpl() - } ``` How to Extend FeatureOne -- -### Toggle Condition -You could add your own conditions by extending the `ICondtion` interface. The interface provides `evaluate()` method that returns a boolean as a result of evaluating logic against list of input claims. +### i. Toggle Condition +You could implement your own condition by extending the `ICondition` interface. +The interface provides `evaluate()` method that returns a boolean result of evaluating logic against list of input claims. ``` /// /// Interface to implement toggle condition. @@ -142,13 +138,14 @@ You could add your own conditions by extending the `ICondtion` interface. The in /// /// Implement method to evaulate toggle condition. /// - /// List of user claims; could be null + /// List of user claims; could be empty /// bool Evaluate(IDictionary claims); } ``` -`Please Note` The condition class should only include primitive data type properties. -Example below +`Please Note` The condition class should only include primitive data type properties for default deserialization. +Example below of custom condition . + ``` public class TimeCondition : ICondition { @@ -160,30 +157,31 @@ Example below return (DateTime.Now.Hour > 12); } } - - -- Example toggle to allow non-admin users access to a feature only after 14 hrs. - - { - operator":"any", - "conditions":[{ - "type":"Regex", - "claim":"user_role", - "expression":"^administrator$" - }, - { - "type":"Time", - "Hour":14 - }] - } +``` + Example usage of above condition in toggle to allow non-admin users access to a feature only after 14 hrs. + ``` + { + "operator":"any", -- Any below condition evaluation to true should succeed the toggle. + "conditions":[{ + "type":"Time", -- Time condition to all access to all after 14hrs + "Hour":14 + }, + { + "type":"Regex", -- Regex to allow admin access + "claim":"user_role", + "expression":"^administrator$" + }] + } ``` -### Store Provider -To use FeatureOne, you need to provide implementation of `Store Provider` to get all the feature toggles from storage medium of choice. Implement `IStoreProvider` interface to return the key-value pairs with feature name and json string toggle. +### ii. Storage Provider +To use FeatureOne, you need to provide implementation of `Storage Provider` to get all the feature toggles from storage medium of choice. +Implement `IStorageProvider` interface to return the key-value pairs with feature name and json string toggle. Below is an example of dummy provider implementation. -> A production implementation should be a provider with `API` or `SQL` or `File system` backend. Ideally, you may also use caching of feature toggles in the provider implementation to optimise calls to storage medium. +> A production implementation should be a provider with `API` , `SQL` or `File system` storage. Ideally, you may also use `caching` of feature toggles in the production implementation to optimise calls to storage medium. ``` -public class CustomStoreProvider : IStoreProvider +public class CustomStoreProvider : IStorageProvider { public IEnumerable> Get() { @@ -195,8 +193,10 @@ public class CustomStoreProvider : IStoreProvider } ``` -### Logger -You could optionally provide an implementation of logger by wrapping your favourite logging libaray under `IFeatureLogger` interface. Please see the interface definition below. This implementation is optional and when no logger is provided FeatureOne will not log any errors. +### iii. Logger +You could optionally provide an implementation of a logger by wrapping your favourite logging libaray under `IFeatureLogger` interface. +Please see the interface definition below. +>This implementation is optional and when no logger is provided FeatureOne will not log any errors, warnings or information. ``` /// /// Interface to implement custom logger. @@ -227,4 +227,7 @@ You could optionally provide an implementation of logger by wrapping your favour /// log message void Warn(string message); } -``` \ No newline at end of file +``` +Credits +-- +Thank you for reading. Please fork, explore, contribute and report. Happy Coding !! :) \ No newline at end of file diff --git a/src/FeatureOne/Configuration.cs b/src/FeatureOne/Configuration.cs deleted file mode 100644 index 0b31a5d..0000000 --- a/src/FeatureOne/Configuration.cs +++ /dev/null @@ -1,10 +0,0 @@ -using FeatureOne.Core.Stores; - -namespace FeatureOne -{ - public class Configuration - { - public IFeatureLogger Logger { get; set; } = new NullLogger(); - public IStoreProvider StoreProvider { get; set; } - } -} \ No newline at end of file diff --git a/src/FeatureOne/Core/ICondition.cs b/src/FeatureOne/Core/ICondition.cs index 0197906..c776285 100644 --- a/src/FeatureOne/Core/ICondition.cs +++ b/src/FeatureOne/Core/ICondition.cs @@ -8,7 +8,7 @@ public interface ICondition /// /// Implement method to evaulate toggle condition. /// - /// List of user claims; could be null + /// List of user claims; could be empty /// bool Evaluate(IDictionary claims); } diff --git a/src/FeatureOne/Core/Stores/FeatureStore.cs b/src/FeatureOne/Core/Stores/FeatureStore.cs index d309b46..4588904 100644 --- a/src/FeatureOne/Core/Stores/FeatureStore.cs +++ b/src/FeatureOne/Core/Stores/FeatureStore.cs @@ -1,23 +1,25 @@ -namespace FeatureOne.Core.Stores +using System.Xml.Linq; + +namespace FeatureOne.Core.Stores { public class FeatureStore : IFeatureStore { - private IStoreProvider storeProvider; - private readonly Configuration Configuration; + private IStorageProvider storageProvider; + private IFeatureLogger logger; private IToggleDeserializer toggleDeserializer; - public FeatureStore(IStoreProvider storeProvider) : this(storeProvider, new ToggleDeserializer(), new Configuration()) + public FeatureStore(IStorageProvider storageProvider) : this(storageProvider, new NullLogger(), new ToggleDeserializer()) { } - public FeatureStore(IStoreProvider storeProvider, IToggleDeserializer toggleDeserializer) : this(storeProvider, toggleDeserializer, new Configuration()) + public FeatureStore(IStorageProvider storageProvider, IFeatureLogger logger) : this(storageProvider, logger, new ToggleDeserializer()) { } - public FeatureStore(IStoreProvider storeProvider, IToggleDeserializer toggleDeserializer, Configuration configuration) + public FeatureStore(IStorageProvider storageProvider, IFeatureLogger logger, IToggleDeserializer toggleDeserializer) { - this.storeProvider = storeProvider; - Configuration = configuration; + this.storageProvider = storageProvider; + this.logger = logger; this.toggleDeserializer = toggleDeserializer; } @@ -28,7 +30,7 @@ public IEnumerable FindStartsWith(string key) public IEnumerable GetAll() { - var features = storeProvider.Get(); + var features = storageProvider.Get(); if (features == null || !features.Any()) return Enumerable.Empty(); var result = new List(); @@ -38,10 +40,11 @@ public IEnumerable GetAll() try { result.Add(new Feature(feature.Key, toggleDeserializer.Deserializer(feature.Value))); + logger?.Info($"FeatureOne, Action='StorageProvider.Get', Feature='{feature.Key}', Message='Reterieved Success'"); } catch (Exception ex) { - Configuration.Logger?.Error($"Action='Failed to Deserialize', Feature='{feature.Key}', Exception='{ex}'."); + logger?.Error($"FeatureOne, Action='StorageProvider.Get', Feature='{feature.Key}', Toggle='{feature.Value}' Exception='{ex}'."); } } diff --git a/src/FeatureOne/Core/Stores/IStorageProvider.cs b/src/FeatureOne/Core/Stores/IStorageProvider.cs new file mode 100644 index 0000000..a955363 --- /dev/null +++ b/src/FeatureOne/Core/Stores/IStorageProvider.cs @@ -0,0 +1,19 @@ +namespace FeatureOne.Core.Stores +{ + /// + /// Interface to implement storage provider. + /// + public interface IStorageProvider + { + /// + /// Implement this method to get all feature toggless from storage. + /// + /// + /// Example: + /// Key : "Feature-01" + /// Value : "{\"conditions\":[{\"type\":\"Simple\",\"isEnabled\": true}]}" + /// + /// + IEnumerable> Get(); + } +} \ No newline at end of file diff --git a/src/FeatureOne/Core/Stores/IStoreProvider.cs b/src/FeatureOne/Core/Stores/IStoreProvider.cs deleted file mode 100644 index 3586807..0000000 --- a/src/FeatureOne/Core/Stores/IStoreProvider.cs +++ /dev/null @@ -1,32 +0,0 @@ -namespace FeatureOne.Core.Stores -{ - /// - /// Interface to implement feature store provider. - /// - public interface IStoreProvider - { - /// - /// Implement this method to return all features from store provider. - /// - /// - /// Example: - /// Key - Feature-01 - /// Value - Json string as - /// { - /// "operator":"all", - /// "conditions":[{ - /// "type":"Regex", - /// "claim":"email", - /// "expression":"*@gbk.com" - /// }, - /// { - /// "type":"RegexCondition", - /// "claim":"user_role", - /// "expression":"^administrator$" - /// }] - /// } - /// - /// - IEnumerable> Get(); - } -} \ No newline at end of file diff --git a/src/FeatureOne/Core/Stores/NullStoreProvder.cs b/src/FeatureOne/Core/Stores/NullStoreProvder.cs index a7de65a..7130306 100644 --- a/src/FeatureOne/Core/Stores/NullStoreProvder.cs +++ b/src/FeatureOne/Core/Stores/NullStoreProvder.cs @@ -1,6 +1,6 @@ namespace FeatureOne.Core.Stores { - public class NullStoreProvder : IStoreProvider + public class NullStoreProvder : IStorageProvider { public IEnumerable> Get() { diff --git a/src/FeatureOne/Core/Toggle.cs b/src/FeatureOne/Core/Toggle.cs index 4c1a59b..360c395 100644 --- a/src/FeatureOne/Core/Toggle.cs +++ b/src/FeatureOne/Core/Toggle.cs @@ -16,6 +16,8 @@ public bool Run(IDictionary claims) if (Conditions == null) return false; + claims ??= new Dictionary(); + return Operator == Operator.Any ? Conditions.Any(x => x.Evaluate(claims)) : Conditions.All(x => x.Evaluate(claims)); diff --git a/src/FeatureOne/FeatureOne.csproj b/src/FeatureOne/FeatureOne.csproj index 88f8326..f6599eb 100644 --- a/src/FeatureOne/FeatureOne.csproj +++ b/src/FeatureOne/FeatureOne.csproj @@ -42,8 +42,4 @@ - - - - - + diff --git a/src/FeatureOne/Features.cs b/src/FeatureOne/Features.cs index 320c17d..817cfff 100644 --- a/src/FeatureOne/Features.cs +++ b/src/FeatureOne/Features.cs @@ -8,22 +8,19 @@ namespace FeatureOne public class Features { private readonly IFeatureStore featureStore; - private readonly Configuration Configuration; + private readonly IFeatureLogger logger; public static Features Current { get; private set; } - public Features(IFeatureStore featureStore) : this(featureStore, new Configuration - { - Logger = new NullLogger() - }) + public Features(IFeatureStore featureStore) : this(featureStore, new NullLogger()) { } - public Features(IFeatureStore featureStore, Configuration configuration) + public Features(IFeatureStore featureStore, IFeatureLogger logger) { this.featureStore = featureStore; - this.Configuration = configuration; + this.logger = logger; } - public static void Initialise(Func factory) => Current = factory(); + public static void Initialize(Func factory) => Current = factory(); /// /// Determines whether the feature is enabled for given claims principal. @@ -58,24 +55,35 @@ public bool IsEnabled(string name, IDictionary claims) try { if (claims == null) - return false; + { + logger?.Warn($"FeatureOne, Action='Features.IsEnabled', Feature= {name}, Message='Empty claims'"); + } var featureName = new FeatureName(name); var features = featureStore.FindStartsWith(featureName.Value).ToList(); if (!features.Any()) + { + logger?.Warn($"FeatureOne, Action='Features.IsEnabled', Feature= {name}, Message='No features in store'"); return false; + } var feature = features.FirstOrDefault(x => x.Name.Value.Equals(featureName.Value, StringComparison.OrdinalIgnoreCase)); if (feature == null) + { + logger?.Warn($"FeatureOne, Action='Features.IsEnabled', Feature= {name}, Message='Featrue not found'"); return false; + } + + var result = feature.IsEnabled(claims); + logger?.Info($"FeatureOne, Action='Features.IsEnabled', Feature= {name}, result={result}"); - return feature.IsEnabled(claims); + return result; } catch (Exception ex) { - Configuration.Logger?.Error($"Action='Features.IsEnabled', Feature='{name}', Exception='{ex}'."); + logger?.Error($"FeatureOne, Action='Features.IsEnabled', Feature='{name}', Exception='{ex}'."); return false; } } diff --git a/src/FeatureOne/ServiceRegistrations.cs b/src/FeatureOne/ServiceRegistrations.cs deleted file mode 100644 index 9b256ac..0000000 --- a/src/FeatureOne/ServiceRegistrations.cs +++ /dev/null @@ -1,34 +0,0 @@ -using FeatureOne.Core.Stores; -using Microsoft.Extensions.DependencyInjection; - -namespace FeatureOne -{ - public static class ServiceRegistrations - { - public static void UseFeatureOne(this ServiceCollection services, Configuration configuration) - { - if (configuration?.Logger == null) - services.AddTransient(); - - if (configuration?.StoreProvider == null) - services.AddTransient(); - - services.AddTransient(); - services.AddTransient(c => configuration?.StoreProvider ?? c.GetService()); - - services.AddTransient(c => - new FeatureStore(c.GetService(), c.GetService(), new Configuration - { - Logger = configuration?.Logger ?? c.GetService(), - StoreProvider = configuration?.StoreProvider ?? c.GetService() - })); - - services.AddTransient(c => - new Features(c.GetService(), new Configuration - { - Logger = configuration?.Logger ?? c.GetService(), - StoreProvider = configuration?.StoreProvider ?? c.GetService() - })); - } - } -} \ No newline at end of file diff --git a/test/FeatureOne.Tests/E2E Tests/ConsoleLogger.cs b/test/FeatureOne.Tests/E2E Tests/ConsoleLogger.cs new file mode 100644 index 0000000..df789ba --- /dev/null +++ b/test/FeatureOne.Tests/E2E Tests/ConsoleLogger.cs @@ -0,0 +1,25 @@ +namespace FeatureOne.Tests.Registeration +{ + public class ConsoleLogger : IFeatureLogger + { + public void Debug(string message) + { + Console.WriteLine(message); + } + + public void Error(string message) + { + Console.WriteLine(message); + } + + public void Info(string message) + { + Console.WriteLine(message); + } + + public void Warn(string message) + { + Console.WriteLine(message); + } + } +} diff --git a/test/FeatureOne.Tests/E2E Tests/E2ETests.cs b/test/FeatureOne.Tests/E2E Tests/E2ETests.cs new file mode 100644 index 0000000..0ed8370 --- /dev/null +++ b/test/FeatureOne.Tests/E2E Tests/E2ETests.cs @@ -0,0 +1,29 @@ +using FeatureOne.Core.Stores; +using System.Security.Claims; + +namespace FeatureOne.Tests.Registeration +{ + [TestFixture] + internal class E2ETests + { + + [Test] + public void TestE2EOfServices() + { + var logger = new ConsoleLogger(); + var storageProvider = new StorageProvider(); + + Features.Initialize(() => new Features(new FeatureStore(storageProvider, logger), logger)); + + + var principal = new ClaimsPrincipal(new ClaimsIdentity(new List + { + new Claim("user", "ninja") + })); + + var isEnabled = Features.Current.IsEnabled("feature-01", principal); + + Assert.That(isEnabled, Is.True); + } + } +} diff --git a/test/FeatureOne.Tests/E2E Tests/StorageProvider.cs b/test/FeatureOne.Tests/E2E Tests/StorageProvider.cs new file mode 100644 index 0000000..ca38d29 --- /dev/null +++ b/test/FeatureOne.Tests/E2E Tests/StorageProvider.cs @@ -0,0 +1,16 @@ +using FeatureOne.Core.Stores; + +namespace FeatureOne.Tests.Registeration +{ + public class StorageProvider : IStorageProvider + { + public IEnumerable> Get() + { + return new[] + { + new KeyValuePair("feature-01", "{\"conditions\":[{\"type\":\"Simple\",\"isEnabled\": true}]}"), + new KeyValuePair("feature-02", "{\"operator\":\"all\",\"conditions\":[{\"type\":\"Simple\",\"isEnabled\": false}, {\"type\":\"RegexCondition\",\"claim\":\"email\",\"expression\":\"*@gbk.com\"}]}") + }; + } + } +} diff --git a/test/FeatureOne.Tests/FeatureOne.Tests.csproj b/test/FeatureOne.Tests/FeatureOne.Tests.csproj index bfb3c1a..7a7aa3e 100644 --- a/test/FeatureOne.Tests/FeatureOne.Tests.csproj +++ b/test/FeatureOne.Tests/FeatureOne.Tests.csproj @@ -21,10 +21,6 @@ - - - - diff --git a/test/FeatureOne.Tests/FeaturesTests.cs b/test/FeatureOne.Tests/FeaturesTests.cs index 023032a..5b07786 100644 --- a/test/FeatureOne.Tests/FeaturesTests.cs +++ b/test/FeatureOne.Tests/FeaturesTests.cs @@ -25,10 +25,7 @@ public void Setup() store.Setup(x => x.FindStartsWith(It.IsAny())).Returns(new[] { feature.Object }); - features = new Features(store.Object, new Configuration - { - Logger = logger.Object - }); + features = new Features(store.Object, logger.Object); principal = new ClaimsPrincipal(new ClaimsIdentity(new List { diff --git a/test/FeatureOne.Tests/Registeration/RegistrationTests.cs b/test/FeatureOne.Tests/Registeration/RegistrationTests.cs deleted file mode 100644 index ef28d55..0000000 --- a/test/FeatureOne.Tests/Registeration/RegistrationTests.cs +++ /dev/null @@ -1,59 +0,0 @@ -using Castle.DynamicProxy.Generators; -using FeatureOne.Core.Stores; -using Microsoft.Extensions.DependencyInjection; -using Moq; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Claims; -using System.Text; -using System.Threading.Tasks; - -namespace FeatureOne.Tests.Registeration -{ - [TestFixture] - internal class RegistrationTests - { - private Mock storeProvider; - private Mock logger; - private ServiceProvider serviceProvider; - - [SetUp] - public void Setup() { - - logger = new Mock(); - storeProvider = new Mock(); - storeProvider.Setup(x => x.Get()) - .Returns(new[] - { - new KeyValuePair("feature-01", "{\"conditions\":[{\"type\":\"Simple\",\"isEnabled\": true}]}"), - new KeyValuePair("feature-02", "{\"operator\":\"all\",\"conditions\":[{\"type\":\"Simple\",\"isEnabled\": false}, {\"type\":\"RegexCondition\",\"claim\":\"email\",\"expression\":\"*@gbk.com\"}]}") - }); - - var services = new ServiceCollection(); - services.UseFeatureOne(new Configuration - { - StoreProvider = storeProvider.Object, - Logger= logger.Object - }); - - serviceProvider = services.BuildServiceProvider(); - - - } - - [Test] - public void TestRegistration() - { - var features = serviceProvider.GetService(); - var principal = new ClaimsPrincipal(new ClaimsIdentity(new List - { - new Claim("user", "ninja") - })); - - var isEnabled = features.IsEnabled("feature-01", principal); - - Assert.That(isEnabled, Is.True); - } - } -} diff --git a/test/FeatureOne.Tests/Stores/FeatureStoreTests.cs b/test/FeatureOne.Tests/Stores/FeatureStoreTests.cs index daf2cba..4b30ffa 100644 --- a/test/FeatureOne.Tests/Stores/FeatureStoreTests.cs +++ b/test/FeatureOne.Tests/Stores/FeatureStoreTests.cs @@ -9,7 +9,7 @@ namespace FeatureOne.Tests.Stores [TestFixture] internal class FeatureStoreTests { - private Mock storeProvider; + private Mock storeProvider; private FeatureStore featureStore; private Mock logger; @@ -17,7 +17,7 @@ internal class FeatureStoreTests public void Setup() { logger = new Mock(); - storeProvider = new Mock(); + storeProvider = new Mock(); storeProvider.Setup(x => x.Get()) .Returns(new[] { @@ -25,10 +25,7 @@ public void Setup() new KeyValuePair("feature-02", "{\"operator\":\"all\",\"conditions\":[{\"type\":\"Simple\",\"isEnabled\": false}, {\"type\":\"RegexCondition\",\"claim\":\"email\",\"expression\":\"*@gbk.com\"}]}") }); - featureStore = new FeatureStore(storeProvider.Object, new ToggleDeserializer(), new Configuration - { - Logger = logger.Object - }); + featureStore = new FeatureStore(storeProvider.Object, logger.Object); } [Test] @@ -93,7 +90,7 @@ public void TestGetAllToReturnAnyDeserializedFeaturesInStoreProvideAndLogErrorsF } } - public class CustomStoreProvider : IStoreProvider + public class CustomStoreProvider : IStorageProvider { public IEnumerable> Get() {