Merge pull request 'test: add Pi spawn path proof integration test' (#6) from feat/pi-spawn-proof into main

This commit is contained in:
clawdie 2026-05-31 16:23:24 +02:00
commit efdb54dfff
2 changed files with 116 additions and 0 deletions

View file

@ -0,0 +1,91 @@
//! Pi spawn path proof — integration test.
//!
//! Validates: Colibri spawns agent → reads JSONL stdout → glasspane ingests → snapshot correct.
//! Uses scripts/fake-pi-agent.py which emits the colibri-pi-events JSONL taxonomy.
//!
//! With real Pi binary (when installed):
//! COLIBRI_PI_BINARY=pi cargo test -p colibri-daemon --test pi_spawn_live -- --nocapture
use std::path::PathBuf;
use std::process::Stdio;
use std::time::SystemTime;
use colibri_glasspane::DEFAULT_STALL_AFTER;
use tokio::io::{AsyncBufReadExt, BufReader};
use tokio::process::Command;
#[tokio::test]
async fn pi_spawn_path_produces_correct_glasspane_state() {
let script = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.parent()
.unwrap()
.parent()
.unwrap()
.join("scripts")
.join("fake-pi-agent.py");
assert!(script.exists(), "fake-pi-agent.py not found at {script:?}");
let mut supervisor = colibri_glasspane::PaneSupervisor::new();
let pane_id = "pi-spawn-proof";
supervisor.attach_pane_at(pane_id, "fake-pi", SystemTime::now());
let mut child = Command::new(PathBuf::from("python3"))
.arg(&script)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.expect("failed to spawn fake-pi-agent.py");
let stdout = child.stdout.take().expect("no stdout");
let mut reader = BufReader::new(stdout).lines();
let mut accepted = 0usize;
loop {
match reader.next_line().await {
Ok(Some(line)) => {
if supervisor
.ingest_line_at(pane_id, &line, SystemTime::now())
.is_some()
{
accepted += 1;
}
}
Ok(None) => break,
Err(e) => {
eprintln!("JSONL read error: {e}");
break;
}
}
}
let status = child.wait().await.expect("child wait failed");
assert!(status.success(), "fake-pi-agent.py exited with {status}");
assert!(
accepted >= 5,
"expected >=5 accepted JSONL lines, got {accepted}"
);
let snapshot = supervisor.snapshot_at("test-host", SystemTime::now(), DEFAULT_STALL_AFTER);
let pane = snapshot
.panes
.iter()
.find(|p| p.id == pane_id)
.expect("pane not found");
assert_eq!(
pane.state,
colibri_glasspane::AgentState::Done,
"expected Done after full agent run"
);
assert!(
pane.pi_session_id.is_some(),
"expected pi_session_id from session event, got {:?}",
pane.pi_session_id
);
eprintln!(
"✓ Pi spawn proof: {} accepted, state={:?}, session={:?}",
accepted, pane.state, pane.pi_session_id
);
}

25
scripts/fake-pi-agent.py Executable file
View file

@ -0,0 +1,25 @@
#!/usr/bin/env python3
"""Fake Pi agent — emits JSONL in the colibri-pi-events format.
Used by Colibri integration tests to validate the spawn JSONL glasspane path.
"""
import sys
import json
import time
messages = [
{"type": "session", "id": f"pi-test-{int(time.time())}", "cwd": "/tmp"},
{"type": "agent_start"},
{"type": "turn_start"},
{"type": "message_start"},
{"type": "message_update", "delta": "Processing task..."},
{"type": "message_end"},
{"type": "turn_end", "stop": "end_turn"},
{"type": "agent_end"},
]
for msg in messages:
sys.stdout.write(json.dumps(msg) + "\n")
sys.stdout.flush()
time.sleep(0.01)
sys.exit(0)