Conversation
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: true
Comment |
aepfli
force-pushed
the
feat/provider-tck-flagsmith
branch
8 times, most recently
from
September 14, 2026 09:44
4ded5e1 to
894a27a
Compare
aepfli
force-pushed
the
feat/provider-tck
branch
from
September 14, 2026 14:00
ccf78a3 to
b9f5150
Compare
aepfli
force-pushed
the
feat/provider-tck-flagsmith
branch
2 times, most recently
from
September 15, 2026 20:34
306466d to
d44f20f
Compare
aepfli
force-pushed
the
feat/provider-tck-flagsmith
branch
from
September 16, 2026 07:46
d44f20f to
c00a3b8
Compare
aepfli
force-pushed
the
feat/provider-tck
branch
from
September 16, 2026 19:49
3e0553e to
92fefde
Compare
aepfli
force-pushed
the
feat/provider-tck-flagsmith
branch
2 times, most recently
from
September 21, 2026 11:00
5302fdc to
e2baaf6
Compare
Experimental adoption. 52 scenarios: 20 pass, 12 fail, 20 skipped. Eight of the twelve failures are 'expected STATIC but was null' -- this provider never populates the resolution reason. The values are all correct; only the reason is missing. 2.2.5 makes it a SHOULD, so null is arguably permitted, but the Go and Python Flagsmith providers both populate it against the identical backend. Two more are float resolution. Flagsmith stores floats as strings, because feature_state_value is natively boolean, integer or string only. Go's provider parses the string back; this one type-checks and falls back to the code default, so GetFloatValue never works against Flagsmith. The last two are shared with every other language: float-flag and object-flag requested as a String succeed, because on this backend both really are strings. @large-integers is withheld deliberately: Java's accessor is a 32-bit Integer, so 2^53-1 cannot be asked for. Same reason Java withholds it for flagd, and it accounts for the extra skip against Go's 19. The Jackson pin is a workaround for a TCK-introduced conflict, not a provider defect: provider-tck exports jackson-databind 2.22.1 while flagsmith-java-client pins jackson-annotations 2.15.2, and the client's ObjectMapper then dies on JsonSerializeAs. Verified by removing the TCK dependency, after which the provider's own 35 tests pass. Signed-off-by: Simon Schrottner <simon.schrottner@flagsmith.com>
Flagsmith's native model is `enabled` plus a value, so the canonical set's four disabled-* flags map straight onto it. The testbed grew the flags in the same pass. 56 scenarios: 24 pass, 12 fail, 20 skip. The four new scenarios pass; the twelve failures are unchanged. Signed-off-by: Simon Schrottner <simon.schrottner@flagsmith.com>
The tool moved from tools/provider-tck to tools/tck and its package from dev.openfeature.contrib.tools.providertck to dev.openfeature.contrib.tools.tck. Dependency and imports follow. The explicit testcontainers dependency is now load-bearing rather than redundant: the TCK made its own provided and optional, so an adopter declares it. The Jackson pin stays, and was re-tested rather than assumed -- removing it still fails with ClassNotFoundException on JsonSerializeAs, so the TCK still exports databind 2.22.1 against the client's annotations 2.15.2. The comment is corrected: it is compile scope and has to be, because the provider's own main source imports com.fasterxml.jackson.databind. Saying 'test scope only' was wrong. 56 scenarios: 24 pass, 12 fail, 20 skip. Unchanged by the rebase. Signed-off-by: Simon Schrottner <simon.schrottner@flagsmith.com>
…onvention The compose file moves to src/test/resources/tck/docker-compose.yaml, the path the TCK javadoc uses, and the image is pinned to 0.1.0 rather than :latest. Pinning is the substantive half. Four language adoptions pull this image, and a mutable tag lets a push to the testbed change four pull requests' results with no diff anywhere to explain it. The tag stays overridable through FLAGSMITH_TESTBED_IMAGE. 56 scenarios: 24 pass, 12 fail, 20 skip. Unchanged. Signed-off-by: Simon Schrottner <simon.schrottner@flagsmith.com>
The base added @standard-reasons, and it is the capability that turned eight of this provider's twelve failures into skips: it never populates the resolution reason, so every evaluation returns null and not one of the reasons the capability claims is reported. The resolved values were correct throughout -- only the reason was missing. Withheld with a deviation rather than simply skipped, because 2.2.5 makes the reason a SHOULD and null is arguably permitted. What makes it a defect worth recording is that the Go Flagsmith provider reports STATIC, DISABLED and TARGETING_MATCH against the identical backend, so this is a gap rather than a considered choice. The numeric-coercion deviation is now measured rather than predicted: this provider behaves like Python, not like Go. float-flag resolves to the caller default, so reading a float back as a float does not work against Flagsmith at all. 65 scenarios: 31 pass, 5 fail, 29 skip -- up from 24/12/20. The five remaining are two unreadable floats, the shared type-system pair, and object-flag not resolving as a structure. Signed-off-by: Simon Schrottner <simon.schrottner@flagsmith.com>
The base corrected the worked example that taught withhold-plus-deviate, and this adoption was doing exactly what the old example illustrated. KnownDeviation now states the rule plainly: withholding a capability in order to turn a failing scenario into a skip is the failure mode the field exists to prevent. @standard-reasons moves from withheld to declared. The previous pass withheld it precisely to turn eight failures into skips, which is the move the rule names. This provider does build a resolution and simply leaves the reason out, so running those scenarios establishes something real, and the failures belong in the results with the deviation attached. @numeric-coercion moves the same way, for the same reason: the provider has the accessors and gets the answer wrong rather than declining to answer. Adds an ungated deviation, with a null capability, for the two mandatory rows that fail because Flagsmith stores floats and objects as strings. That form exists for a gap against a scenario belonging to no tag. 65 scenarios: 35 pass, 13 fail, 17 skip -- from 31/5/29. Twelve more scenarios run; four of them pass and eight fail visibly, which is the point of the change. Signed-off-by: Simon Schrottner <simon.schrottner@flagsmith.com>
Three things were written here that are documented elsewhere: the rule for when a deviation is the right shape (tck.KnownDeviation owns it), the control API's inability to hand connection parameters to a provider, and the backend's design and cross-language results (the testbed's README and FINDINGS own those). Each is now a sentence and a link. What stays is what only this adoption knows: which capabilities this provider has, which it gets wrong, and why. The deviation summaries stay verbose on purpose -- they travel into a conformance report read across languages, so they have to stand alone. No behaviour change; the suite reports the same numbers. Signed-off-by: Simon Schrottner <simon.schrottner@flagsmith.com>
The deviation said the provider never populates the reason. Reading the source rather than the run output: it returns DISABLED for a disabled flag and leaves the reason null on every successful resolution, so STATIC and TARGETING_MATCH are the ones never reported. DISABLED is one of the reasons the capability claims, and saying otherwise understated what the provider does. Signed-off-by: Simon Schrottner <simon.schrottner@flagsmith.com>
…eplaces This is the backend the capability was created for, and the run says so rather than the prediction. With the tag declared: two of its four scenarios fail and two pass. float-flag through the String accessor resolves to "0.5" and object-flag to its raw JSON text, while boolean-flag and integer-flag report TYPE_MISMATCH correctly. The split is exactly Flagsmith's type system -- feature_state_value is natively boolean, integer or string, so a boolean really is a boolean and an integer really is an integer, while a float and a structure have no native type and are stored as strings. Asked for as strings, they are returned, and that is the resolved flag value. Withheld rather than declared-and-failing, which is the opposite call from @numeric-coercion beside it. Appendix F's scenario-level rule is subordinate to a prior question -- whether an answer is owed -- and here none is: TYPE_MISMATCH is obliged by no requirement, and the only normative statement about value type is Requirement 1.3.4, a SHOULD on the client. Where the specification permits declining, withholding is the honest report however askable the scenarios are. @numeric-coercion stays declared because flagd's ADR is a rule this suite binds providers to; nothing binds Flagsmith to report a type its backend lacks. The deviation that recorded these same two scenarios goes with it. It existed because they were mandatory and failing, and it called the question open; the pin answers it. Keeping it would assert a defect the specification says is not one, which is precisely what a knownDeviations entry must not do. Measured: 65 scenarios, 33 passing, 21 skipped, 11 failing -- against 35 passing, 17 skipped and 13 failing with the tag declared. The four scenarios now skip naming @string-typing, two of which this provider had been getting right; that cost is recorded in the javadoc rather than hidden, since the tag is the unit of declaration and this backend's typing is not uniform across the four flags. Signed-off-by: Simon Schrottner <simon.schrottner@flagsmith.com>
The previous pass withheld one tag over all four scenarios and wrote down what that cost: two of the four were skipped despite this provider getting them right, because the tag was the unit of declaration and Flagsmith's typing is not uniform across the four flags. That note ended "revisit if the capability is ever split by flag type". Specification revision bda599f1 split it, citing this measurement, so this is the revisit. feature_state_value is natively boolean, integer or string only. A boolean flag really is a boolean and an integer really is an integer, so @string-typing is declared and its two rows are held to. A float and a structure have no native type and are stored as strings, so asked for as strings they are returned -- there is no mismatch to report, and @fully-typed-values is withheld. Withheld rather than declared-and-failing for the reason the file already argued: TYPE_MISMATCH is obliged by no requirement, so no answer is owed and a deviation would assert a defect that does not exist. Measured, both sides of the re-pin: 65 scenarios either way, 33 passing / 11 failing / 21 skipped before, 35 / 11 / 19 after. The two scenarios that started running are the boolean and integer rows and both pass; the failure count does not move. The skips now attribute as VARIANTS 8, LIFECYCLE 6, FULLY_TYPED_VALUES 2, EVENTS 2 and LARGE_INTEGERS 1 -- where the withheld capability was answering for four scenarios it now answers for exactly the two whose question this backend cannot be asked. Also fixes a contradiction this file carried: the numeric-coercion deviation said the capability was "withheld pending the run" while capabilities() declared it, and its indentation had drifted out of line. The run has since happened, so it now records what was measured -- Java resembles Python rather than Go and does not parse Flagsmith's string-stored float, which fails both lossless coercion scenarios and the untagged float scenario with the caller's default. Declared and left failing, because the provider does attempt the coercion. Signed-off-by: Simon Schrottner <simon.schrottner@flagsmith.com>
173 lines to 132. The string-typing note argued the case for the capability split across five paragraphs; the split has landed, so one paragraph saying which half this backend answers is enough. The deviation summaries lose their narrative and keep their claim. No behaviour change. Signed-off-by: Simon Schrottner <simon.schrottner@flagsmith.com>
aepfli
force-pushed
the
feat/provider-tck-flagsmith
branch
from
October 1, 2026 08:11
e2baaf6 to
f91d8eb
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Runs the OpenFeature Provider Conformance Suite against the Flagsmith Java provider.
Stacked on
feat/provider-tck. Companion to go-sdk-contrib#959 and js-sdk-contrib#1623 — same container, same scenarios.Result: 35 pass, 11 fail, 19 skipped of 65
@standard-reasonsis what changed this run. It was 24 pass / 12 fail before that capability existed; eight of those twelve failures were this provider not populating the resolution reason, and they are now skips carrying a recorded reason rather than bare failures.@standard-reasons— declared, and failingThis provider never populates the resolution reason. Every evaluation returns
null, so not one of the reasons the capability claims is reported. The resolved values are correct throughout; only the reason is missing.An earlier pass withheld this capability, which turned eight failures into skips — the exact move
KnownDeviationexists to discourage, and the base has since corrected the worked example that taught it. The provider does build a resolution and simply leaves the reason out, so running those scenarios establishes something and the failures belong in the results with the deviation attached.What makes it a defect rather than a permitted absence is that the Go Flagsmith provider reports
STATIC,DISABLEDandTARGETING_MATCHagainst the identical backend.The 5 remaining failures
feature_state_valueis natively boolean, integer or string — so every float is stored as a string. Go's provider parses it back ("Because We store floats as string"); this one type-checks and falls through to the caller default, sogetFloatValuenever works against Flagsmith. Python has the same gap; the@numeric-coerciondeviation now records this as measured rather than predicted.float-flagandobject-flagrequested as a String succeed rather than reportingTYPE_MISMATCH, because on this backend both genuinely are strings. Not a provider bug in any of the four languages.object-flagnot resolving as a structure.@large-integers— refused by the TCK, and correctlyThe base now refuses this capability in Java rather than leaving it to the adopter: Java's integer accessor is a 32-bit
Integer, so 2^53−1 cannot be asked for at all. That is a property of the SDK, not of the provider, and it is the same reason Java withholds it for flagd.Worth knowing alongside it:
Value.asInteger()silently truncates aLongto its low 32 bits —4294967301becomes5,2^53−1becomes−1— with no error. That is a defect in the Java SDK rather than anything this PR touches, but it is why the refusal matters: declaring the capability would not have errored, it would have returned a plausible wrong number.Other capabilities
Declared:
@object,@targeting,@disabled-flags,@string-typing.@string-typingdeclared,@fully-typed-valueswithheld — and this adoption is why the suitesplit them. Flagsmith's
feature_state_valueis natively boolean, integer or string, so thisprovider correctly reports
TYPE_MISMATCHfor a boolean and an integer asked for as strings, whilefloats and structures are stored as text and cannot be asked the question at all. One tag over all
four made this suite withhold everything and give up two passes it had earned; splitting them
recovered exactly those two, 33 → 35 passing and 21 → 19 skipped, with no change to the provider.
This file's own note ended "revisit if the capability is ever split by flag type" — this is that
revisit.
@disabled-flagsholds because Flagsmith models a feature state asenabledplus a value, so the canonical set's fourdisabled-*flags map straight onto it, and this provider returns the caller default with reasonDISABLED. Go agrees; Python and JS both raiseGENERALand withhold it.@variantsis withheld: Flagsmith has no variant concept for a plain feature, and the evaluation response carries no variant key. Permitted rather than defective — 2.2.4 makes it a SHOULD — so no deviation.The lifecycle and event capabilities are withheld because this provider has no observable initialisation for the suite to assert against.
A finding about the TCK, not the provider
tools/tckexportsjackson-databind 2.22.1, whileflagsmith-java-client7.4.3 pinsjackson-annotationsandjackson-coreat2.15.2. Maven's nearest-wins assembles databind 2.22 against annotations 2.15, and the Flagsmith client'sObjectMapperdies withClassNotFoundException: com.fasterxml.jackson.annotation.JsonSerializeAs.Verified this is the TCK's doing: with the TCK dependency removed the provider's own 35 tests pass; with it added, 22 error. Re-tested after the TCK moved testcontainers to
provided/optional— still present, so it wants its own fix upstream: shade Jackson, or stop exporting a hard databind version.The pin here is the workaround. It is compile scope and has to be, because the provider's own main source imports
com.fasterxml.jackson.databind. It cannot be aligned upward —jackson-annotationshas no 2.22.1 release, it tracks its own version line — so that route needs the Jackson BOM.The backend
aepfli/flagsmith-tck-testbed
0.1.0, pinned rather than:latestbecause four adoptions pull it and a mutable tag lets a push change four PRs' results with no diff to explain it.Why this is a draft