//! FC-70: pin the per-backend secret-injection model documented in
//! `docs/secret-delivery-per-backend.md`.
//!
//! Pure structural test — no env reads, no I/O, no backend plumbing
//! (D11: cellos-core/src/ MUST NOT read env vars; this is the test crate
//! and follows the same rule for parity).
//!
//! Two pieces:
//!
//! 1. `documents_default_secret_delivery_is_env` — a positive structural
//!    assertion that today's `SecretDeliveryMode::default()` is `Env`. This
//!    is the baseline A3-01 will flip to `RuntimeLeasedBroker`. When that
//!    flip lands, this test is updated in lockstep with the doc.
//!
//! 2. `firecracker_secret_refs_with_env_delivery_is_documented_failure` —
//!    the FC-25-style negative experiment FC-70 calls for. The FC backend
//!    does NOT materialise `secretRefs` into the guest today (host-side
//!    only). The hook for asserting that a spec like
//!    `{ secretRefs: ["FOO"], secretDelivery: Env, backend: firecracker }`
//!    fails closed lives in supervisor + host-firecracker, neither of
//!    which is on cellos-core's dependency surface. Until either:
//!
//!      (a) `cellos-core` exposes a `validate_secret_refs_against_allowlist`
//!          (or similarly-named) helper that knows about per-backend
//!          delivery semantics, or
//!      (b) the FC backend rejects `secretRefs + Env` at `start_cell`
//!          time,
//!
//!    the negative path is documentation-only. This test pins that gap
//!    explicitly with `#[ignore]` + `panic!`, mirroring the FC-25 corpus
//!    posture (`Plans/firecracker-release-readiness.md`).

use cellos_core::types::{
    AuthorityBundle, ExecutionCellDocument, ExecutionCellSpec, Lifetime, RunSpec,
    SecretDeliveryMode,
};

/// Builds a minimal spec with `secretRefs` populated and an explicit
/// `secret_delivery` mode. Pure constructor — no validation invoked.
fn spec_with_secret_refs(mode: SecretDeliveryMode) -> ExecutionCellDocument {
    ExecutionCellDocument {
        api_version: "cellos.io/v1".into(),
        kind: "ExecutionCell".into(),
        spec: ExecutionCellSpec {
            id: "fc70-test-cell".into(),
            correlation: None,
            identity: None,
            policy: None,
            placement: None,
            ingress: None,
            environment: None,
            run: Some(RunSpec {
                argv: vec!["/bin/true".into()],
                working_directory: None,
                timeout_ms: None,
                limits: None,
                secret_delivery: mode,
            }),
            authority: AuthorityBundle {
                filesystem: None,
                network: None,
                egress_rules: None,
                secret_refs: Some(vec!["FOO".into()]),
                authority_derivation: None,
                dns_authority: None,
                cdn_authority: None,
            },
            lifetime: Lifetime { ttl_seconds: 60 },
            export: None,
            telemetry: None,
        },
    }
}

/// A3-01 lockstep pin: post-A3-01 the default delivery mode is
/// `RuntimeLeasedBroker`. The test name is kept for grep continuity with
/// the pre-flip doc table; the assertion now reflects the post-flip
/// contract. When the next default flip lands, update assertion and
/// per-backend doc table together.
#[test]
fn documents_default_secret_delivery_is_env() {
    let mode = SecretDeliveryMode::default();
    assert_eq!(
        mode,
        SecretDeliveryMode::RuntimeLeasedBroker,
        "default SecretDeliveryMode is RuntimeLeasedBroker post-A3-01; \
         doc and test must be updated together"
    );
}

/// Positive structural pin: a spec built with `secret_delivery: Env` and a
/// non-empty `authority.secret_refs` is shape-valid (the contract layer
/// does not reject this combination). This is exactly the misconfiguration
/// FC-70 says must fail-closed at the FC backend boundary — the test
/// captures the *current* shape so a future tightening of the contract or
/// the FC backend invalidates this assertion deliberately, not silently.
#[test]
fn env_mode_with_secret_refs_is_structurally_allowed_today() {
    let doc = spec_with_secret_refs(SecretDeliveryMode::Env);
    let run = doc.spec.run.as_ref().expect("run set by builder");
    assert_eq!(run.secret_delivery, SecretDeliveryMode::Env);
    let refs = doc
        .spec
        .authority
        .secret_refs
        .as_ref()
        .expect("secret_refs set by builder");
    assert_eq!(refs, &vec!["FOO".to_string()]);
    // No core-level rejection exists for this combination today; the
    // failure is supposed to land at the FC backend boundary. See the
    // ignored test below for the gap.
}

/// FC-70 negative experiment.
///
/// Documented behaviour: under `host-firecracker`, a spec with
/// `secret_refs` non-empty and `secret_delivery: Env` MUST fail-closed
/// with an error string identifying both "firecracker" and "secret"
/// (or be rejected at policy time via `requireRuntimeSecretDelivery`).
///
/// Hook does not exist on the core surface today
/// (`validate_secret_refs_against_allowlist` is not implemented; the FC
/// backend does not reject at `start_cell`). Marking the gap explicitly
/// per FC-70 acceptance ("the answer is testable per FC-25-style negative
/// experiment").
#[test]
#[ignore = "FC-70 GAP: secret-injection negative-test path not yet wired (see docs/secret-delivery-per-backend.md)"]
fn firecracker_secret_refs_with_env_delivery_is_documented_failure() {
    // Build the misconfigured spec the doc warns against.
    let _doc = spec_with_secret_refs(SecretDeliveryMode::Env);

    // When the gap closes, replace this panic with the actual assertion:
    //
    //   match cellos_core::validate_secret_refs_against_allowlist(
    //       &_doc,
    //       BackendKind::Firecracker,
    //   ) {
    //       Err(CellosError::InvalidSpec(msg)) => {
    //           assert!(msg.contains("firecracker"));
    //           assert!(msg.contains("secret"));
    //       }
    //       other => panic!("expected InvalidSpec, got {other:?}"),
    //   }
    //
    // Or, if the FC backend rejects at `start_cell` instead, the
    // assertion lives in `crates/cellos-host-firecracker/tests/`
    // and this test is removed in favour of that one.

    panic!(
        "FC-70 GAP: secret-injection negative-test path not yet wired. \
         See docs/secret-delivery-per-backend.md for the documented \
         posture; either add `validate_secret_refs_against_allowlist` to \
         cellos-core or have host-firecracker::start_cell reject \
         `secretRefs + Env` at the backend boundary."
    );
}
