diff --git a/providers/openfeature-provider-flagd/openfeature/test-harness b/providers/openfeature-provider-flagd/openfeature/test-harness index ff2fbe6c..190307b3 160000 --- a/providers/openfeature-provider-flagd/openfeature/test-harness +++ b/providers/openfeature-provider-flagd/openfeature/test-harness @@ -1 +1 @@ -Subproject commit ff2fbe6c6584953cb2753ae9188d1cee14f7f57f +Subproject commit 190307b3b1982773976f05464942f69bb23528a4 diff --git a/providers/openfeature-provider-flagd/src/openfeature/contrib/provider/flagd/resolvers/process/custom_ops.py b/providers/openfeature-provider-flagd/src/openfeature/contrib/provider/flagd/resolvers/process/custom_ops.py index 22c4ae69..9e834cfb 100644 --- a/providers/openfeature-provider-flagd/src/openfeature/contrib/provider/flagd/resolvers/process/custom_ops.py +++ b/providers/openfeature-provider-flagd/src/openfeature/contrib/provider/flagd/resolvers/process/custom_ops.py @@ -6,7 +6,7 @@ JsonPrimitive, ends_with, fractional, - parse_version, + normalize_version, sem_ver, starts_with, string_comp, diff --git a/providers/openfeature-provider-flagd/tests/e2e/inprocess/conftest.py b/providers/openfeature-provider-flagd/tests/e2e/inprocess/conftest.py index 12f7520e..804bea12 100644 --- a/providers/openfeature-provider-flagd/tests/e2e/inprocess/conftest.py +++ b/providers/openfeature-provider-flagd/tests/e2e/inprocess/conftest.py @@ -9,7 +9,6 @@ "~unixsocket", "~deprecated", "~fractional-v1", - "~operator-errors", ] diff --git a/providers/openfeature-provider-flagd/tests/e2e/rpc/conftest.py b/providers/openfeature-provider-flagd/tests/e2e/rpc/conftest.py index e00d257e..7922da21 100644 --- a/providers/openfeature-provider-flagd/tests/e2e/rpc/conftest.py +++ b/providers/openfeature-provider-flagd/tests/e2e/rpc/conftest.py @@ -12,7 +12,11 @@ "~deprecated", "~fractional-v1", "~operator-errors", + "~semver-v-prefix", + "~fractional-single-entry", + "~semver-numeric-context", ] +# TODO remove last 4 tags when adjusted flagd is released def pytest_collection_modifyitems(config, items): diff --git a/tools/openfeature-flagd-core/src/openfeature/contrib/tools/flagd/core/targeting/custom_ops.py b/tools/openfeature-flagd-core/src/openfeature/contrib/tools/flagd/core/targeting/custom_ops.py index 50d82994..c47eec2e 100644 --- a/tools/openfeature-flagd-core/src/openfeature/contrib/tools/flagd/core/targeting/custom_ops.py +++ b/tools/openfeature-flagd-core/src/openfeature/contrib/tools/flagd/core/targeting/custom_ops.py @@ -125,10 +125,10 @@ def string_comp( arg1, arg2 = args if not isinstance(arg1, str): logger.debug(f"incorrect argument for first argument, expected string: {arg1}") - return False + return None if not isinstance(arg2, str): logger.debug(f"incorrect argument for second argument, expected string: {arg2}") - return False + return None return comparator(arg1, arg2) @@ -144,8 +144,8 @@ def sem_ver(data: dict, *args: JsonLogicArg) -> bool | None: # noqa: C901 arg1, op, arg2 = args try: - v1 = parse_version(arg1) - v2 = parse_version(arg2) + v1 = normalize_version(arg1) + v2 = normalize_version(arg2) except ValueError as e: logger.exception(e) return None @@ -171,9 +171,15 @@ def sem_ver(data: dict, *args: JsonLogicArg) -> bool | None: # noqa: C901 return None -def parse_version(arg: typing.Any) -> semver.Version: +def normalize_version(arg: typing.Any) -> semver.Version: version = str(arg) if version.startswith(("v", "V")): version = version[1:] + # Pad partial versions (e.g. "1" → "1.0.0", "1.2" → "1.2.0") + numeric_part = version.split("-")[0].split("+")[0] + dot_count = numeric_part.count(".") + if dot_count < 2: + version = numeric_part + ".0" * (2 - dot_count) + version[len(numeric_part) :] + return semver.Version.parse(version) diff --git a/tools/openfeature-flagd-core/tests/test_targeting.py b/tools/openfeature-flagd-core/tests/test_targeting.py index 8d12ad14..9554a903 100644 --- a/tools/openfeature-flagd-core/tests/test_targeting.py +++ b/tools/openfeature-flagd-core/tests/test_targeting.py @@ -66,7 +66,7 @@ def test_starts_with_no_args(self) -> None: def test_starts_with_non_string(self) -> None: result = starts_with({}, 123, "abc") - assert result is False + assert result is None class TestEndsWith: @@ -78,6 +78,10 @@ def test_ends_with_false(self) -> None: result = ends_with({}, "hello world", "hello") assert result is False + def test_ends_with_non_string(self) -> None: + result = ends_with({}, 123, "abc") + assert result is None + class TestSemVer: def test_equal(self) -> None: @@ -113,6 +117,9 @@ def test_minor_no_match(self) -> None: def test_v_prefix(self) -> None: assert sem_ver({}, "v2.0.0", "=", "2.0.0") is True + def test_partial_version(self) -> None: + assert sem_ver({}, "2", "=", "2.0.0") is True + def test_invalid_version(self) -> None: result = sem_ver({}, "not-a-version", "=", "1.0.0") assert result is None