| 1 |
|
| 2 |
|
| 3 |
|
| 4 |
|
| 5 |
mod common; |
| 6 |
|
| 7 |
use common::{add_agent, hermetic, run, setup, text, Serving}; |
| 8 |
use serde_json::{json, Value}; |
| 9 |
use spectra_mcp::client::Client; |
| 10 |
use std::io::{BufRead, BufReader}; |
| 11 |
use std::process::Stdio; |
| 12 |
use std::time::Duration; |
| 13 |
|
| 14 |
fn json_of(d: &std::path::Path, args: &[&str]) -> Value { |
| 15 |
let o = run(d, args); |
| 16 |
assert!(o.status.success(), "{args:?}: {}", text(&o)); |
| 17 |
serde_json::from_slice(&o.stdout) |
| 18 |
.unwrap_or_else(|e| panic!("{args:?} printed more than one JSON document ({e}): {}", String::from_utf8_lossy(&o.stdout))) |
| 19 |
} |
| 20 |
|
| 21 |
#[test] |
| 22 |
fn every_json_output_parses_without_the_daemon() { |
| 23 |
let dir = setup(); |
| 24 |
let d = dir.path(); |
| 25 |
add_agent(d, "claude-oxygen", &[]); |
| 26 |
assert_eq!(json_of(d, &["version", "--json"])["ledger_format"], 1); |
| 27 |
assert_eq!(json_of(d, &["identity", "show", "--json"])["owner"]["name"], "Andy McCall"); |
| 28 |
assert_eq!(json_of(d, &["agent", "list", "--json"])[0]["name"], "claude-oxygen"); |
| 29 |
assert_eq!(json_of(d, &["project", "list", "--json"])[0]["name"], "oxygen"); |
| 30 |
let posted = json_of(d, &["say", "--json", "-c", "general", "the release is tomorrow @claude-oxygen"]); |
| 31 |
assert_eq!(posted["channel"], "#general"); |
| 32 |
assert!(json_of(d, &["inbox", "--json"]).as_array().unwrap().is_empty()); |
| 33 |
assert_eq!(json_of(d, &["search", "--json", "release"]).as_array().unwrap().len(), 1); |
| 34 |
let v = json_of(d, &["admin", "verify", "--json"]); |
| 35 |
assert!(v["findings"].as_array().unwrap().is_empty(), "a CLI post verifies: {v}"); |
| 36 |
} |
| 37 |
|
| 38 |
#[tokio::test] |
| 39 |
async fn a_terminal_chat_with_the_agents() { |
| 40 |
let dir = setup(); |
| 41 |
let d = dir.path(); |
| 42 |
let claude = add_agent(d, "claude-oxygen", &[]); |
| 43 |
let srv = Serving::start(d); |
| 44 |
let a = Client::new(&srv.socket, Some(claude)); |
| 45 |
|
| 46 |
|
| 47 |
assert_eq!(json_of(d, &["say", "--json", "hello from the terminal"])["channel"], "#general"); |
| 48 |
let api: Value = serde_json::from_slice(&run(d, &["api", "GET", "/api/v1/brief"]).stdout).unwrap(); |
| 49 |
assert_eq!(api["actor"], "human"); |
| 50 |
|
| 51 |
let mut follow = hermetic(d).args(["inbox", "--follow", "--json"]).stdout(Stdio::piped()).stderr(Stdio::null()).spawn().unwrap(); |
| 52 |
let mut lines = BufReader::new(follow.stdout.take().unwrap()).lines(); |
| 53 |
tokio::time::sleep(Duration::from_millis(500)).await; |
| 54 |
let say = json!({ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "say", "arguments": { "body": "@andy the build is green", "channel": "general" } } }); |
| 55 |
assert_eq!(a.mcp("2025-11-25", &say).await.unwrap().status, 200); |
| 56 |
let got = tokio::task::spawn_blocking(move || lines.next()).await.unwrap().expect("a line").unwrap(); |
| 57 |
let item: Value = serde_json::from_str(&got).unwrap(); |
| 58 |
assert_eq!(item["from"], "agent:claude-oxygen"); |
| 59 |
assert!(item["summary"].as_str().unwrap().contains("the build is green")); |
| 60 |
follow.kill().ok(); |
| 61 |
follow.wait().ok(); |
| 62 |
srv.stop(); |
| 63 |
} |