use std::path::PathBuf;
use std::process::Command;
use std::{fs, str};
const SECRET: &str = "sk-abcDEF1234567890ghijkl";
const PLANTED: &str = "the quokka census migration plan";
struct World {
base: PathBuf,
home: PathBuf,
cv_home: PathBuf,
}
impl World {
fn new(tag: &str) -> World {
let base = std::env::temp_dir().join(format!(
"cv-share-{tag}-{}-{}",
std::process::id(),
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos()
));
let home = base.join("home");
let cv_home = base.join("cvhome");
fs::create_dir_all(home.join(".claude/projects/-work-proj")).unwrap();
fs::create_dir_all(&cv_home).unwrap();
World { base, home, cv_home }
}
fn cv(&self, args: &[&str]) -> (bool, String, String) {
let out = Command::new(env!("CARGO_BIN_EXE_cv"))
.args(args)
.current_dir(&self.base)
.env("HOME", &self.home)
.env("CLUSTERVISION_HOME", &self.cv_home)
.env("XDG_CACHE_HOME", self.home.join(".cache"))
.env("XDG_CONFIG_HOME", self.home.join(".config"))
.env("XDG_DATA_HOME", self.home.join(".local/share"))
.output()
.expect("cv should run");
(
out.status.success(),
String::from_utf8_lossy(&out.stdout).into_owned(),
String::from_utf8_lossy(&out.stderr).into_owned(),
)
}
fn cv_ok(&self, args: &[&str]) -> (String, String) {
let (ok, out, err) = self.cv(args);
assert!(ok, "cv {args:?} failed\nstdout:\n{out}\nstderr:\n{err}");
(out, err)
}
}
impl Drop for World {
fn drop(&mut self) {
fs::remove_dir_all(&self.base).ok();
}
}
fn share_corpus(w: &World) {
let lines = [
serde_json::json!({"type": "ai-title", "aiTitle": "quokka quest"}),
serde_json::json!({
"type": "user", "uuid": "u1", "sessionId": "s", "timestamp": "2026-02-01T10:00:00Z",
"cwd": "/work/proj",
"message": {"role": "user", "content": PLANTED}
}),
serde_json::json!({
"type": "assistant", "uuid": "a1", "sessionId": "s", "timestamp": "2026-02-01T10:01:00Z",
"cwd": "/work/proj",
"message": {"role": "assistant", "model": "claude-test-1", "content": [
{"type": "thinking", "thinking": "hmm, the census <tables> need care", "signature": "sig"},
{"type": "text", "text": format!("found a leaked key {SECRET} in the env, scrubbing")},
{"type": "tool_use", "id": "t1", "name": "Bash",
"input": {"command": format!("export API_KEY={SECRET} && ./migrate")}}
]}
}),
serde_json::json!({
"type": "user", "uuid": "u2", "sessionId": "s", "timestamp": "2026-02-01T10:02:00Z",
"message": {"role": "user", "content": [
{"type": "tool_result", "tool_use_id": "t1",
"content": format!("migrated 7 quokkas; key was {SECRET}"), "is_error": false}
]}
}),
];
let body: String = lines.iter().map(|l| format!("{l}\n")).collect();
fs::write(w.home.join(".claude/projects/-work-proj/sharesess.jsonl"), body).unwrap();
}
fn assert_no_external_urls(html: &str) {
let lower = html.to_lowercase();
for needle in [
"src=\"http",
"src='http",
"href=\"http",
"href='http",
"src=\"//",
"href=\"//",
"@import",
"url(http",
] {
assert!(
!lower.contains(needle),
"external reference {needle:?} found in artifact"
);
}
}
fn assert_well_formed(html: &str) {
assert!(html.starts_with("<!doctype html"), "missing doctype");
assert!(html.trim_end().ends_with("</html>"), "unterminated document");
for tag in ["section", "details", "header", "main", "footer", "pre", "summary"] {
let open = html.matches(&format!("<{tag}")).count();
let close = html.matches(&format!("</{tag}>")).count();
assert_eq!(open, close, "unbalanced <{tag}>: {open} open vs {close} close");
}
}
#[test]
fn share_redacts_by_default() {
let w = World::new("redact");
share_corpus(&w);
let (_, err) = w.cv_ok(&["share", "sharesess", "--out", "shared.html"]);
assert!(!err.contains("NOT be scrubbed"), "stderr: {err}");
assert!(err.contains("redacted"), "stderr: {err}");
let html = fs::read_to_string(w.base.join("shared.html")).expect("artifact exists");
assert_well_formed(&html);
assert_no_external_urls(&html);
assert!(html.contains(PLANTED), "planted text missing");
assert!(!html.contains(SECRET), "secret leaked into artifact");
assert!(html.contains("[REDACTED:api_key]"), "no redaction placeholder");
assert!(html.contains("🛡 redacted"), "header badge missing");
assert!(html.contains("this transcript was redacted"), "footer badge missing");
assert!(!html.contains("unredacted"));
assert!(html.contains("quokka quest"), "session title missing");
assert!(html.contains(">claude</span>"), "harness badge missing");
assert!(html.contains("fold think"), "thinking not collapsible");
assert!(html.contains("fold tool tool-use"), "tool call not collapsible");
assert!(html.contains("fold tool tool-res"), "tool result not collapsible");
assert!(
html.contains("<style>") && html.contains("<script>"),
"inline assets missing"
);
assert!(html.contains("<tables>"), "content not escaped");
assert!(html.contains(&format!("clustervision</strong> v{}", env!("CARGO_PKG_VERSION"))));
}
#[test]
fn share_no_redact_keeps_secret_and_warns() {
let w = World::new("noredact");
share_corpus(&w);
let (_, err) = w.cv_ok(&["share", "sharesess", "--no-redact", "--out", "raw.html"]);
assert!(err.contains("--no-redact"), "missing warning: {err}");
assert!(err.contains("NOT be scrubbed"), "missing warning: {err}");
let html = fs::read_to_string(w.base.join("raw.html")).unwrap();
assert_well_formed(&html);
assert_no_external_urls(&html);
assert!(html.contains(SECRET), "--no-redact must keep the secret");
assert!(!html.contains("[REDACTED:"));
assert!(html.contains("⚠ unredacted"), "header badge missing");
assert!(html.contains("shared without redaction"), "footer badge missing");
assert!(!html.contains("this transcript was redacted"));
}
#[test]
fn share_default_output_path_is_id_html() {
let w = World::new("defaultout");
share_corpus(&w);
w.cv_ok(&["share", "sharesess"]);
let path = w.base.join("sharesess.html");
assert!(path.exists(), "default ./<id>.html not written");
let html = fs::read_to_string(path).unwrap();
assert_well_formed(&html);
assert!(html.contains(PLANTED));
}
#[test]
fn share_unknown_session_fails_cleanly() {
let w = World::new("missing");
share_corpus(&w);
let (ok, _, err) = w.cv(&["share", "nope-no-such-session"]);
assert!(!ok, "should fail on unknown id");
assert!(err.contains("no session matching"), "stderr: {err}");
assert!(!err.contains("panicked"), "panic instead of error: {err}");
}