From 5e5596aee5ccc27101ea9e8cfe4b6ae1f1960008 Mon Sep 17 00:00:00 2001 From: "christian.lutnik" Date: Tue, 21 Jan 2025 15:30:31 +0100 Subject: [PATCH 01/10] feat: Update test harness (copy test files) #1467 Signed-off-by: christian.lutnik --- .gitmodules | 3 --- pyproject.toml | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.gitmodules b/.gitmodules index 643f35ec..85115b56 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "test-harness"] - path = test-harness - url = https://github.com/open-feature/test-harness.git [submodule "spec"] path = spec url = https://github.com/open-feature/spec.git diff --git a/pyproject.toml b/pyproject.toml index 394947a7..39e45fe3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,7 +49,7 @@ cov = [ ] e2e = [ "git submodule update --init", - "cp test-harness/features/evaluation.feature tests/features/", + "cp spec/specification/assets/gherkin/*.feature tests/features/", "behave tests/features/", "rm tests/features/*.feature", ] From b1b3b51959b365e38a370ff2bfcd9feb7061b5a0 Mon Sep 17 00:00:00 2001 From: "christian.lutnik" Date: Tue, 21 Jan 2025 16:41:56 +0100 Subject: [PATCH 02/10] fixup! feat: Update test harness (copy test files) #1467 Signed-off-by: christian.lutnik --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 39e45fe3..fb96634f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,6 +48,7 @@ cov = [ "cov-report", ] e2e = [ + "git submodule init", "git submodule update --init", "cp spec/specification/assets/gherkin/*.feature tests/features/", "behave tests/features/", From b385f372042ebc6ef975b4b2638ca91cb059154c Mon Sep 17 00:00:00 2001 From: "christian.lutnik" Date: Wed, 22 Jan 2025 11:01:07 +0100 Subject: [PATCH 03/10] fixup! feat: Update test harness (copy test files) #1467 Signed-off-by: christian.lutnik --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index fb96634f..362d3a57 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,6 +48,7 @@ cov = [ "cov-report", ] e2e = [ + "git submodule sync", "git submodule init", "git submodule update --init", "cp spec/specification/assets/gherkin/*.feature tests/features/", From 985230cd79e416c4fa2da7e1bb1c3131c616aab1 Mon Sep 17 00:00:00 2001 From: "christian.lutnik" Date: Wed, 22 Jan 2025 15:06:44 +0100 Subject: [PATCH 04/10] fixup! feat: Update test harness (copy test files) #1467 Signed-off-by: christian.lutnik --- test-harness | 1 - 1 file changed, 1 deletion(-) delete mode 160000 test-harness diff --git a/test-harness b/test-harness deleted file mode 160000 index bd13458f..00000000 --- a/test-harness +++ /dev/null @@ -1 +0,0 @@ -Subproject commit bd13458f7e3587ab2ed98b8017bea3c2eb472cc9 From 8161c10688528c1610bec7074f029c0601a0b01d Mon Sep 17 00:00:00 2001 From: "christian.lutnik" Date: Thu, 23 Jan 2025 10:16:10 +0100 Subject: [PATCH 05/10] fixup! feat: Update test harness (copy test files) #1467 Signed-off-by: christian.lutnik --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 362d3a57..74f92096 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,7 +51,7 @@ e2e = [ "git submodule sync", "git submodule init", "git submodule update --init", - "cp spec/specification/assets/gherkin/*.feature tests/features/", + "cp -r spec/specification/assets/gherkin/. tests/features/", "behave tests/features/", "rm tests/features/*.feature", ] From c33dd52aac06f20665dad7abe955b1ae8dc8c69a Mon Sep 17 00:00:00 2001 From: "christian.lutnik" Date: Thu, 23 Jan 2025 10:55:32 +0100 Subject: [PATCH 06/10] fixup! feat: Update test harness (copy test files) #1467 Signed-off-by: christian.lutnik --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 74f92096..361d29d6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,7 +51,7 @@ e2e = [ "git submodule sync", "git submodule init", "git submodule update --init", - "cp -r spec/specification/assets/gherkin/. tests/features/", + "cp -r spec/specification/assets/gherkin/evaluation.feature tests/features/", "behave tests/features/", "rm tests/features/*.feature", ] From 318d736900a3923bfb75c1802267be03dca96e78 Mon Sep 17 00:00:00 2001 From: "christian.lutnik" Date: Thu, 23 Jan 2025 11:26:10 +0100 Subject: [PATCH 07/10] fixup! feat: Update test harness (copy test files) #1467 Signed-off-by: christian.lutnik --- pyproject.toml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 361d29d6..2e076acd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,9 +48,7 @@ cov = [ "cov-report", ] e2e = [ - "git submodule sync", - "git submodule init", - "git submodule update --init", + "git submodule add --force https://github.com/open-feature/spec.git spec", "cp -r spec/specification/assets/gherkin/evaluation.feature tests/features/", "behave tests/features/", "rm tests/features/*.feature", From b24ed1adfefdadf7626c758e03b1270ec01f39ac Mon Sep 17 00:00:00 2001 From: "christian.lutnik" Date: Thu, 23 Jan 2025 11:31:14 +0100 Subject: [PATCH 08/10] fixup! feat: Update test harness (copy test files) #1467 Signed-off-by: christian.lutnik --- tests/features/steps/steps.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/features/steps/steps.py b/tests/features/steps/steps.py index ff517dfa..0cfe6274 100644 --- a/tests/features/steps/steps.py +++ b/tests/features/steps/steps.py @@ -10,6 +10,7 @@ from openfeature.provider.in_memory_provider import InMemoryProvider from tests.features.data import IN_MEMORY_FLAGS + # Common step definitions @@ -30,6 +31,12 @@ def step_impl(context): context.client = get_client() +@given("a provider is registered") +def step_impl(context): + set_provider(InMemoryProvider(IN_MEMORY_FLAGS)) + context.client = get_client() + + @when( 'a {flag_type} flag with key "{key}" is evaluated with details and default value ' '"{default_value}"' From 401d1b1c103a78e697a1873ab4ac9d94072c2812 Mon Sep 17 00:00:00 2001 From: "christian.lutnik" Date: Thu, 23 Jan 2025 11:42:18 +0100 Subject: [PATCH 09/10] fixup! feat: Update test harness (copy test files) #1467 Signed-off-by: christian.lutnik --- tests/features/steps/steps.py | 72 ++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 35 deletions(-) diff --git a/tests/features/steps/steps.py b/tests/features/steps/steps.py index 0cfe6274..b1d4cd6c 100644 --- a/tests/features/steps/steps.py +++ b/tests/features/steps/steps.py @@ -1,5 +1,7 @@ # flake8: noqa: F811 +from time import sleep + from behave import given, then, when from openfeature.api import get_client, set_provider @@ -18,7 +20,7 @@ 'the resolved {flag_type} details reason of flag with key "{key}" should be ' '"{reason}"' ) -def step_impl(context, flag_type, key, expected_reason): +def step_impl_resolved_should_be(context, flag_type, key, expected_reason): details: FlagEvaluationDetails = None if flag_type == "boolean": details = context.boolean_flag_details @@ -26,13 +28,13 @@ def step_impl(context, flag_type, key, expected_reason): @given("a provider is registered with cache disabled") -def step_impl(context): +def step_impl_provider_without_cache(context): set_provider(InMemoryProvider(IN_MEMORY_FLAGS)) context.client = get_client() @given("a provider is registered") -def step_impl(context): +def step_impl_provider(context): set_provider(InMemoryProvider(IN_MEMORY_FLAGS)) context.client = get_client() @@ -41,7 +43,7 @@ def step_impl(context): 'a {flag_type} flag with key "{key}" is evaluated with details and default value ' '"{default_value}"' ) -def step_impl(context, flag_type, key, default_value): +def step_impl_evaluated_with_details(context, flag_type, key, default_value): context.client = get_client() if flag_type == "boolean": context.boolean_flag_details = context.client.get_boolean_details( @@ -57,7 +59,7 @@ def step_impl(context, flag_type, key, default_value): 'a boolean flag with key "{key}" is evaluated with {eval_details} and default ' 'value "{default_value}"' ) -def step_impl(context, key, eval_details, default_value): +def step_impl_bool_evaluated_with_details_and_default(context, key, eval_details, default_value): client: OpenFeatureClient = context.client context.boolean_flag_details = client.get_boolean_details(key, default_value) @@ -67,7 +69,7 @@ def step_impl(context, key, eval_details, default_value): 'a {flag_type} flag with key "{key}" is evaluated with default value ' '"{default_value}"' ) -def step_impl(context, flag_type, key, default_value): +def step_impl_evaluated_with_default(context, flag_type, key, default_value): client: OpenFeatureClient = context.client if flag_type == "boolean": @@ -77,12 +79,12 @@ def step_impl(context, flag_type, key, default_value): @then('the resolved string value should be "{expected_value}"') -def step_impl(context, expected_value): +def step_impl_resolved_string_should_be(context, expected_value): assert expected_value == context.string_flag_details.value @then('the resolved boolean value should be "{expected_value}"') -def step_impl(context, expected_value): +def step_impl_resolved_bool_should_be(context, expected_value): assert parse_boolean(expected_value) == context.boolean_flag_details.value @@ -90,7 +92,7 @@ def step_impl(context, expected_value): 'an integer flag with key "{key}" is evaluated with details and default value ' "{default_value:d}" ) -def step_impl(context, key, default_value): +def step_impl_int_evaluated_with_details_and_default(context, key, default_value): context.flag_key = key context.default_value = default_value context.integer_flag_details = context.client.get_integer_details( @@ -101,7 +103,7 @@ def step_impl(context, key, default_value): @when( 'an integer flag with key "{key}" is evaluated with default value {default_value:d}' ) -def step_impl(context, key, default_value): +def step_impl_int_evaluated_with_default(context, key, default_value): context.flag_key = key context.default_value = default_value context.integer_flag_details = context.client.get_integer_details( @@ -110,26 +112,26 @@ def step_impl(context, key, default_value): @when('a float flag with key "{key}" is evaluated with default value {default_value:f}') -def step_impl(context, key, default_value): +def step_impl_float_evaluated_with_default(context, key, default_value): context.flag_key = key context.default_value = default_value context.float_flag_details = context.client.get_float_details(key, default_value) @when('an object flag with key "{key}" is evaluated with a null default value') -def step_impl(context, key): +def step_impl_obj_evaluated_with_default(context, key): context.flag_key = key context.default_value = None context.object_flag_details = context.client.get_object_details(key, None) @then("the resolved integer value should be {expected_value:d}") -def step_impl(context, expected_value): +def step_impl_resolved_int_should_be(context, expected_value): assert expected_value == context.integer_flag_details.value @then("the resolved float value should be {expected_value:f}") -def step_impl(context, expected_value): +def step_impl_resolved_bool_should_be(context, expected_value): assert expected_value == context.float_flag_details.value @@ -138,7 +140,7 @@ def step_impl(context, expected_value): 'the resolved boolean details value should be "{expected_value}", the variant ' 'should be "{variant}", and the reason should be "{reason}"' ) -def step_impl(context, expected_value, variant, reason): +def step_impl_resolved_bool_should_be_with_reason(context, expected_value, variant, reason): assert parse_boolean(expected_value) == context.boolean_flag_details.value assert variant == context.boolean_flag_details.variant assert reason == context.boolean_flag_details.reason @@ -148,7 +150,7 @@ def step_impl(context, expected_value, variant, reason): 'the resolved string details value should be "{expected_value}", the variant ' 'should be "{variant}", and the reason should be "{reason}"' ) -def step_impl(context, expected_value, variant, reason): +def step_impl_resolved_string_should_be_with_reason(context, expected_value, variant, reason): assert expected_value == context.string_flag_details.value assert variant == context.string_flag_details.variant assert reason == context.string_flag_details.reason @@ -158,7 +160,7 @@ def step_impl(context, expected_value, variant, reason): 'the resolved object value should be contain fields "{field1}", "{field2}", and ' '"{field3}", with values "{val1}", "{val2}" and {val3}, respectively' ) -def step_impl(context, field1, field2, field3, val1, val2, val3): +def step_impl_resolved_obj_should_contain(context, field1, field2, field3, val1, val2, val3): value = context.object_flag_details.value assert field1 in value assert field2 in value @@ -169,7 +171,7 @@ def step_impl(context, field1, field2, field3, val1, val2, val3): @then('the resolved flag value is "{flag_value}" when the context is empty') -def step_impl(context, flag_value): +def step_impl_resolved_is_with_empty_context(context, flag_value): context.string_flag_details = context.client.get_boolean_details( context.flag_key, context.default_value ) @@ -180,13 +182,13 @@ def step_impl(context, flag_value): "the reason should indicate an error and the error code should indicate a missing " 'flag with "{error_code}"' ) -def step_impl(context, error_code): +def step_impl_reason_should_indicate(context, error_code): assert context.string_flag_details.reason == Reason.ERROR assert context.string_flag_details.error_code == ErrorCode[error_code] @then("the default {flag_type} value should be returned") -def step_impl(context, flag_type): +def step_impl_return_default(context, flag_type): flag_details = getattr(context, f"{flag_type}_flag_details") assert context.default_value == flag_details.value @@ -195,7 +197,7 @@ def step_impl(context, flag_type): 'a float flag with key "{key}" is evaluated with details and default value ' "{default_value:f}" ) -def step_impl(context, key, default_value): +def step_impl_float_with_details(context, key, default_value): context.float_flag_details = context.client.get_float_details(key, default_value) @@ -203,7 +205,7 @@ def step_impl(context, key, default_value): "the resolved float details value should be {expected_value:f}, the variant should " 'be "{variant}", and the reason should be "{reason}"' ) -def step_impl(context, expected_value, variant, reason): +def step_impl_resolved_float_with_variant(context, expected_value, variant, reason): assert expected_value == context.float_flag_details.value assert variant == context.float_flag_details.variant assert reason == context.float_flag_details.reason @@ -212,7 +214,7 @@ def step_impl(context, expected_value, variant, reason): @when( 'an object flag with key "{key}" is evaluated with details and a null default value' ) -def step_impl(context, key): +def step_impl_eval_obj(context, key): context.object_flag_details = context.client.get_object_details(key, None) @@ -220,7 +222,7 @@ def step_impl(context, key): 'the resolved object details value should be contain fields "{field1}", "{field2}",' ' and "{field3}", with values "{val1}", "{val2}" and {val3}, respectively' ) -def step_impl(context, field1, field2, field3, val1, val2, val3): +def step_impl_eval_obj_with_fields(context, field1, field2, field3, val1, val2, val3): value = context.object_flag_details.value assert field1 in value assert field2 in value @@ -231,7 +233,7 @@ def step_impl(context, field1, field2, field3, val1, val2, val3): @then('the variant should be "{variant}", and the reason should be "{reason}"') -def step_impl(context, variant, reason): +def step_impl_variant(context, variant, reason): assert variant == context.object_flag_details.variant assert reason == context.object_flag_details.reason @@ -240,7 +242,7 @@ def step_impl(context, variant, reason): 'context contains keys "{key1}", "{key2}", "{key3}", "{key4}" with values "{val1}",' ' "{val2}", {val3:d}, "{val4}"' ) -def step_impl(context, key1, key2, key3, key4, val1, val2, val3, val4): +def step_impl_context(context, key1, key2, key3, key4, val1, val2, val3, val4): context.evaluation_context = EvaluationContext( None, { @@ -253,7 +255,7 @@ def step_impl(context, key1, key2, key3, key4, val1, val2, val3, val4): @when('a flag with key "{key}" is evaluated with default value "{default_value}"') -def step_impl(context, key, default_value): +def step_impl_flag_with_key_and_default(context, key, default_value): context.flag_key = key context.default_value = default_value context.string_flag_details = context.client.get_string_details( @@ -262,7 +264,7 @@ def step_impl(context, key, default_value): @then('the resolved string response should be "{expected_value}"') -def step_impl(context, expected_value): +def step_impl_reason(context, expected_value): assert expected_value == context.string_flag_details.value @@ -270,7 +272,7 @@ def step_impl(context, expected_value): 'a non-existent string flag with key "{flag_key}" is evaluated with details and a ' 'default value "{default_value}"' ) -def step_impl(context, flag_key, default_value): +def step_impl_non_existing(context, flag_key, default_value): context.flag_key = flag_key context.default_value = default_value context.string_flag_details = context.client.get_string_details( @@ -282,7 +284,7 @@ def step_impl(context, flag_key, default_value): 'a string flag with key "{flag_key}" is evaluated as an integer, with details and a' " default value {default_value:d}" ) -def step_impl(context, flag_key, default_value): +def step_impl_string_with_details(context, flag_key, default_value): context.flag_key = flag_key context.default_value = default_value context.integer_flag_details = context.client.get_integer_details( @@ -294,7 +296,7 @@ def step_impl(context, flag_key, default_value): "the reason should indicate an error and the error code should indicate a type " 'mismatch with "{error_code}"' ) -def step_impl(context, error_code): +def step_impl_type_mismatch(context, error_code): assert context.integer_flag_details.reason == Reason.ERROR assert context.integer_flag_details.error_code == ErrorCode[error_code] @@ -306,17 +308,17 @@ def step_impl(context, error_code): 'the flag\'s configuration with key "{key}" is updated to defaultVariant ' '"{variant}"' ) -def step_impl(context, key, variant): +def step_impl_config_update(context, key, variant): raise NotImplementedError("Step definition not implemented yet") @given("sleep for {duration} milliseconds") -def step_impl(context, duration): - raise NotImplementedError("Step definition not implemented yet") +def step_impl_sleep(context, duration): + sleep(float(duration) * .001) @then('the resolved string details reason should be "{reason}"') -def step_impl(context, reason): +def step_impl_reason_should_be(context, reason): raise NotImplementedError("Step definition not implemented yet") From 401d97440b095055fe12c697e7bda92e43336a3a Mon Sep 17 00:00:00 2001 From: "christian.lutnik" Date: Thu, 23 Jan 2025 12:27:02 +0100 Subject: [PATCH 10/10] fixup! feat: Update test harness (copy test files) #1467 Signed-off-by: christian.lutnik --- tests/features/steps/steps.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/tests/features/steps/steps.py b/tests/features/steps/steps.py index b1d4cd6c..dd8f264c 100644 --- a/tests/features/steps/steps.py +++ b/tests/features/steps/steps.py @@ -12,7 +12,6 @@ from openfeature.provider.in_memory_provider import InMemoryProvider from tests.features.data import IN_MEMORY_FLAGS - # Common step definitions @@ -59,7 +58,9 @@ def step_impl_evaluated_with_details(context, flag_type, key, default_value): 'a boolean flag with key "{key}" is evaluated with {eval_details} and default ' 'value "{default_value}"' ) -def step_impl_bool_evaluated_with_details_and_default(context, key, eval_details, default_value): +def step_impl_bool_evaluated_with_details_and_default( + context, key, eval_details, default_value +): client: OpenFeatureClient = context.client context.boolean_flag_details = client.get_boolean_details(key, default_value) @@ -140,7 +141,9 @@ def step_impl_resolved_bool_should_be(context, expected_value): 'the resolved boolean details value should be "{expected_value}", the variant ' 'should be "{variant}", and the reason should be "{reason}"' ) -def step_impl_resolved_bool_should_be_with_reason(context, expected_value, variant, reason): +def step_impl_resolved_bool_should_be_with_reason( + context, expected_value, variant, reason +): assert parse_boolean(expected_value) == context.boolean_flag_details.value assert variant == context.boolean_flag_details.variant assert reason == context.boolean_flag_details.reason @@ -150,7 +153,9 @@ def step_impl_resolved_bool_should_be_with_reason(context, expected_value, varia 'the resolved string details value should be "{expected_value}", the variant ' 'should be "{variant}", and the reason should be "{reason}"' ) -def step_impl_resolved_string_should_be_with_reason(context, expected_value, variant, reason): +def step_impl_resolved_string_should_be_with_reason( + context, expected_value, variant, reason +): assert expected_value == context.string_flag_details.value assert variant == context.string_flag_details.variant assert reason == context.string_flag_details.reason @@ -160,7 +165,9 @@ def step_impl_resolved_string_should_be_with_reason(context, expected_value, var 'the resolved object value should be contain fields "{field1}", "{field2}", and ' '"{field3}", with values "{val1}", "{val2}" and {val3}, respectively' ) -def step_impl_resolved_obj_should_contain(context, field1, field2, field3, val1, val2, val3): +def step_impl_resolved_obj_should_contain( + context, field1, field2, field3, val1, val2, val3 +): value = context.object_flag_details.value assert field1 in value assert field2 in value @@ -314,7 +321,7 @@ def step_impl_config_update(context, key, variant): @given("sleep for {duration} milliseconds") def step_impl_sleep(context, duration): - sleep(float(duration) * .001) + sleep(float(duration) * 0.001) @then('the resolved string details reason should be "{reason}"')