use crate::support::Seat;
use serde_json::{Value, json};
use std::os::unix::fs::{PermissionsExt as _, symlink};
#[test]
fn assist004_r2_harness_gets_its_own_home_and_no_other_harness_home() -> anyhow::Result<()> {
let inherited = tempfile::tempdir()?;
for (id, variable, absent) in [
("codex", "CODEX_HOME", "CLAUDE_CONFIG_DIR"),
("claude-code", "CLAUDE_CONFIG_DIR", "CODEX_HOME"),
] {
let parent = inherited
.path()
.to_str()
.ok_or_else(|| anyhow::anyhow!("fixture path invalid"))?;
let mut seat = Seat::configured(
&[],
&[("CODEX_HOME", parent), ("CLAUDE_CONFIG_DIR", parent)],
|config| {
config["harness"]["id"] = json!(id);
config["harness"]["environment"] = json!(["CODEX_HOME", "CLAUDE_CONFIG_DIR"]);
},
)?;
let initialized = seat.event("initialize")?;
let actual = initialized["homes"][variable].as_str();
let expected = seat.directory.path().join("harness-home");
assert!(
actual.is_some_and(|home| std::path::Path::new(home) == expected),
"harness did not receive the seat's home"
);
assert!(
initialized["homes"][absent].is_null(),
"other harness home was inherited"
);
seat.event("session_opened")?;
seat.close(0)?;
}
Ok(())
}
fn refused(configure: impl FnOnce(&mut Value), reason: &str) -> anyhow::Result<()> {
let mut seat = Seat::configured(&[], &[], configure)?;
seat.eof();
let output = seat.finish()?;
let error = String::from_utf8_lossy(&output.stderr);
assert_eq!(
output.status.code(),
Some(1),
"invalid harness.home started"
);
assert!(
error.contains("harness.home") && error.contains(reason),
"home refusal did not name its reason: {error}"
);
assert_eq!(
seat.issuer.count("/api/agents/assistant-fixture/pass")?,
0,
"invalid home reached startup"
);
Ok(())
}
#[test]
fn assist004_r2_missing_home_is_refused_before_start() -> anyhow::Result<()> {
refused(
|config| {
config["harness"]
.as_object_mut()
.map(|harness| harness.remove("home"));
},
"home_required",
)
}
#[test]
fn assist004_r2_relative_home_is_refused_before_start() -> anyhow::Result<()> {
refused(
|config| config["harness"]["home"] = json!("relative"),
"home_must_be_absolute",
)
}
#[test]
fn assist004_r2_nonexistent_home_is_refused_before_start() -> anyhow::Result<()> {
let directory = tempfile::tempdir()?;
refused(
|config| config["harness"]["home"] = json!(directory.path().join("missing")),
"home_unreadable",
)
}
#[test]
fn assist004_r2_shared_home_is_refused_before_start() -> anyhow::Result<()> {
let directory = tempfile::tempdir()?;
std::fs::set_permissions(directory.path(), std::fs::Permissions::from_mode(0o755))?;
refused(
|config| config["harness"]["home"] = json!(directory.path()),
"home_not_private",
)
}
#[test]
fn assist004_r2_symlink_home_is_refused_before_start() -> anyhow::Result<()> {
let directory = tempfile::tempdir()?;
let link = directory.path().join("link");
symlink(directory.path(), &link)?;
refused(
|config| config["harness"]["home"] = json!(link),
"home_unreadable",
)
}