Files
buzz/crates/buzz-feature-flags/tests/relay_feature_selection_contract.rs
Luke Tornquist 8f6b66f9ff Select one feature flag provider at compile time (#7677)
## Why

Buzz server components need one typed feature-flag contract without
forcing the open-source build to depend on LaunchDarkly. A staging or
production relay must not be able to choose the wrong provider through
runtime configuration.

## What

- Adds typed boolean and signed `i64` flags with static, environment,
and compile-gated LaunchDarkly evaluators.
- Defines an executable relay artifact contract that compiles exactly
one provider.
- Rejects zero or multiple relay provider features at compile time.
- Runs the LaunchDarkly feature tests and the provider compile matrix in
the unit CI lane and fallback test script.

## How

`buzz-feature-flags` stays provider-neutral. The future relay
composition root will expose one build path per artifact feature:
`static-feature-flags`, `environment-feature-flags`, or
`launchdarkly-feature-flags`. Runtime configuration carries only
settings for the selected provider. It never carries a provider name,
fallback, or precedence rule.

The crate still has no relay consumer. A locked compile fixture proves
each singleton builds and every zero or multi-provider combination fails
until the relay adopts this contract.

This removes the proposed runtime `ProviderMode` and leaves one
provider, one construction path, and one evaluator per artifact.

## Risk

This PR still has no relay or app consumer, so it changes no
user-visible behavior. Risk is limited to the new feature-flag crate,
its compile contract, and its unit-test lane.

## Testing

An external Rust consumer at `30cf968bfe85e82980fda7e63f5bebe649ca939f`
exercised six environment-evaluator scenarios and 59 assertions. The
compile-time correction adds no live consumer to exercise manually.

## Bigger picture

When `buzz-relay` adopts this crate, its build manifests must map each
distribution to one provider feature. That consumer PR must verify live
Relay Proxy reachability, secret ingestion, lifecycle ownership, and
user-visible behavior.

Generated with Codex

---------

Signed-off-by: tornquist <tornquist@squareup.com>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-24 15:31:08 -04:00

230 lines
7.8 KiB
Rust

use std::{
env,
ffi::OsString,
path::{Path, PathBuf},
process::Command,
};
use tempfile::TempDir;
const FIXTURE_LOCK_REGEN_RECIPE: &str =
"cargo generate-lockfile --manifest-path crates/buzz-feature-flags/tests/fixtures/relay-feature-selection/Cargo.toml";
fn fixture_manifest() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/relay-feature-selection/Cargo.toml")
}
fn fixture_lockfile() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/relay-feature-selection/Cargo.lock")
}
fn crate_readme() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR")).join("README.md")
}
fn cargo_bin() -> OsString {
env::var_os("CARGO").unwrap_or_else(|| OsString::from("cargo"))
}
fn cargo_check(target_dir: &Path, case_name: &str, features: &[&str]) -> std::process::Output {
let mut command = Command::new(cargo_bin());
command
.current_dir(env!("CARGO_MANIFEST_DIR"))
.arg("check")
.arg("--quiet")
.arg("--locked")
.arg("--manifest-path")
.arg(fixture_manifest())
.arg("--no-default-features")
.env("CARGO_TARGET_DIR", target_dir);
if !features.is_empty() {
command.arg("--features").arg(features.join(","));
}
command
.output()
.unwrap_or_else(|error| {
panic!(
"failed to run cargo check for {case_name}: {error}\nfixture lock regeneration recipe: `{FIXTURE_LOCK_REGEN_RECIPE}`"
)
})
}
fn format_valid_case_failed_status(case_name: &str, stdout: &str, stderr: &str) -> String {
format!(
"expected {case_name} to compile successfully\nstdout:\n{stdout}\nstderr:\n{stderr}\nregenerate fixture lock with `{FIXTURE_LOCK_REGEN_RECIPE}`"
)
}
fn format_invalid_case_missing_exact_one(case_name: &str, stderr: &str) -> String {
format!(
"expected {case_name} failure to explain the exact-one contract\nstderr:\n{stderr}\nregenerate fixture lock with `{FIXTURE_LOCK_REGEN_RECIPE}`"
)
}
fn assert_valid_case_success(case_name: &str, output: &std::process::Output) {
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
output.status.success(),
"{}",
format_valid_case_failed_status(case_name, &stdout, &stderr)
);
}
fn assert_invalid_case_has_exact_one_diagnostic(case_name: &str, output: &std::process::Output) {
assert!(
!output.status.success(),
"expected {case_name} to fail compilation"
);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
stderr.contains("select exactly one relay feature flag provider feature"),
"{}",
format_invalid_case_missing_exact_one(case_name, &stderr)
);
}
#[test]
fn relay_fixture_lock_regeneration_recipe_is_documented() {
let readme = std::fs::read_to_string(crate_readme()).expect("read crate README");
assert!(
readme.contains(FIXTURE_LOCK_REGEN_RECIPE),
"README.md must document fixture lock regeneration with `{FIXTURE_LOCK_REGEN_RECIPE}`"
);
}
#[test]
fn relay_readme_documents_fail_closed_launchdarkly_startup_contract() {
let readme = std::fs::read_to_string(crate_readme()).expect("read crate README");
assert!(
readme.contains("start_with_default_executor_and_wait"),
"README.md must show LaunchDarkly startup via start_with_default_executor_and_wait"
);
assert!(
readme.contains("async fn build_feature_flag_evaluator"),
"README.md must declare LaunchDarkly build_feature_flag_evaluator as async"
);
assert!(
readme.contains(
"start_with_default_executor_and_wait(Duration::from_secs(5))\n .await"
),
"README.md must await start_with_default_executor_and_wait"
);
assert!(
readme.contains("fail-closed"),
"README.md must state fail-closed startup policy"
);
assert!(
readme.contains("exact-one compile_error guards"),
"README.md must require exact-one compile_error guards in the consumer"
);
assert!(
readme.contains("future flag registry"),
"README.md must defer environment-name uniqueness enforcement to a future registry"
);
assert!(
readme.contains("kind `community` plus")
&& readme.contains("optional global kind `pubkey`"),
"README.md must document LaunchDarkly adapter context kinds"
);
assert!(
readme.contains("rollouts must set `contextKind` to a kind present")
&& readme.contains("in evaluation"),
"README.md must document rollout contextKind requirements"
);
assert!(
readme.contains("omits `contextKind` defaults") && readme.contains("to `user`;"),
"README.md must document LaunchDarkly default rollout context kind"
);
assert!(
readme.contains("bucket as")
&& readme.contains("zero and pick the first positive-weight variation"),
"README.md must document missing-kind rollout bucketing behavior"
);
assert!(
readme.contains("close()` blocks the calling thread")
&& readme.contains("while it flushes analytics"),
"README.md must document blocking LaunchDarkly close semantics"
);
assert!(
readme.contains("offload it to a blocking shutdown path"),
"README.md must document async shutdown offloading guidance for close()"
);
}
#[test]
fn valid_case_failed_status_formatter_includes_fixture_lock_regeneration_recipe() {
let message = format_valid_case_failed_status(
"static",
"",
"error: lock file needs update but --locked was passed",
);
assert!(
message.contains(FIXTURE_LOCK_REGEN_RECIPE),
"valid-case failure formatter must include fixture lock recipe\nmessage:\n{message}"
);
}
#[test]
fn invalid_case_missing_exact_one_formatter_includes_fixture_lock_regeneration_recipe() {
let message =
format_invalid_case_missing_exact_one("none", "error: unrelated nested cargo failure");
assert!(
message.contains(FIXTURE_LOCK_REGEN_RECIPE),
"invalid-case failure formatter must include fixture lock recipe\nmessage:\n{message}"
);
}
#[test]
fn relay_artifact_provider_features_require_exactly_one_selection() {
assert!(
fixture_lockfile().is_file(),
"relay feature selection fixture must check in Cargo.lock for locked nested cargo runs\nregenerate it with `{FIXTURE_LOCK_REGEN_RECIPE}`"
);
let target_dir = TempDir::new().expect("temp target dir");
let valid_cases = [
("static", &["static-feature-flags"][..]),
("environment", &["environment-feature-flags"][..]),
("launchdarkly", &["launchdarkly-feature-flags"][..]),
];
for (case_name, features) in valid_cases {
let output = cargo_check(target_dir.path(), case_name, features);
assert_valid_case_success(case_name, &output);
}
let invalid_cases = [
("none", &[][..]),
(
"static-and-environment",
&["static-feature-flags", "environment-feature-flags"][..],
),
(
"static-and-launchdarkly",
&["static-feature-flags", "launchdarkly-feature-flags"][..],
),
(
"environment-and-launchdarkly",
&["environment-feature-flags", "launchdarkly-feature-flags"][..],
),
(
"all-three",
&[
"static-feature-flags",
"environment-feature-flags",
"launchdarkly-feature-flags",
][..],
),
];
for (case_name, features) in invalid_cases {
let output = cargo_check(target_dir.path(), case_name, features);
assert_invalid_case_has_exact_one_diagnostic(case_name, &output);
}
}