diff --git a/providers/ofrep/pom.xml b/providers/ofrep/pom.xml index 3f2fd6695..66064d472 100644 --- a/providers/ofrep/pom.xml +++ b/providers/ofrep/pom.xml @@ -17,6 +17,11 @@ OFREP Provider https://openfeature.dev + + + [0.0.1,) + + Rahul-Baradol @@ -81,5 +86,17 @@ 4.12.0 test + + + + dev.openfeature.contrib.tools + provider-tck + ${provider-tck.version} + test + diff --git a/providers/ofrep/src/test/java/dev/openfeature/contrib/providers/ofrep/e2e/OfrepTckTest.java b/providers/ofrep/src/test/java/dev/openfeature/contrib/providers/ofrep/e2e/OfrepTckTest.java new file mode 100644 index 000000000..b4153c240 --- /dev/null +++ b/providers/ofrep/src/test/java/dev/openfeature/contrib/providers/ofrep/e2e/OfrepTckTest.java @@ -0,0 +1,125 @@ +package dev.openfeature.contrib.providers.ofrep.e2e; + +import dev.openfeature.contrib.providers.ofrep.OfrepProvider; +import dev.openfeature.contrib.providers.ofrep.OfrepProviderOptions; +import dev.openfeature.contrib.tools.providertck.AbstractProviderTckTest; +import dev.openfeature.contrib.tools.providertck.BackendEndpoint; +import dev.openfeature.contrib.tools.providertck.Capability; +import dev.openfeature.sdk.FeatureProvider; +import java.io.File; +import java.time.Duration; +import java.util.Collections; +import java.util.EnumSet; +import java.util.List; +import java.util.Set; + +/** + * Runs the OpenFeature Provider TCK against the OFREP provider. + * + *

OFREP is a protocol rather than a vendor, so the backend under test is simply something that + * speaks it. flagd does, on port {@value #OFREP_PORT}, which means this suite reuses the flagd + * testbed image and its launchpad control API unchanged — see + * {@code src/test/resources/tck/docker-compose.yaml}. + */ +public class OfrepTckTest extends AbstractProviderTckTest { + + /** The container-internal port flagd serves the OFREP HTTP API on. */ + private static final int OFREP_PORT = 8016; + + /** + * A port nothing listens on, for the initialisation-failure scenarios. + * + *

Deliberately not a port on the Compose stack: the stack must stay up for the whole suite, + * and simulated outages belong to the control API. + */ + private static final int UNAVAILABLE_PORT = 9999; + + @Override + public File composeFile() { + return new File("src/test/resources/tck/docker-compose.yaml"); + } + + @Override + public List backendPorts() { + return Collections.singletonList(OFREP_PORT); + } + + @Override + public FeatureProvider createProvider(BackendEndpoint endpoint) { + return OfrepProvider.constructProvider(OfrepProviderOptions.builder() + .baseUrl("http://" + endpoint.host() + ":" + endpoint.port(OFREP_PORT)) + .build()); + } + + /** + * {@inheritDoc} + * + *

Short timeouts on purpose: the {@code @unavailable} scenarios assert that failure is + * reported promptly. They are skipped for this provider — see + * {@link #capabilities()} — but the deadlines stay correct so that the scenarios start passing + * on their own the day the provider grows an {@code initialize()}. + */ + @Override + public FeatureProvider createUnavailableProvider() { + return OfrepProvider.constructProvider(OfrepProviderOptions.builder() + .baseUrl("http://localhost:" + UNAVAILABLE_PORT) + .connectTimeout(Duration.ofMillis(500)) + .requestTimeout(Duration.ofMillis(500)) + .build()); + } + + /** + * {@inheritDoc} + * + *

Four capabilities are withheld, and all four come from the same root cause: {@code + * OfrepProvider} is a bare {@link dev.openfeature.sdk.FeatureProvider} (OfrepProvider.java:19) + * with no lifecycle of its own. It holds no state, opens no stream, runs no poll loop and does + * not override {@code initialize()} — every evaluation is a fresh, independent HTTP POST + * (Resolver.java:54-97, OfrepApi.java:93-118). There is nothing in it that could observe a + * backend transition, let alone report one. + * + *

+ * + *

{@link Capability#OBJECT} and {@link Capability#STRICT_NUMERIC_TYPING} are both declared, + * and the second is worth spelling out because the flagd provider cannot declare it. + * Deserialisation goes through a plain Jackson {@code ObjectMapper} into an untyped + * {@code Object value} (OfrepResponse.java:16, OfrepApi.java:109), which maps a JSON integer to + * {@link Integer} and a JSON fraction to {@link Double}. {@code handleResolved} then admits the + * value only on an exact {@code type.isInstance(responseValue)} check and otherwise returns + * {@code TYPE_MISMATCH} with the code default (Resolver.java:183-190). Nothing anywhere widens + * or narrows between the two numeric types, so {@code float-flag} (0.5) requested as an integer + * is rejected rather than truncated to {@code 0}. The same exact-instance check is what makes + * the {@code @object} mismatch matrix work; the structured happy path passes through + * {@code resolve(Object.class, ...)}, which every non-null value satisfies, and is converted + * with {@code Value.objectToValue} (Resolver.java:125-136). + */ + @Override + public Set capabilities() { + return EnumSet.complementOf(EnumSet.of( + Capability.EVENTS, Capability.STALE, Capability.CONFIGURATION_CHANGE, Capability.UNAVAILABLE_INIT)); + } +} diff --git a/providers/ofrep/src/test/resources/tck/docker-compose.yaml b/providers/ofrep/src/test/resources/tck/docker-compose.yaml new file mode 100644 index 000000000..93aa4cf80 --- /dev/null +++ b/providers/ofrep/src/test/resources/tck/docker-compose.yaml @@ -0,0 +1,17 @@ +# Backend stack for the OpenFeature Provider TCK, wrapping the unmodified flagd testbed image. +# +# OFREP is a vendor-neutral protocol, so the TCK does not need an OFREP-specific testbed: any +# backend that speaks OFREP and exposes the control API will do. flagd serves the OFREP HTTP API +# on 8016 alongside its own gRPC surfaces, and the testbed image already ships the "launchpad" +# control API on 8080 — the same stack the flagd provider's TCK suite uses, seeded with the same +# canonical flag set. No new image, no new control API. +# +# Note there are no host port bindings. The TCK requires dynamically mapped ports and discovers +# them after startup — a pinned host port would make the suite unrunnable in parallel and would +# collide with a developer's local flagd. +services: + backend: + image: ghcr.io/open-feature/flagd-testbed:v3.8.0 + ports: + - 8016 # flagd OFREP evaluation (HTTP) + - 8080 # launchpad control API