use std::fs;
use std::io::{Read, Write};
use std::net::TcpListener;
use std::path::PathBuf;
use std::process::{Command, Stdio};
use std::sync::mpsc;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
fn bin() -> &'static str {
env!("CARGO_BIN_EXE_supercode")
}
fn temp_home(label: &str) -> PathBuf {
let nonce = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_nanos();
let home = std::env::temp_dir().join(format!(
"supercode-claude-runtime-resume-{label}-{}-{nonce}",
std::process::id(),
));
fs::create_dir_all(&home).unwrap();
home
}
fn write_overdue_wakeup(path: &PathBuf) {
let lines = [
serde_json::json!({
"type": "user",
"sessionId": "runtime-resume",
"cwd": "/tmp",
"timestamp": "2020-01-01T00:00:00Z",
"message": {"role": "user", "content": "initial context"}
}),
serde_json::json!({
"type": "assistant",
"timestamp": "2020-01-01T00:00:01Z",
"message": {"role": "assistant", "content": [{
"type": "tool_use",
"id": "wake-overdue",
"name": "ScheduleWakeup",
"input": {
"delaySeconds": 1,
"reason": "acceptance wakeup",
"prompt": "RUNTIME_WAKEUP_MARKER"
}
}]}
}),
serde_json::json!({
"type": "user",
"timestamp": "2020-01-01T00:00:02Z",
"message": {"role": "user", "content": [{
"type": "tool_result",
"tool_use_id": "wake-overdue",
"content": "Next wakeup scheduled for 00:00:02 (in 1s)."
}]}
}),
];
let text = lines
.iter()
.map(serde_json::Value::to_string)
.collect::<Vec<_>>()
.join("\n")
+ "\n";
fs::write(path, text).unwrap();
}
fn write_pending_queue(path: &PathBuf) {
let lines = [
serde_json::json!({
"type": "user",
"sessionId": "runtime-queue-resume",
"cwd": "/tmp",
"timestamp": "2020-01-01T00:00:00Z",
"message": {"role": "user", "content": "initial context"}
}),
serde_json::json!({
"type": "queue-operation",
"operation": "enqueue",
"timestamp": "2020-01-01T00:00:01Z",
"content": "RUNTIME_QUEUE_MARKER"
}),
];
let text = lines
.iter()
.map(serde_json::Value::to_string)
.collect::<Vec<_>>()
.join("\n")
+ "\n";
fs::write(path, text).unwrap();
}
fn spawn_sse_stub() -> (
std::net::SocketAddr,
mpsc::Receiver<String>,
std::thread::JoinHandle<()>,
) {
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
let addr = listener.local_addr().unwrap();
let (tx, rx) = mpsc::channel();
let handle = std::thread::spawn(move || {
let (mut socket, _) = listener.accept().unwrap();
socket
.set_read_timeout(Some(Duration::from_secs(10)))
.unwrap();
let mut request = Vec::new();
let mut buf = [0_u8; 4096];
loop {
let read = socket.read(&mut buf).unwrap();
if read == 0 {
break;
}
request.extend_from_slice(&buf[..read]);
let Some(header_end) = request.windows(4).position(|w| w == b"\r\n\r\n") else {
continue;
};
let headers = String::from_utf8_lossy(&request[..header_end]);
let content_len = headers
.lines()
.find_map(|line| {
let (name, value) = line.split_once(':')?;
name.eq_ignore_ascii_case("content-length")
.then(|| value.trim().parse::<usize>().ok())
.flatten()
})
.unwrap_or(0);
if request.len() >= header_end + 4 + content_len {
break;
}
}
tx.send(String::from_utf8_lossy(&request).into_owned())
.unwrap();
let sse = "data: {\"choices\":[{\"delta\":{\"content\":\"scheduled done\"}}]}\n\n\
data: [DONE]\n\n";
let response = format!(
"HTTP/1.1 200 OK\r\nContent-Type: text/event-stream\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{sse}",
sse.len()
);
socket.write_all(response.as_bytes()).unwrap();
});
(addr, rx, handle)
}
#[test]
fn active_resume_delivers_overdue_wakeup_while_idle_and_keeps_source_unchanged() {
let home = temp_home("wakeup");
let source = home.join("session.jsonl");
write_overdue_wakeup(&source);
let before = fs::read(&source).unwrap();
let (addr, request_rx, server) = spawn_sse_stub();
let mut child = Command::new(bin())
.env("HOME", &home)
.env("SUPERCODE_HOME", home.join("supercode-home"))
.env_remove("OPENROUTER_API_KEY")
.args([
"--quiet",
"--api-key",
"x",
"--base-url",
&format!("http://{addr}"),
"--max-iterations",
"1",
"resume",
source.to_str().unwrap(),
])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.unwrap();
let stdin = child.stdin.take().unwrap();
let request = request_rx
.recv_timeout(Duration::from_secs(20))
.expect("scheduler should issue a provider request while stdin remains idle");
assert!(request.contains("RUNTIME_WAKEUP_MARKER"), "{request}");
drop(stdin);
let output = child.wait_with_output().unwrap();
assert!(
output.status.success(),
"stdout={} stderr={}",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
);
assert!(String::from_utf8_lossy(&output.stdout).contains("scheduled done"));
assert_eq!(fs::read(&source).unwrap(), before);
let sessions_dir = home.join("supercode-home").join("sessions");
let files = fs::read_dir(&sessions_dir)
.unwrap()
.map(|entry| entry.unwrap().path())
.collect::<Vec<_>>();
let sidecar = files
.iter()
.find(|path| path.to_string_lossy().ends_with(".sidecar.jsonl"))
.expect("unreduced imported resume should own a lossless sidecar");
let transcript = files
.iter()
.find(|path| {
path.extension().and_then(|ext| ext.to_str()) == Some("jsonl")
&& !path.to_string_lossy().ends_with(".sidecar.jsonl")
})
.expect("unreduced imported resume should persist a working transcript");
let runtime = files
.iter()
.find(|path| path.to_string_lossy().ends_with(".claude-runtime.json"))
.expect("unreduced imported resume should persist Claude runtime state");
let metadata = files
.iter()
.find(|path| path.to_string_lossy().ends_with(".meta.json"))
.expect("unreduced imported resume should persist session metadata");
assert!(fs::read_to_string(sidecar)
.unwrap()
.contains("scheduled done"));
assert!(fs::read_to_string(transcript)
.unwrap()
.contains("scheduled done"));
let runtime: serde_json::Value =
serde_json::from_str(&fs::read_to_string(runtime).unwrap()).unwrap();
let metadata: serde_json::Value =
serde_json::from_str(&fs::read_to_string(metadata).unwrap()).unwrap();
assert_eq!(
metadata.get("reduced").and_then(serde_json::Value::as_bool),
Some(false)
);
assert_eq!(
runtime
.pointer("/scheduler/deliveries")
.and_then(serde_json::Value::as_array)
.map(Vec::len),
Some(0),
"successful scheduled turn should durably acknowledge its delivery"
);
server.join().unwrap();
fs::remove_dir_all(home).ok();
}
#[test]
fn queue_only_active_resume_warns_and_delivers_while_idle() {
let home = temp_home("queue");
let source = home.join("queue-session.jsonl");
write_pending_queue(&source);
let before = fs::read(&source).unwrap();
let (addr, request_rx, server) = spawn_sse_stub();
let mut child = Command::new(bin())
.env("HOME", &home)
.env("SUPERCODE_HOME", home.join("supercode-home"))
.env_remove("OPENROUTER_API_KEY")
.args([
"--api-key",
"x",
"--base-url",
&format!("http://{addr}"),
"--max-iterations",
"1",
"resume",
source.to_str().unwrap(),
])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.unwrap();
let stdin = child.stdin.take().unwrap();
let request = request_rx
.recv_timeout(Duration::from_secs(20))
.expect("pending queue should issue a provider request while stdin remains idle");
assert!(request.contains("RUNTIME_QUEUE_MARKER"), "{request}");
drop(stdin);
let output = child.wait_with_output().unwrap();
assert!(output.status.success(), "{:?}", output.status);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(stderr.contains("same scheduled or queued work"), "{stderr}");
assert!(stderr.contains("--paused"), "{stderr}");
assert_eq!(fs::read(&source).unwrap(), before);
server.join().unwrap();
fs::remove_dir_all(home).ok();
}