Skip to content

test: add gherkin e2e suite for evaluation.feature - #127

Draft
mtonko-flx wants to merge 4 commits into
open-feature:mainfrom
mtonko-flx:test-gherkin-e2e
Draft

mtonko-flx wants to merge 4 commits into
open-feature:mainfrom
mtonko-flx:test-gherkin-e2e

Conversation

@mtonko-flx

@mtonko-flx mtonko-flx commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Depends on #126 and rebased onto its head (0f811f5). #125 has merged; until #126 merges, this PR's diff also contains its three commits.

Closes #105.

Intent

Appendix A requires end-to-end tests to run against an in-memory provider and be self-contained, using the shared Gherkin suite from Appendix B. This adds open-feature/spec as a submodule and runs evaluation.feature against InMemoryProvider through the public OpenFeatureAPI and Client surface, with no mocks or stubs anywhere in the path.

Motivation

Nothing in this repository validated the SDK end to end. The unit tests each verify one component against a double, so a defect in the seam between them (how the client dispatches by flag type, what it does with a provider throw, whether the evaluation context reaches the provider at all) would not have been caught. The shared suite is also how the ecosystem checks that SDKs agree with each other, and this SDK was not running it. evaluation.feature is what js-sdk and go-sdk run today; evaluation_v2.feature is left for follow-up, because it additionally needs test-flags.json parsing, provider-state doubles, and metadata and data-table steps.

Changes

Implementation

  • Add open-feature/spec as a submodule at spec/, pinned by commit with no branch = key, matching java-sdk and js-sdk.
  • Add an e2e/ Swift package, separate from the root package, depending on it by path and on CucumberSwift. CucumberSwift declares no watchOS support and uses XCTAttachment/XCTContext unconditionally, so a root test target would break the watchOS CI job and add CucumberSwift plus CucumberSwiftExpressions to every consumer's dependency graph. The root Package.resolved still pins swift-log alone.
  • Declare the path dependency as .package(name: "OpenFeature", path: ".."), so package identity does not follow the checkout directory name.
  • Add scripts/e2e, which initialises the submodule, copies evaluation.feature into the test target's gitignored Features/ directory, then runs swift test in e2e/.
  • Copy the feature file rather than symlink it: swift test copies a symlinked resource directory as a dangling symlink while Xcode follows it, so a symlink would pass locally and crash in CI. Copying one file also keeps evaluation_v2, hooks, metadata and contextMerging out of the bundle, which CucumberSwift would otherwise enumerate and fail on.
  • Declare resources with .copy, not .process: .process flattens directories, and CucumberSwift resolves features via bundle.url(forResource: "Features", withExtension: nil).
  • Set the step implementation's bundle to Bundle.module, not Bundle(for:) as in CucumberSwift's own consumer test, because under SwiftPM the copied resources live in a sibling bundle rather than inside the .xctest.
  • Map the spec's SCREAMING_SNAKE error-code names to ErrorCode cases in GherkinArguments.errorCode(_:), failing the step on a code the Gherkin names but the SDK does not define. Java gets this from ErrorCode.valueOf; Swift's cases are camelCase, so the map is explicit and ErrorCode's public representation stays untouched.
  • Anchor all 32 step patterns as ^...$ in one file. CucumberSwift matches unanchored and case-insensitively and lets the last matching registration win, so an unanchored a flag with key "..." would silently claim a boolean flag with key "...".
  • Install per-scenario state from the Given a stable provider background step and clear it before every scenario. That step registers a fresh provider with an explicitly empty initialContext, which is what makes the suite order-independent: this is a static-context SDK, so the evaluation context is global and the context-aware scenario mutates it.
  • Add a macOS-only E2E CI job that checks out with submodules: recursive.
  • Exclude e2e/.build and spec/ from SwiftLint, and extend scripts/swift-format to cover e2e/Tests.
  • Document in CONTRIBUTING.md how to run the suite, why swift test inside e2e/ traps on a fresh clone, why --filter silently matches zero tests, and how to bump the spec pin.

Testing

  • All 13 scenarios and 45 steps pass: boolean, string, integer, float and object values; the same five with details, asserting variant and reason; context-aware resolution followed by re-resolution with an emptied context; flag not found; type mismatch.
  • CucumberSwift generates a stub test case for any step it cannot match, and the run produces none, so no step is silently unimplemented.
  • The suite can actually fail: flipping string-flag's default variant from greeting to parting fails only the three string steps that assert the resolved value.
  • Mis-mapping FLAG_NOT_FOUND to .parseError fails only the Flag not found scenario, so the error-code map is load-bearing rather than decorative.
  • Against feat: add InMemoryProvider #126's stricter getObjectEvaluation, which rejects non-.structure variants, all 13 scenarios still pass, because object-flag's variants are both structures. Pointing its template variant at a scalar fails exactly the two object scenarios, which confirms the suite exercises that guard rather than passing around it.
  • The root package is unaffected: swift test (168 tests) and xcodebuild test -scheme OpenFeature on the iOS simulator are both green, and the root Package.resolved is unchanged.

Breaking Changes

None - test-only, plus a new submodule. A fresh clone that skips submodules is unaffected unless it runs scripts/e2e, which initialises the submodule itself.

@coderabbitai

coderabbitai Bot commented Aug 18, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

Comment @coderabbitai help to get the list of available commands.

@mtonko-flx
mtonko-flx marked this pull request as draft August 18, 2026 12:31
@mtonko-flx
mtonko-flx force-pushed the test-gherkin-e2e branch 4 times, most recently from 3c0b87a to 143dce7 Compare August 19, 2026 14:35
Signed-off-by: Mark Tonkonoh <mark.tonkonoh@fluxon.com>
Signed-off-by: Mark Tonkonoh <mark.tonkonoh@fluxon.com>
Signed-off-by: Mark Tonkonoh <mark.tonkonoh@fluxon.com>
Signed-off-by: Mark Tonkonoh <mark.tonkonoh@fluxon.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add InMemory Provider and Basic E2E Tests

1 participant