use super::*;
use crate::live::channel::caller::{classify_with, session_transcript_for, SettingsDisclosure};
use crate::path::settings::{SourceReport, SCOPE_LOCAL, SCOPE_PLUGIN, SCOPE_PROJECT, SCOPE_USER};
use std::path::Path;
#[test]
fn the_session_variable_present_means_a_claude_code_lane() {
let c = classify_with(None, Some(SESSION.to_string())).unwrap();
assert_eq!(c.kind, SenderKind::Lane);
assert_eq!(c.session.as_deref(), Some(SESSION));
assert_eq!(
c.lane.as_deref(),
Some(SESSION),
"with no --from the send is attributed to the top-level session"
);
assert!(
!c.lane_exact,
"and says so: the environment never names a subagent lane"
);
assert!(c.label.is_none());
}
#[test]
fn from_names_an_exact_lane_and_main_resolves_to_the_session() {
let exact = classify_with(Some(&format!("@{AGENT}")), Some(SESSION.to_string())).unwrap();
assert_eq!(exact.lane.as_deref(), Some(AGENT));
assert!(exact.lane_exact);
let teammate = classify_with(Some(&format!("@{TEAMMATE}")), Some(SESSION.to_string())).unwrap();
assert_eq!(teammate.lane.as_deref(), Some(TEAMMATE));
let main = classify_with(Some("@main"), Some(SESSION.to_string())).unwrap();
assert_eq!(main.lane.as_deref(), Some(SESSION));
assert!(
main.lane_exact,
"@main is an exact claim, not an assumption"
);
}
#[test]
fn a_bare_label_inside_claude_code_is_refused_because_it_would_misattribute_a_lane() {
let err = classify_with(Some("ci-runner"), Some(SESSION.to_string())).unwrap_err();
let msg = format!("{err:#}");
assert!(msg.contains("`@<lane>` form"), "{msg}");
assert!(msg.contains("external-caller form"), "{msg}");
}
#[test]
fn a_from_that_is_not_a_lane_id_is_refused_with_the_grammar() {
let err = classify_with(Some("@nope"), Some(SESSION.to_string())).unwrap_err();
let msg = format!("{err:#}");
assert!(msg.contains("not a lane id"), "{msg}");
assert!(msg.contains("csift agents"), "{msg}");
}
#[test]
fn no_session_variable_means_external_with_a_free_label() {
let anon = classify_with(None, None).unwrap();
assert_eq!(anon.kind, SenderKind::External);
assert_eq!(
anon.label.as_deref(),
Some("unknown"),
"the default label is a literal, never the operating-system user"
);
assert!(anon.session.is_none() && anon.lane.is_none());
let named = classify_with(Some("ci-runner"), None).unwrap();
assert_eq!(named.label.as_deref(), Some("ci-runner"));
assert_eq!(named.kind, SenderKind::External);
}
#[test]
fn an_external_caller_cannot_claim_a_lane() {
let err = classify_with(Some(&format!("@{AGENT}")), None).unwrap_err();
let msg = format!("{err:#}");
assert!(msg.contains("free LABEL"), "{msg}");
assert!(msg.contains("cannot verify a lane claim"), "{msg}");
}
#[test]
fn a_blank_from_falls_back_to_the_default_label_rather_than_an_empty_sender() {
let c = classify_with(Some(" "), None).unwrap();
assert_eq!(c.label.as_deref(), Some("unknown"));
}
fn disclosure(rows: &[(&'static str, &str, bool, Option<&str>)]) -> SettingsDisclosure {
SettingsDisclosure {
sources: rows
.iter()
.map(|(scope, path, read, note)| SourceReport {
scope,
path: PathBuf::from(path),
read: *read,
note: note.map(std::string::ToString::to_string),
})
.collect(),
unobservable: vec!["--settings (a file path or inline JSON)", "MDM policy"],
}
}
#[test]
fn the_disclosure_names_the_scopes_that_contributed_and_the_ones_that_did_not() {
let d = disclosure(&[
(
SCOPE_PLUGIN,
"/Users/dev/.claude/plugins/a/plugin.json",
true,
None,
),
(SCOPE_USER, "/Users/dev/.claude/settings.json", true, None),
(
SCOPE_PROJECT,
"/Users/dev/p/.claude/settings.json",
false,
None,
),
(
SCOPE_LOCAL,
"/Users/dev/p/.claude/settings.local.json",
false,
None,
),
]);
assert_eq!(d.read_scopes(), vec![SCOPE_PLUGIN, SCOPE_USER]);
assert_eq!(d.absent_scopes(), vec![SCOPE_PROJECT, SCOPE_LOCAL]);
let lines = d.lines();
assert_eq!(
lines[0], "read: plugin, user · absent: project, local",
"the scopes are named in fold order, read ones first"
);
assert_eq!(
lines.last().unwrap(),
"unobservable: --settings (a file path or inline JSON); MDM policy",
"the unobservable list rides verbatim, so `unknown` always arrives with its reason"
);
}
#[test]
fn a_scope_read_from_two_files_is_named_once_and_never_also_as_absent() {
let d = disclosure(&[
(
SCOPE_PLUGIN,
"/Users/dev/.claude/plugins/a/hooks/hooks.json",
true,
None,
),
(
SCOPE_PLUGIN,
"/Users/dev/.claude/plugins/b/plugin.json",
true,
None,
),
(
SCOPE_PLUGIN,
"/Users/dev/.claude/plugins/c/plugin.json",
false,
None,
),
]);
assert_eq!(d.read_scopes(), vec![SCOPE_PLUGIN]);
assert!(d.absent_scopes().is_empty());
assert_eq!(d.lines()[0], "read: plugin · absent: none");
}
#[test]
fn a_broken_file_reaches_the_disclosure_as_a_note_and_the_json_carries_every_row() {
let d = disclosure(&[
(SCOPE_USER, "/Users/dev/.claude/settings.json", true, None),
(
SCOPE_PROJECT,
"/Users/dev/p/.claude/settings.json",
false,
Some("malformed JSON: expected value at line 1 column 1"),
),
]);
assert_eq!(
d.notes(),
vec!["project - malformed JSON: expected value at line 1 column 1"]
);
assert_eq!(
d.lines()[1],
"note: project - malformed JSON: expected value at line 1 column 1"
);
let json = d.json();
let sources = json["sources"].as_array().unwrap();
assert_eq!(sources.len(), 2, "one entry per FILE, in read order");
assert_eq!(sources[0]["scope"], SCOPE_USER);
assert_eq!(sources[0]["read"], true);
assert!(sources[0]["note"].is_null());
assert_eq!(sources[1]["read"], false);
assert!(sources[1]["note"]
.as_str()
.unwrap()
.contains("malformed JSON"));
assert_eq!(json["unobservable"].as_array().unwrap().len(), 2);
}
#[test]
fn a_cascade_with_no_readable_file_says_so_rather_than_printing_an_empty_list() {
let d = disclosure(&[(SCOPE_USER, "/Users/dev/.claude/settings.json", false, None)]);
assert_eq!(d.lines()[0], "read: none · absent: user");
}
#[test]
fn a_lanes_owning_session_transcript_is_found_through_the_subagents_component() {
let top = Path::new("/p/-Users-dev-relay").join(format!("{SESSION}.jsonl"));
assert_eq!(session_transcript_for(&top), top);
let sub = Path::new("/p/-Users-dev-relay")
.join(SESSION)
.join("subagents")
.join(format!("agent-{AGENT}.jsonl"));
assert_eq!(session_transcript_for(&sub), top);
let workflow = Path::new("/p/-Users-dev-relay")
.join(SESSION)
.join("subagents")
.join("workflows")
.join("wf_1")
.join(format!("agent-{AGENT}.jsonl"));
assert_eq!(
session_transcript_for(&workflow),
top,
"a workflow lane sits deeper but under the same session"
);
}