use assert_cmd::cargo::cargo_bin;
use serde_json::Value;
use sylphx_wire_mock::WireMockServer;
use tokio::process::Command;
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn login_token_then_whoami_twice_without_env_token() {
let mock = WireMockServer::start().await;
let base = mock.management_base_url();
let bin = cargo_bin!("sylphx");
let dir = tempfile::tempdir().expect("tmpdir");
for run in 1..=2 {
let cfg = dir.path().join(format!("cfg{run}"));
std::fs::create_dir_all(&cfg).unwrap();
let out = Command::new(&bin)
.env("SYLPHX_CONFIG_DIR", &cfg)
.env_remove("SYLPHX_TOKEN")
.args([
"login",
"--token",
"svc_test",
"--api-url",
&base,
"--json",
])
.output()
.await
.expect("spawn login");
assert!(
out.status.success(),
"login run={run} stderr={}",
String::from_utf8_lossy(&out.stderr)
);
let body: Value = serde_json::from_slice(&out.stdout).expect("login json");
assert_eq!(body["ok"], true, "run={run} body={body}");
assert_eq!(body["source"], "token");
assert!(
body["whoami"]["user"]["id"].as_str().unwrap_or("").len() > 0
|| body["whoami"]["user"].is_object(),
"whoami fields run={run} {body}"
);
let out2 = Command::new(&bin)
.env("SYLPHX_CONFIG_DIR", &cfg)
.env_remove("SYLPHX_TOKEN")
.args(["--api-url", &base, "whoami", "--json"])
.output()
.await
.expect("spawn whoami");
assert!(
out2.status.success(),
"whoami run={run} stderr={}",
String::from_utf8_lossy(&out2.stderr)
);
let who: Value = serde_json::from_slice(&out2.stdout).expect("whoami json");
assert!(
who.get("user").is_some() || who.pointer("/user/id").is_some(),
"whoami primary fields run={run} {who}"
);
let out3 = Command::new(&bin)
.env("SYLPHX_CONFIG_DIR", &cfg)
.env_remove("SYLPHX_TOKEN")
.args(["logout", "--json"])
.output()
.await
.expect("spawn logout");
assert!(out3.status.success());
let lo: Value = serde_json::from_slice(&out3.stdout).unwrap();
assert_eq!(lo["removed"], true);
let out4 = Command::new(&bin)
.env("SYLPHX_CONFIG_DIR", &cfg)
.env_remove("SYLPHX_TOKEN")
.args(["--api-url", &base, "whoami", "--json"])
.output()
.await
.expect("spawn whoami after logout");
assert!(
!out4.status.success(),
"whoami should fail after logout run={run}"
);
eprintln!("OK cli_auth token path run={run}");
}
eprintln!("CLI_AUTH_OK token");
mock.shutdown().await;
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn login_device_flow_twice() {
let mock = WireMockServer::start().await;
let base = mock.management_base_url();
let bin = cargo_bin!("sylphx");
let dir = tempfile::tempdir().expect("tmpdir");
for run in 1..=2 {
let cfg = dir.path().join(format!("dev{run}"));
std::fs::create_dir_all(&cfg).unwrap();
let out = Command::new(&bin)
.env("SYLPHX_CONFIG_DIR", &cfg)
.env_remove("SYLPHX_TOKEN")
.args([
"login",
"--device",
"--auth-url",
&base,
"--api-url",
&base,
"--timeout-secs",
"15",
"--json",
])
.output()
.await
.expect("spawn device login");
assert!(
out.status.success(),
"device login run={run} stdout={} stderr={}",
String::from_utf8_lossy(&out.stdout),
String::from_utf8_lossy(&out.stderr)
);
let body: Value = serde_json::from_slice(&out.stdout).expect("json");
assert_eq!(body["ok"], true);
assert_eq!(body["source"], "device");
assert!(
body["whoami"]["user"].is_object() || body["whoami"].get("user").is_some(),
"device whoami run={run} {body}"
);
let out2 = Command::new(&bin)
.env("SYLPHX_CONFIG_DIR", &cfg)
.env_remove("SYLPHX_TOKEN")
.args(["--api-url", &base, "whoami", "--json"])
.output()
.await
.expect("whoami");
assert!(out2.status.success(), "whoami after device login run={run}");
eprintln!("OK cli_auth device path run={run}");
}
eprintln!("CLI_AUTH_OK device");
mock.shutdown().await;
}