//! The front door as a person or an agent uses it, through the binary: a //! project made with `new`, its own `test.sh` run as made, `setup-agent` run //! twice, `mcp config` and `mcp install`, the docs, and the stdio server's //! resources. #![cfg(unix)] use serde_json::Value; use std::path::{Path, PathBuf}; use std::process::{Command, Output}; const BIN: &str = env!("CARGO_BIN_EXE_wai-quantum"); fn scratch(name: &str) -> PathBuf { let d = std::env::temp_dir().join(format!("wai-quantum-front-door-{name}-{}", std::process::id())); let _ = std::fs::remove_dir_all(&d); std::fs::create_dir_all(&d).unwrap(); d } /// The binary with `args`, its key kept in `home`. fn run(home: &Path, args: &[&str]) -> Output { Command::new(BIN).args(args).env("XDG_CONFIG_HOME", home.join("config")).env_remove("WAI_QUANTUM_KEY").output().unwrap() } fn text(o: &Output) -> String { format!("{}{}", String::from_utf8_lossy(&o.stdout), String::from_utf8_lossy(&o.stderr)) } #[test] fn every_template_s_test_passes_as_made() { for t in ["bell", "ghz", "grover", "noisy-bell"] { let home = scratch(t); let dir = home.join("project"); let made = run(&home, &["new", dir.to_str().unwrap(), "--template", t]); assert!(made.status.success(), "{t}: {}", text(&made)); // The scaffolded test, as a person runs it. let test = Command::new("sh").arg(dir.join("test.sh")).env("WAI_QUANTUM", BIN).env("XDG_CONFIG_HOME", home.join("config")).env_remove("WAI_QUANTUM_KEY").output().unwrap(); assert!(test.status.success(), "{t}: {}", text(&test)); assert!(text(&test).contains("both receipts verify"), "{t}: {}", text(&test)); // The check's record says each intent held, and how. let record: Value = serde_json::from_str(&std::fs::read_to_string(dir.join("receipts").join(format!("{t}.intent.json"))).unwrap()).unwrap(); assert_eq!(record["result"]["holds"], true, "{t}: {record}"); let v = run(&home, &["verify", dir.join("receipts").join(format!("{t}.intent.json")).to_str().unwrap()]); assert!(v.status.success() && text(&v).contains("signature VALID"), "{t}: {}", text(&v)); // The CI script finds the command line it is given and runs the same test. let ci = Command::new("sh").arg(dir.join("ci/check.sh")).env("WAI_QUANTUM", BIN).env("XDG_CONFIG_HOME", home.join("config")).output().unwrap(); assert!(ci.status.success(), "{t}: {}", text(&ci)); // The project's MCP entry is the one `mcp config` prints. let entry: Value = serde_json::from_str(&std::fs::read_to_string(dir.join("mcp.json")).unwrap()).unwrap(); let printed: Value = serde_json::from_slice(&run(&home, &["mcp", "config"]).stdout).unwrap(); assert_eq!(entry, printed); let _ = std::fs::remove_dir_all(&home); } } #[test] fn a_test_that_fails_exits_1_and_says_why() { let home = scratch("fails"); let dir = home.join("p"); assert!(run(&home, &["new", dir.to_str().unwrap()]).status.success()); std::fs::write(dir.join("bell.qasm"), "OPENQASM 3.0;\nqubit[2] q;\nbit[2] c;\nh q[0];\nc = measure q;\n").unwrap(); let o = run(&home, &["test", dir.to_str().unwrap()]); assert_eq!(o.status.code(), Some(1), "{}", text(&o)); assert!(text(&o).contains("fails") && text(&o).contains("FAILED"), "{}", text(&o)); let _ = std::fs::remove_dir_all(&home); } #[test] fn setup_agent_twice_changes_the_file_once() { let home = scratch("agents"); std::fs::write(home.join("AGENTS.md"), "# This repository\n\nRun the tests before you push.\n").unwrap(); let first = run(&home, &["setup-agent", home.to_str().unwrap()]); assert!(first.status.success() && text(&first).contains("added"), "{}", text(&first)); let once = std::fs::read_to_string(home.join("AGENTS.md")).unwrap(); assert!(once.starts_with("# This repository\n\nRun the tests before you push.\n\n"), "{once}"); let second = run(&home, &["setup-agent", home.to_str().unwrap()]); assert!(second.status.success() && text(&second).contains("unchanged"), "{}", text(&second)); assert_eq!(std::fs::read_to_string(home.join("AGENTS.md")).unwrap(), once); assert!(run(&home, &["setup-agent", home.to_str().unwrap(), "--check"]).status.success()); // The block is the one `docs agents` prints. let printed = String::from_utf8(run(&home, &["docs", "agents"]).stdout).unwrap(); assert!(once.ends_with(&printed), "{printed}"); let _ = std::fs::remove_dir_all(&home); } #[test] fn mcp_install_merges_into_the_file_named() { let home = scratch("install"); let file = home.join("client.json"); std::fs::write(&file, "{\"mcpServers\": {\"other\": {\"command\": \"x\"}}}").unwrap(); let o = run(&home, &["mcp", "install", "--file", file.to_str().unwrap()]); assert!(o.status.success(), "{}", text(&o)); let v: Value = serde_json::from_str(&std::fs::read_to_string(&file).unwrap()).unwrap(); assert_eq!(v["mcpServers"]["other"]["command"], "x"); assert_eq!(v["mcpServers"]["wai-quantum"]["args"][0], "mcp"); let refused = run(&home, &["mcp", "install", "--file", file.to_str().unwrap(), "--memory-gb", "4"]); assert!(!refused.status.success() && text(&refused).contains("--force"), "{}", text(&refused)); let forced = run(&home, &["mcp", "install", "--file", file.to_str().unwrap(), "--memory-gb", "4", "--force"]); assert!(forced.status.success(), "{}", text(&forced)); let v: Value = serde_json::from_str(&std::fs::read_to_string(&file).unwrap()).unwrap(); assert_eq!(v["mcpServers"]["wai-quantum"]["args"], serde_json::json!(["mcp", "--memory-gb", "4"])); let _ = std::fs::remove_dir_all(&home); } #[test] fn the_docs_and_the_stdio_server_s_resources_agree() { let home = scratch("docs"); let language = String::from_utf8(run(&home, &["docs", "language"]).stdout).unwrap(); assert!(language.starts_with("# The OpenQASM the engine reads"), "{language}"); let out = home.join("out"); assert!(run(&home, &["docs", "llms", "--out", out.to_str().unwrap()]).status.success()); assert!(out.join("llms.txt").is_file() && out.join("llms-full.txt").is_file()); assert!(run(&home, &["docs", "skill", "--out", out.to_str().unwrap()]).status.success()); assert!(out.join("wai-quantum/SKILL.md").is_file() && out.join("wai-quantum/reference/tools.md").is_file()); // The same text over MCP. let mut child = Command::new(BIN).args(["mcp"]).env("XDG_CONFIG_HOME", home.join("config")).stdin(std::process::Stdio::piped()).stdout(std::process::Stdio::piped()).stderr(std::process::Stdio::null()).spawn().unwrap(); { use std::io::Write; let mut stdin = child.stdin.take().unwrap(); writeln!(stdin, r#"{{"jsonrpc":"2.0","id":1,"method":"resources/read","params":{{"uri":"wai-quantum://docs/language"}}}}"#).unwrap(); } let o = child.wait_with_output().unwrap(); let reply: Value = serde_json::from_slice(&o.stdout).unwrap(); assert_eq!(reply["result"]["contents"][0]["text"].as_str(), Some(language.as_str())); let _ = std::fs::remove_dir_all(&home); }