test/test_config_resolver.rb in prefab-cloud-ruby-0.20.0 vs test/test_config_resolver.rb in prefab-cloud-ruby-0.21.0
- old
+ new
@@ -1,204 +1,381 @@
# frozen_string_literal: true
+
require 'test_helper'
class TestConfigResolver < Minitest::Test
-
STAGING_ENV_ID = 1
PRODUCTION_ENV_ID = 2
TEST_ENV_ID = 3
+ SEGMENT_KEY = 'segment_key'
+ CONFIG_KEY = 'config_key'
+ DEFAULT_VALUE = 'default_value'
+ IN_SEGMENT_VALUE = 'in_segment_value'
+ WRONG_ENV_VALUE = 'wrong_env_value'
+ NOT_IN_SEGMENT_VALUE = 'not_in_segment_value'
def test_resolution
@loader = MockConfigLoader.new
loaded_values = {
- "key" => { config: Prefab::Config.new(
- key: "key",
+ 'key' => { config: Prefab::Config.new(
+ key: 'key',
rows: [
Prefab::ConfigRow.new(
- value: Prefab::ConfigValue.new(string: "value_no_env_default"),
+ values: [
+ Prefab::ConditionalValue.new(
+ value: Prefab::ConfigValue.new(string: 'value_no_env_default')
+ )
+ ]
),
Prefab::ConfigRow.new(
project_env_id: TEST_ENV_ID,
- value: Prefab::ConfigValue.new(string: "value_none"),
- ),
- Prefab::ConfigRow.new(
- project_env_id: TEST_ENV_ID,
- namespace: "projectA",
- value: Prefab::ConfigValue.new(string: "valueA"),
- ),
- Prefab::ConfigRow.new(
- project_env_id: TEST_ENV_ID,
- namespace: "projectB",
- value: Prefab::ConfigValue.new(string: "valueB"),
- ),
- Prefab::ConfigRow.new(
- project_env_id: TEST_ENV_ID,
- namespace: "projectB.subprojectX",
- value: Prefab::ConfigValue.new(string: "projectB.subprojectX"),
- ),
- Prefab::ConfigRow.new(
- project_env_id: TEST_ENV_ID,
- namespace: "projectB.subprojectY",
- value: Prefab::ConfigValue.new(string: "projectB.subprojectY"),
- ),
+ values: [
+ Prefab::ConditionalValue.new(
+ criteria: [
+ Prefab::Criterion.new(
+ operator: Prefab::Criterion::CriterionOperator::HIERARCHICAL_MATCH,
+ value_to_match: Prefab::ConfigValue.new(string: 'projectB.subprojectX'),
+ property_name: Prefab::CriteriaEvaluator::NAMESPACE_KEY
+ )
+ ],
+ value: Prefab::ConfigValue.new(string: 'projectB.subprojectX')
+ ),
+ Prefab::ConditionalValue.new(
+ criteria: [
+ Prefab::Criterion.new(
+ operator: Prefab::Criterion::CriterionOperator::HIERARCHICAL_MATCH,
+ value_to_match: Prefab::ConfigValue.new(string: 'projectB.subprojectY'),
+ property_name: Prefab::CriteriaEvaluator::NAMESPACE_KEY
+ )
+ ],
+ value: Prefab::ConfigValue.new(string: 'projectB.subprojectY')
+ ),
+ Prefab::ConditionalValue.new(
+ criteria: [
+ Prefab::Criterion.new(
+ operator: Prefab::Criterion::CriterionOperator::HIERARCHICAL_MATCH,
+ value_to_match: Prefab::ConfigValue.new(string: 'projectA'),
+ property_name: Prefab::CriteriaEvaluator::NAMESPACE_KEY
+ )
+ ],
+ value: Prefab::ConfigValue.new(string: 'valueA')
+ ),
+ Prefab::ConditionalValue.new(
+ criteria: [
+ Prefab::Criterion.new(
+ operator: Prefab::Criterion::CriterionOperator::HIERARCHICAL_MATCH,
+ value_to_match: Prefab::ConfigValue.new(string: 'projectB'),
+ property_name: Prefab::CriteriaEvaluator::NAMESPACE_KEY
+ )
+ ],
+ value: Prefab::ConfigValue.new(string: 'valueB')
+ ),
+ Prefab::ConditionalValue.new(
+ criteria: [
+ Prefab::Criterion.new(
+ operator: Prefab::Criterion::CriterionOperator::HIERARCHICAL_MATCH,
+ value_to_match: Prefab::ConfigValue.new(string: 'projectB.subprojectX'),
+ property_name: Prefab::CriteriaEvaluator::NAMESPACE_KEY
+ )
+ ],
+ value: Prefab::ConfigValue.new(string: 'projectB.subprojectX')
+ ),
+ Prefab::ConditionalValue.new(
+ criteria: [
+ Prefab::Criterion.new(
+ operator: Prefab::Criterion::CriterionOperator::HIERARCHICAL_MATCH,
+ value_to_match: Prefab::ConfigValue.new(string: 'projectB.subprojectY'),
+ property_name: Prefab::CriteriaEvaluator::NAMESPACE_KEY
+ )
+ ],
+ value: Prefab::ConfigValue.new(string: 'projectB.subprojectY')
+ ),
+ Prefab::ConditionalValue.new(
+ value: Prefab::ConfigValue.new(string: 'value_none')
+ )
+ ]
+ )
]
) },
- "key2" => { config: Prefab::Config.new(
- key: "key2",
+ 'key2' => { config: Prefab::Config.new(
+ key: 'key2',
rows: [
- value: Prefab::ConfigValue.new(string: "valueB2"),
+ Prefab::ConfigRow.new(
+ values: [
+ Prefab::ConditionalValue.new(
+ value: Prefab::ConfigValue.new(string: 'valueB2')
+ )
+ ]
+ )
]
) }
-
}
@loader.stub :calc_config, loaded_values do
+ @resolverA = resolver_for_namespace('', @loader, project_env_id: PRODUCTION_ENV_ID)
+ assert_equal 'value_no_env_default', @resolverA.get('key', nil).string
- @resolverA = resolver_for_namespace("", @loader, project_env_id: PRODUCTION_ENV_ID)
- assert_equal "value_no_env_default", @resolverA.get("key")
-
## below here in the test env
- @resolverA = resolver_for_namespace("", @loader)
- assert_equal "value_none", @resolverA.get("key")
+ @resolverA = resolver_for_namespace('', @loader)
+ assert_equal 'value_none', @resolverA.get('key', nil).string
- @resolverA = resolver_for_namespace("projectA", @loader)
- assert_equal "valueA", @resolverA.get("key")
+ @resolverA = resolver_for_namespace('projectA', @loader)
+ assert_equal 'valueA', @resolverA.get('key', nil).string
- @resolverB = resolver_for_namespace("projectB", @loader)
- assert_equal "valueB", @resolverB.get("key")
+ @resolverB = resolver_for_namespace('projectB', @loader)
+ assert_equal 'valueB', @resolverB.get('key', nil).string
- @resolverBX = resolver_for_namespace("projectB.subprojectX", @loader)
- assert_equal "projectB.subprojectX", @resolverBX.get("key")
+ @resolverBX = resolver_for_namespace('projectB.subprojectX', @loader)
+ assert_equal 'projectB.subprojectX', @resolverBX.get('key', nil).string
- @resolverBX = resolver_for_namespace("projectB.subprojectX", @loader)
- assert_equal "valueB2", @resolverBX.get("key2")
+ @resolverBX = resolver_for_namespace('projectB.subprojectX', @loader)
+ assert_equal 'valueB2', @resolverBX.get('key2', nil).string
- @resolverUndefinedSubProject = resolver_for_namespace("projectB.subprojectX.subsubQ", @loader)
- assert_equal "projectB.subprojectX", @resolverBX.get("key")
+ @resolverUndefinedSubProject = resolver_for_namespace('projectB.subprojectX.subsubQ', @loader)
+ assert_equal 'projectB.subprojectX', @resolverUndefinedSubProject.get('key', nil).string
- @resolverBX = resolver_for_namespace("projectC", @loader)
- assert_equal "value_none", @resolverBX.get("key")
- assert_nil @resolverBX.get("key_that_doesnt_exist")
+ @resolverBX = resolver_for_namespace('projectC', @loader)
+ assert_equal 'value_none', @resolverBX.get('key', nil).string
+
+ assert_nil @resolverBX.get('key_that_doesnt_exist', nil)
end
end
- def test_starts_with_ns
- @loader = MockConfigLoader.new
- @loader.stub :calc_config, {} do
- resolver = Prefab::ConfigResolver.new(MockBaseClient.new, @loader)
- assert_equal [true, 0], resolver.send(:starts_with_ns?, "", "a")
- assert_equal [true, 1], resolver.send(:starts_with_ns?, "a", "a")
- assert_equal [true, 1], resolver.send(:starts_with_ns?, "a", "a.b")
- assert_equal [false, 2], resolver.send(:starts_with_ns?, "a.b", "a")
+ def test_resolving_in_segment
+ segment_config = Prefab::Config.new(
+ config_type: Prefab::ConfigType::SEGMENT,
+ key: SEGMENT_KEY,
+ rows: [
+ Prefab::ConfigRow.new(
+ values: [
+ Prefab::ConditionalValue.new(
+ value: Prefab::ConfigValue.new(bool: true),
+ criteria: [
+ Prefab::Criterion.new(
+ operator: Prefab::Criterion::CriterionOperator::PROP_ENDS_WITH_ONE_OF,
+ value_to_match: string_list(['hotmail.com', 'gmail.com']),
+ property_name: 'email'
+ )
+ ]
+ ),
+ Prefab::ConditionalValue.new(value: Prefab::ConfigValue.new(bool: false))
+ ]
+ )
+ ]
+ )
- assert_equal [true, 1], resolver.send(:starts_with_ns?, "corp", "corp.proj.proja")
- assert_equal [true, 2], resolver.send(:starts_with_ns?, "corp.proj", "corp.proj.proja")
- assert_equal [true, 3], resolver.send(:starts_with_ns?, "corp.proj.proja", "corp.proj.proja")
- assert_equal [false, 3], resolver.send(:starts_with_ns?, "corp.proj.projb", "corp.proj.proja")
+ config = Prefab::Config.new(
+ key: CONFIG_KEY,
+ rows: [
+ # wrong env
+ Prefab::ConfigRow.new(
+ project_env_id: TEST_ENV_ID,
+ values: [
+ Prefab::ConditionalValue.new(
+ criteria: [
+ Prefab::Criterion.new(
+ operator: Prefab::Criterion::CriterionOperator::IN_SEG,
+ value_to_match: Prefab::ConfigValue.new(string: SEGMENT_KEY)
+ )
+ ],
+ value: Prefab::ConfigValue.new(string: WRONG_ENV_VALUE)
+ ),
+ Prefab::ConditionalValue.new(
+ criteria: [],
+ value: Prefab::ConfigValue.new(string: DEFAULT_VALUE)
+ )
+ ]
+ ),
- # corp_equal [true, 1:,a:b is not a real delimited namespace[0
- assert_equal [false, 1], resolver.send(:starts_with_ns?, "corp", "corp:a:b")
- assert_equal [true, 1], resolver.send(:starts_with_ns?, "foo", "foo.baz")
- assert_equal [true, 2], resolver.send(:starts_with_ns?, "foo.baz", "foo.baz")
- assert_equal [false, 2], resolver.send(:starts_with_ns?, "foo.baz", "foo.bazz")
- end
- end
+ # correct env
+ Prefab::ConfigRow.new(
+ project_env_id: PRODUCTION_ENV_ID,
+ values: [
+ Prefab::ConditionalValue.new(
+ criteria: [
+ Prefab::Criterion.new(
+ operator: Prefab::Criterion::CriterionOperator::IN_SEG,
+ value_to_match: Prefab::ConfigValue.new(string: SEGMENT_KEY)
+ )
+ ],
+ value: Prefab::ConfigValue.new(string: IN_SEGMENT_VALUE)
+ ),
+ Prefab::ConditionalValue.new(
+ criteria: [],
+ value: Prefab::ConfigValue.new(string: DEFAULT_VALUE)
+ )
+ ]
+ )
+ ]
+ )
- def test_special_ff_variant_copying
-
- @loader = MockConfigLoader.new
loaded_values = {
- "ff" => { source: 'test',
- config: Prefab::Config.new(
- key: "ff",
- variants: [
- Prefab::FeatureFlagVariant.new(string: "inactive"),
- Prefab::FeatureFlagVariant.new(string: "default"),
- Prefab::FeatureFlagVariant.new(string: "env"),
- ],
- rows: [
- { value: Prefab::ConfigValue.new(feature_flag: Prefab::FeatureFlag.new(
- inactive_variant_idx: 0,
- rules: default_ff_rule(1),
- )) },
- { project_env_id: TEST_ENV_ID,
- value: Prefab::ConfigValue.new(feature_flag: Prefab::FeatureFlag.new(
- inactive_variant_idx: 0,
- rules: default_ff_rule(2),
- )) }
- ]
- )
- }
+ SEGMENT_KEY => { config: segment_config },
+ CONFIG_KEY => { config: config }
}
- @loader.stub :calc_config, loaded_values do
- resolver = Prefab::ConfigResolver.new(MockBaseClient.new, @loader)
- config = resolver.get_config("ff")
- assert_equal 3, config.variants.size
- assert_equal %w(inactive default env), config.variants.map(&:string)
+
+ loader = MockConfigLoader.new
+
+ loader.stub :calc_config, loaded_values do
+ options = Prefab::Options.new
+ resolver = Prefab::ConfigResolver.new(MockBaseClient.new(options), loader)
+ resolver.project_env_id = PRODUCTION_ENV_ID
+
+ assert_equal DEFAULT_VALUE, resolver.get(CONFIG_KEY, nil, { email: 'test@something-else.com' }).string
+ assert_equal IN_SEGMENT_VALUE, resolver.get(CONFIG_KEY, nil, { email: 'test@hotmail.com' }).string
end
end
- # colons are not allowed in keys, but verify behavior anyway
- def test_key_and_namespaces_with_colons
- @loader = MockConfigLoader.new
+ def test_resolving_not_in_segment
+ segment_config = Prefab::Config.new(
+ config_type: Prefab::ConfigType::SEGMENT,
+ key: SEGMENT_KEY,
+ rows: [
+ Prefab::ConfigRow.new(
+ values: [
+ Prefab::ConditionalValue.new(
+ value: Prefab::ConfigValue.new(bool: true),
+ criteria: [
+ Prefab::Criterion.new(
+ operator: Prefab::Criterion::CriterionOperator::PROP_ENDS_WITH_ONE_OF,
+ value_to_match: string_list(['hotmail.com', 'gmail.com']),
+ property_name: 'email'
+ )
+ ]
+ ),
+ Prefab::ConditionalValue.new(value: Prefab::ConfigValue.new(bool: false))
+ ]
+ )
+ ]
+ )
+ config = Prefab::Config.new(
+ key: CONFIG_KEY,
+ rows: [
+ Prefab::ConfigRow.new(
+ values: [
+ Prefab::ConditionalValue.new(
+ criteria: [
+ Prefab::Criterion.new(
+ operator: Prefab::Criterion::CriterionOperator::IN_SEG,
+ value_to_match: Prefab::ConfigValue.new(string: SEGMENT_KEY)
+ )
+ ],
+ value: Prefab::ConfigValue.new(string: IN_SEGMENT_VALUE)
+ ),
+ Prefab::ConditionalValue.new(
+ criteria: [
+ Prefab::Criterion.new(
+ operator: Prefab::Criterion::CriterionOperator::NOT_IN_SEG,
+ value_to_match: Prefab::ConfigValue.new(string: SEGMENT_KEY)
+ )
+ ],
+ value: Prefab::ConfigValue.new(string: NOT_IN_SEGMENT_VALUE)
+ )
+ ]
+ )
+ ]
+ )
+
loaded_values = {
- "Key:With:Colons" => { config: Prefab::Config.new(
- key: "Key:With:Colons",
- rows: [Prefab::ConfigRow.new(
- value: Prefab::ConfigValue.new(string: "value")
- )]
- ) },
- "proj:apikey" => { config: Prefab::Config.new(
- key: "proj:apikey",
- rows: [Prefab::ConfigRow.new(
- value: Prefab::ConfigValue.new(string: "v2")
- )]
- ) }
+ SEGMENT_KEY => { config: segment_config },
+ CONFIG_KEY => { config: config }
}
- @loader.stub :calc_config, loaded_values do
+ loader = MockConfigLoader.new
- r = resolver_for_namespace("foo", @loader)
- assert_nil r.get("apikey")
+ loader.stub :calc_config, loaded_values do
+ options = Prefab::Options.new
+ resolver = Prefab::ConfigResolver.new(MockBaseClient.new(options), loader)
- r = resolver_for_namespace("proj", @loader)
- assert_nil r.get("apikey")
+ assert_equal IN_SEGMENT_VALUE, resolver.get(CONFIG_KEY, nil, { email: 'test@hotmail.com' }).string
+ assert_equal NOT_IN_SEGMENT_VALUE, resolver.get(CONFIG_KEY, nil, { email: 'test@something-else.com' }).string
+ end
+ end
- r = resolver_for_namespace("", @loader)
- assert_nil r.get("apikey")
+ def test_resolving_in_segment_with_lookup_key
+ segment_config = Prefab::Config.new(
+ config_type: Prefab::ConfigType::SEGMENT,
+ key: SEGMENT_KEY,
+ rows: [
+ Prefab::ConfigRow.new(
+ values: [
+ Prefab::ConditionalValue.new(
+ value: Prefab::ConfigValue.new(bool: true),
+ criteria: [
+ Prefab::Criterion.new(
+ operator: Prefab::Criterion::CriterionOperator::LOOKUP_KEY_IN,
+ value_to_match: string_list(['user:1234', 'user:4567']),
+ property_name: 'LOOKUP'
+ )
+ ]
+ ),
+ Prefab::ConditionalValue.new(value: Prefab::ConfigValue.new(bool: false))
+ ]
+ )
+ ]
+ )
- @resolverKeyWith = resolver_for_namespace("Ket:With", @loader)
- assert_nil @resolverKeyWith.get("Colons")
- assert_nil @resolverKeyWith.get("With:Colons")
- assert_equal "value", @resolverKeyWith.get("Key:With:Colons")
+ config = Prefab::Config.new(
+ key: CONFIG_KEY,
+ rows: [
+ Prefab::ConfigRow.new(
+ values: [
+ Prefab::ConditionalValue.new(
+ criteria: [
+ Prefab::Criterion.new(
+ operator: Prefab::Criterion::CriterionOperator::IN_SEG,
+ value_to_match: Prefab::ConfigValue.new(string: SEGMENT_KEY)
+ )
+ ],
+ value: Prefab::ConfigValue.new(string: IN_SEGMENT_VALUE)
+ ),
+ Prefab::ConditionalValue.new(
+ criteria: [
+ Prefab::Criterion.new(
+ operator: Prefab::Criterion::CriterionOperator::NOT_IN_SEG,
+ value_to_match: Prefab::ConfigValue.new(string: SEGMENT_KEY)
+ )
+ ],
+ value: Prefab::ConfigValue.new(string: NOT_IN_SEGMENT_VALUE)
+ )
+ ]
+ )
+ ]
+ )
- @resolverKeyWithExtra = resolver_for_namespace("Key:With:Extra", @loader)
- assert_nil @resolverKeyWithExtra.get("Colons")
- assert_nil @resolverKeyWithExtra.get("With:Colons")
- assert_equal "value", @resolverKeyWithExtra.get("Key:With:Colons")
+ loaded_values = {
+ SEGMENT_KEY => { config: segment_config },
+ CONFIG_KEY => { config: config }
+ }
- @resolverKey = resolver_for_namespace("Key", @loader)
- assert_nil @resolverKey.get("With:Colons")
- assert_nil @resolverKey.get("Colons")
- assert_equal "value", @resolverKey.get("Key:With:Colons")
+ loader = MockConfigLoader.new
- @resolverWithProperlySegmentedNamespace = resolver_for_namespace("Key.With.Extra", @loader)
- assert_nil @resolverWithProperlySegmentedNamespace.get("Colons")
- assert_nil @resolverWithProperlySegmentedNamespace.get("With:Colons")
- assert_equal "value", @resolverWithProperlySegmentedNamespace.get("Key:With:Colons")
+ loader.stub :calc_config, loaded_values do
+ options = Prefab::Options.new
+ resolver = Prefab::ConfigResolver.new(MockBaseClient.new(options), loader)
+
+ assert_equal IN_SEGMENT_VALUE, resolver.get(CONFIG_KEY, 'user:1234', {}).string
+ assert_equal IN_SEGMENT_VALUE, resolver.get(CONFIG_KEY, 'user:4567', {}).string
+ assert_equal NOT_IN_SEGMENT_VALUE, resolver.get(CONFIG_KEY, nil, {}).string
+ assert_equal NOT_IN_SEGMENT_VALUE, resolver.get(CONFIG_KEY, 'user:9999', {}).string
end
end
+ private
+
def resolver_for_namespace(namespace, loader, project_env_id: TEST_ENV_ID)
options = Prefab::Options.new(
namespace: namespace
)
resolver = Prefab::ConfigResolver.new(MockBaseClient.new(options), loader)
resolver.project_env_id = project_env_id
resolver.update
resolver
end
+ def string_list(values)
+ Prefab::ConfigValue.new(string_list: Prefab::StringList.new(values: values))
+ end
end