//! Regression tests for the SMOKE-TEST report findings: `cellctl diff` and
//! `cellctl version` MUST exit non-zero when the cellos-server is unreachable.
//!
//! Before this fix:
//! - `diff` matched on `ErrorKind::Api` (which covers both "HTTP 4xx/5xx" AND
//!   "network call failed") and printed `(formation/... not found on server —
//!   would create)` followed by a plus-line dump of the local spec, returning
//!   `Ok(())`. Adopters wiring this into CI got a false-clean signal.
//! - `version` swallowed any server error into a stderr log line and returned
//!   `Ok(())`, so scripts saw exit 0 even when no server was contacted.
//!
//! These tests bind `Config { server_url: Some("http://127.0.0.1:1"), .. }`
//! (port 1 is reserved and never listened on) and assert that both subcommands
//! return an `Err(CtlError { kind: ErrorKind::Api, .. })`. Port 1 was chosen
//! over a random ephemeral port because it produces an immediate
//! `ConnectionRefused`/`AddrNotAvailable` on every platform without needing
//! to allocate-then-close a listener (which would race with the connect).
//!
//! See `/tmp/smoke-test-report.md` for the smoke-test transcript that surfaced
//! these issues against the published v0.5.0 binary.

use std::io::Write;

use cellos_ctl::client::CellosClient;
use cellos_ctl::cmd;
use cellos_ctl::config::Config;
use cellos_ctl::exit::ErrorKind;

/// Build a client pointed at a guaranteed-unreachable loopback port.
fn unreachable_client() -> CellosClient {
    let cfg = Config {
        // Port 1 is a reserved low port; nothing legitimate listens here and a
        // userspace connect() returns immediately with refused/permission-denied.
        server_url: Some("http://127.0.0.1:1".to_string()),
        api_token: None,
    };
    CellosClient::new(&cfg).expect("client build")
}

#[tokio::test]
async fn diff_exits_nonzero_when_server_unreachable() {
    // Write a minimal valid formation YAML to a tempfile. The point is not
    // schema validation — we want `diff` to get past the local-file read and
    // into the HTTP GET, which must then fail transport-side.
    let dir = std::env::temp_dir().join(format!("cellctl-diff-smoke-{}", std::process::id()));
    std::fs::create_dir_all(&dir).expect("mkdir tempdir");
    let path = dir.join("formation.yaml");
    let mut f = std::fs::File::create(&path).expect("create temp file");
    f.write_all(
        b"kind: Formation\n\
          name: smoke-diff\n\
          spec:\n  replicas: 1\n",
    )
    .expect("write temp file");

    let client = unreachable_client();
    let res = cmd::diff::run(&client, &path).await;

    let _ = std::fs::remove_file(&path);
    let _ = std::fs::remove_dir(&dir);

    let err = res.expect_err(
        "diff against an unreachable server MUST return Err — the historical \
         `would create` short-circuit was a false success (SMOKE-TEST Finding #1)",
    );
    assert_eq!(
        err.kind,
        ErrorKind::Api,
        "transport failure must map to ErrorKind::Api (exit code 64, Exit::ToolSpecific), got {err:?}",
    );
    assert!(
        err.status.is_none(),
        "a transport-level failure has no HTTP status — only real 4xx/5xx responses do; got status={:?}",
        err.status,
    );
}

#[tokio::test]
async fn version_exits_nonzero_when_server_unreachable() {
    let client = unreachable_client();
    let res = cmd::version::run(&client).await;
    let err = res.expect_err(
        "version against an unreachable server MUST return Err — kubectl exits \
         non-zero in this case and so must cellctl (SMOKE-TEST Finding #2)",
    );
    assert_eq!(
        err.kind,
        ErrorKind::Api,
        "transport failure must map to ErrorKind::Api (exit code 64, Exit::ToolSpecific), got {err:?}",
    );
    assert!(
        err.message.contains("unable to connect to the server"),
        "version error message should be operator-friendly; got: {}",
        err.message,
    );
}
