mod pty_harness;
use std::time::Duration;
use pty_harness::PtySession;
const STUB_SESSION_ID: &str = "00000000-0000-0000-0000-000000000001";
fn read_until_contains(
pty: &mut PtySession,
needle: &str,
timeout: Duration,
what: &str,
) -> String {
let buf = pty.read_until(needle, timeout).unwrap_or_default();
if buf.contains(needle) {
buf
} else {
let _ = pty.kill();
panic!("expected {what:?} within {timeout:?}; buffer:\n---\n{buf}\n---");
}
}
fn write_stub_session() {
use oxicode::store::session::{
AgentMessage, ContentValue, FileEntry, SessionEntryBase, SessionEntryEnum, SessionHeader,
SessionMessageEntry,
};
let dir = dirs::home_dir()
.expect("home dir must be resolvable for stub write")
.join(".oxicode")
.join("sessions");
let file = dir.join(format!("{STUB_SESSION_ID}.jsonl"));
if file.exists() {
return;
}
std::fs::create_dir_all(&dir).expect("create sessions dir");
let cwd = dirs::home_dir()
.expect("home dir must be resolvable for stub cwd")
.to_string_lossy()
.to_string();
let header = SessionHeader::new(STUB_SESSION_ID.to_string(), cwd, None);
let entry = SessionEntryEnum::Message(SessionMessageEntry {
base: SessionEntryBase {
entry_type: "message".to_string(),
id: "stub-entry-1".to_string(),
parent_id: None,
timestamp: chrono::Utc::now().to_rfc3339(),
},
message: AgentMessage::User {
content: ContentValue::String("stub prior turn".to_string()),
},
});
let mut s = String::new();
s.push_str(&serde_json::to_string(&FileEntry::Header(header)).unwrap());
s.push('\n');
s.push_str(&serde_json::to_string(&FileEntry::Entry(entry)).unwrap());
s.push('\n');
std::fs::write(&file, s).expect("write stub session");
}
#[test]
fn sessions_direct_resume_does_not_reopen_picker() {
if !pty_harness::oxicode_binary_available() {
eprintln!("oxicode binary not in PATH; skipping (build with `cargo build -p oxicode-cli`)");
return;
}
write_stub_session();
let mut pty = match PtySession::spawn(&["-i"]) {
Ok(p) => p,
Err(e) => {
eprintln!("skipping: failed to spawn oxicode: {e}");
return;
}
};
read_until_contains(
&mut pty,
"\x1b[?2026l",
Duration::from_secs(10),
"TUI synchronized tape frame marker",
);
pty.send_raw(format!("/sessions {STUB_SESSION_ID}").as_bytes())
.expect("send /sessions text without enter");
read_until_contains(
&mut pty,
STUB_SESSION_ID,
Duration::from_secs(10),
"composer render of the /sessions line",
);
pty.send_raw(b"\r").expect("send enter");
read_until_contains(
&mut pty,
"Resum",
Duration::from_secs(10),
"synchronous 'Resuming ...' reply from /sessions",
);
pty.send_line("").expect("failed to send empty enter");
read_until_contains(
&mut pty,
"Resumed session",
Duration::from_secs(10),
"resume worker 'Resumed session ...' line",
);
let buf = pty
.read_until("Select a session", Duration::from_millis(300))
.unwrap_or_default();
if buf.contains("Select a session") {
let _ = pty.kill();
panic!("picker reopened — got: {buf}");
}
let _ = pty.kill();
}