use std::io::{Read, Write};
use std::net::TcpListener;
use std::path::{Path, PathBuf};
use std::process::{Command, Output, Stdio};
use std::time::Duration;
fn bin() -> PathBuf {
PathBuf::from(env!("CARGO_BIN_EXE_supercode"))
}
fn fresh_home(tag: &str) -> PathBuf {
let nanos = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos();
let dir = std::env::temp_dir().join(format!(
"supercode-ux39-tokencounter-{tag}-{}-{nanos}",
std::process::id()
));
std::fs::create_dir_all(&dir).unwrap();
dir
}
const CHUNK_DELAY: Duration = Duration::from_millis(150);
fn spawn_delayed_multi_chunk_sse_stub() -> (std::net::SocketAddr, std::thread::JoinHandle<()>) {
let listener = TcpListener::bind("127.0.0.1:0").expect("bind stub listener");
let addr = listener.local_addr().unwrap();
let handle = std::thread::spawn(move || {
let (mut sock, _) = listener.accept().expect("accept one connection");
sock.set_read_timeout(Some(Duration::from_millis(200)))
.expect("set read timeout");
let mut buf = [0u8; 65536];
loop {
match sock.read(&mut buf) {
Ok(0) => break,
Ok(_) => continue,
Err(e)
if e.kind() == std::io::ErrorKind::WouldBlock
|| e.kind() == std::io::ErrorKind::TimedOut =>
{
break
}
Err(_) => break,
}
}
sock.set_read_timeout(None).expect("clear read timeout");
sock.write_all(
b"HTTP/1.1 200 OK\r\n\
Content-Type: text/event-stream\r\n\
Transfer-Encoding: chunked\r\n\
Connection: close\r\n\r\n",
)
.expect("write stub headers");
let words = [
"This ",
"is ",
"a ",
"long ",
"streamed ",
"reply ",
"made ",
"of ",
"many ",
"small ",
"delayed ",
"chunks ",
"to ",
"stress ",
"the ",
"counter's ",
"own ",
"gating.",
];
for w in words {
std::thread::sleep(CHUNK_DELAY);
let data = format!(r#"data: {{"choices":[{{"delta":{{"content":"{w}"}}}}]}}"#);
let payload = format!("{data}\n\n");
let framed = format!("{:x}\r\n{payload}\r\n", payload.len());
sock.write_all(framed.as_bytes()).expect("write stub chunk");
}
std::thread::sleep(CHUNK_DELAY);
let done = "data: [DONE]\n\n";
let framed = format!("{:x}\r\n{done}\r\n", done.len());
sock.write_all(framed.as_bytes()).expect("write [DONE]");
sock.write_all(b"0\r\n\r\n").expect("write terminal chunk");
sock.flush().ok();
});
(addr, handle)
}
fn run_piped(home: &Path, base_url: &str, extra: &[&str]) -> Output {
let mut args = vec!["--api-key", "x", "--base-url", base_url];
args.extend_from_slice(extra);
Command::new(bin())
.env("SUPERCODE_HOME", home)
.env_remove("OPENROUTER_API_KEY")
.env_remove("OPENAI_API_KEY")
.env_remove("ANTHROPIC_API_KEY")
.env_remove("NO_COLOR")
.env_remove("SUPERCODE_QUIET")
.args(&args)
.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.expect("failed to spawn the supercode binary")
.wait_with_output()
.expect("child process failed")
}
fn contains_status_line_bytes(raw: &[u8]) -> bool {
if raw.contains(&0x1b) {
return true; }
let text = String::from_utf8_lossy(raw);
text.contains("this turn:")
|| text.contains("session:")
|| text.contains("Thinking…")
|| text.contains("Working…")
}
const EXPECTED_REPLY: &str =
"This is a long streamed reply made of many small delayed chunks to stress the counter's own gating.";
fn normalize_whitespace(s: &str) -> String {
s.split_whitespace().collect::<Vec<_>>().join(" ")
}
#[test]
fn piped_multi_chunk_stream_emits_zero_counter_bytes_and_reply_is_intact() {
let (addr, server) = spawn_delayed_multi_chunk_sse_stub();
let home = fresh_home("piped-clean");
let out = run_piped(&home, &format!("http://{addr}"), &["run", "say hi"]);
assert!(
out.status.success(),
"run failed: status={:?} stdout={} stderr={}",
out.status,
String::from_utf8_lossy(&out.stdout),
String::from_utf8_lossy(&out.stderr)
);
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
normalize_whitespace(&stdout).contains(EXPECTED_REPLY),
"expected the fully-reassembled streamed reply on stdout, got: {stdout}"
);
assert!(
!contains_status_line_bytes(&out.stdout),
"stdout must never carry counter/spinner bytes; raw: {:?}",
out.stdout
);
assert!(
!contains_status_line_bytes(&out.stderr),
"piped (non-tty) stderr must carry zero counter/spinner bytes even across many \
separately-delayed chunks; raw: {:?}",
out.stderr
);
server.join().expect("stub server thread panicked");
}
#[test]
fn piped_json_output_format_emits_zero_counter_bytes() {
let (addr, server) = spawn_delayed_multi_chunk_sse_stub();
let home = fresh_home("piped-json-clean");
let out = run_piped(
&home,
&format!("http://{addr}"),
&["run", "--output-format", "json", "say hi"],
);
assert!(
out.status.success(),
"run --output-format json failed: status={:?} stdout={} stderr={}",
out.status,
String::from_utf8_lossy(&out.stdout),
String::from_utf8_lossy(&out.stderr)
);
assert!(
!contains_status_line_bytes(&out.stdout),
"json stdout must never carry counter/spinner bytes; raw: {:?}",
out.stdout
);
assert!(
!contains_status_line_bytes(&out.stderr),
"json-mode stderr must carry zero counter/spinner bytes; raw: {:?}",
out.stderr
);
server.join().expect("stub server thread panicked");
}
#[test]
fn quiet_flag_emits_zero_counter_bytes_even_though_piped_already_would() {
let (addr, server) = spawn_delayed_multi_chunk_sse_stub();
let home = fresh_home("quiet-clean");
let out = run_piped(
&home,
&format!("http://{addr}"),
&["--quiet", "run", "say hi"],
);
assert!(
out.status.success(),
"run --quiet failed: status={:?} stdout={} stderr={}",
out.status,
String::from_utf8_lossy(&out.stdout),
String::from_utf8_lossy(&out.stderr)
);
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
normalize_whitespace(&stdout).contains(EXPECTED_REPLY),
"the reply itself must still stream through under --quiet, got: {stdout}"
);
assert!(
!contains_status_line_bytes(&out.stdout),
"stdout must never carry counter/spinner bytes under --quiet; raw: {:?}",
out.stdout
);
assert!(
!contains_status_line_bytes(&out.stderr),
"--quiet stderr must carry zero counter/spinner bytes; raw: {:?}",
out.stderr
);
server.join().expect("stub server thread panicked");
}