Skip to content

Development

Build and test

sbt compile                 # all published modules, Scala 3
sbt +test                   # Scala 2.13 and 3
sbt scalafmtAll             # format
sbt conformance/test        # the OpenFeature spec's gherkin suite, via Cucumber
sbt conformanceZioBdd/test  # the same suite via zio-bdd (JDK 22+)
sbt mimaReportBinaryIssues  # binary compatibility with the last release

The examples/ projects are not published, but CI compiles them:

Project What it shows
examplesDocs Every code sample on this site, plus tests that run them
examplesTestkitApp Unit-testing a flag-driven service with TestFeatureProvider
examplesOfrepInitTimeout An OFREP-backed app with an init timeout and a circuit breaker

Mutation testing

sbt core/stryker runs stryker4s on core: it makes small deliberate changes to the code (flipping a condition, swapping an operator) and checks that some test fails for each. The score and the surviving mutants are in core/target/stryker4s-report/. A full run takes hours, so CI runs it weekly (the Mutation testing workflow, also startable by hand) and never on a pull request; the score is informational and never fails the build. To try it on a few files, narrow mutate in stryker4s.conf locally.

Property tests

LifecycleModelSpec in the testkit generates sequences of lifecycle actions (swaps to providers that start, fail, hang or start slowly, provider events, shutdown, an interrupted swap) and runs each against a real FeatureFlags, checking after every step that the status, the provider name and the evaluations agree with a model written from Lifecycle and events. It also checks that the scope closes within 5 seconds and leaves no fiber or thread behind. Pull requests run 25 sequences; the weekly Property tests workflow (also startable by hand) runs 300. Set LIFECYCLE_MODEL_SAMPLES to choose the count locally:

LIFECYCLE_MODEL_SAMPLES=300 sbt "testkit/testOnly *LifecycleModelSpec"

A failure prints the shrunk sequence. Add it to the spec's regressions suite as an explicit list, not as a seed.

Provider fault tests

provider-fault-tests runs the real OFREP and Optimizely providers through rift-scala's in-process TLS intercept proxy, while scripted network faults (rejected credentials, 503, 429, a connection reset, a stalled response, a truncated or non-JSON body) play out. It asserts what the library does about each: a typed error or the fallback's answer, in bounded time. See Testing real providers for the approach.

sbt providerFaultTests/test    # JDK 22+

It is a dev-machine check, by design:

  • It is not aggregated into the root project, so sbt test and sbt +test never run it.
  • CI only compiles it (providerFaultTests/Test/compile). No CI job runs its tests, and none holds a secret for it.
  • It needs JDK 22 or later, for rift's native engine. On a JDK where the engine cannot load, the suites are reported as ignored. The tests make no external network calls: the proxy and the stand-in servers are in-process.

The module enables the sbt-rift plugin, which writes a throwaway CA and truststore under provider-fault-tests/target/rift and points the forked test JVM at them.

The documentation site

The site is built with MkDocs Material from docs/, and published to GitHub Pages from main.

Code samples are not written in the pages. They live in examples/docs/src/main/scala-3/snippets/ (and, for the rift code on the real-providers page, provider-fault-tests/src/test/scala-3/snippets/), between --8<-- "File.scala:name". So every sample compiles, and examples/docs/src/test runs the ones that can run on their own and checks what the page says about them.

sbt examplesDocs/test                      # compile and check the samples
pip install -r requirements-docs.txt
mkdocs serve                               # preview at http://127.0.0.1:8000
mkdocs build --strict                      # what CI runs: fails on broken links and missing snippets

The animations are SVG files generated by the Python scripts in scripts/visuals/, one per animation, on top of the small toolkit in hud.py. Edit the script, not the SVG, then regenerate:

python3 scripts/visuals/build.py              # all of them
python3 scripts/visuals/build.py evaluation   # one

CI fails if a committed SVG is out of date with its script.

Releasing

See RELEASING.md.