open-standards/wai/wai-quantum-cli/tests/front_door.rs
Transaction Science d5d613e5db wai-quantum: the agent front door (new, test, setup-agent, mcp config and install, docs)
- new DIR --template bell|ghz|grover|noisy-bell: the program, intent.json,
  test.sh, ci/check.sh and an example workflow, mcp.json and AGENTS.md;
  its test passes as made.
- test: every intent checked by the engine (an exact distribution by the
  state vector or, under noise, the density matrix; a reference by the
  equivalence ladder; a state from |0...0>), the run and the check both
  sealed with the user's key, the check binding the run's receipt.
- setup-agent: the block between <!-- wai-quantum:start --> and
  <!-- wai-quantum:end --> in AGENTS.md, from the live tool lists and the
  engine's rules; a rerun replaces only the block; --check for CI.
- mcp config / mcp install --file PATH: the server's entry in the common
  mcpServers form, merged keeping every other entry; a different entry is
  refused without --force.
- The stdio server serves the engine's resources and prompts.
- docs: the resources offline, llms.txt and llms-full.txt (committed in
  docs/, held by a test to both servers' tool lists and this help), and
  the Agent Skill in skills/wai-quantum/. server.json for the registry.
- Tests for every verb, an end-to-end test through the binary, and smoke
  checks.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-27 14:41:43 -04:00

126 lines
7 KiB
Rust

//! 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<!-- wai-quantum:start -->"), "{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);
}