use super::*;
use crate::cmd::run::read_only_delivery;
#[test]
fn scope_cut_fix_rejects_task_home_inside_repository_or_git_common_directory() {
let _permit = test_subprocess::acquire();
let home = tempfile::tempdir().unwrap();
let _guard = paths::AidHomeGuard::set(home.path());
let f = Fixture::new(home.path(), false, false, false);
for root in [
PathBuf::from(f.args.dir.as_ref().unwrap()),
f._repo.path().join(".git"),
] {
let _nested = paths::AidHomeGuard::set(&root.join("aid-home"));
let mut args = f.args.clone();
let task = f.store.get_task(f.id.as_str()).unwrap().unwrap();
let error = read_only_delivery::prepare(&mut args, AgentKind::Codex, &task)
.expect_err("read-only dispatch must reject a protected task directory");
assert!(error.to_string().contains("task directory"), "{error}");
}
}
#[tokio::test]
async fn scope_cut_fix_missing_or_refused_staging_never_selects_old_reports() {
let _permit = test_subprocess::acquire();
for output in [false, true] {
for refused in [false, true] {
let home = tempfile::tempdir().unwrap();
let _guard = paths::AidHomeGuard::set(home.path());
let mut f = Fixture::new(home.path(), false, false, false);
fs::write(
Path::new(f.args.dir.as_ref().unwrap()).join("base.txt"),
"base\n",
)
.unwrap();
let destination = home.path().join("old-report.md");
fs::write(&destination, "STALE REPORT").unwrap();
let official = paths::task_dir(f.id.as_str()).join("result.md");
fs::create_dir_all(official.parent().unwrap()).unwrap();
fs::write(&official, "STALE OFFICIAL REPORT").unwrap();
let name = if output {
f.args.result_file = None;
f.args.output = Some(destination.display().to_string());
f.store
.update_output_path(f.id.as_str(), destination.to_str().unwrap())
.unwrap();
"agent-output.txt"
} else {
f.args.result_file = Some(destination.display().to_string());
"agent-result.md"
};
if refused {
std::os::unix::fs::symlink(&destination, staged(&f, name)).unwrap();
}
f.settle().await;
assert_not_delivered(&f).await;
assert_delivery_failed(&f);
assert_eq!(fs::read_to_string(destination).unwrap(), "STALE REPORT");
}
}
}
async fn assert_not_delivered(f: &Fixture) {
let task = f.store.get_task(f.id.as_str()).unwrap().unwrap();
assert!(!crate::cmd::run::run_lifecycle::output_has_content(&task));
let outputs = vec![
crate::cmd::show::output_text(&f.store, f.id.as_str()).unwrap(),
crate::task::view::read_output(&task),
];
#[cfg(feature = "web")]
let outputs = {
let mut outputs = outputs;
let axum::Json(response) = crate::ui::web::api::get_task_result(
axum::extract::Path(f.id.0.clone()),
axum::extract::State(f.store.clone()),
)
.await
.unwrap();
outputs.push(response.result);
outputs
};
for text in outputs {
assert!(text.contains("not delivered by this run"), "{text}");
assert!(!text.contains("STALE"), "{text}");
}
}
#[tokio::test]
async fn scope_cut_fix_official_report_copy_failure_records_task_event() {
let _permit = test_subprocess::acquire();
let home = tempfile::tempdir().unwrap();
let _guard = paths::AidHomeGuard::set(home.path());
let f = Fixture::new(home.path(), true, false, false);
fs::write(
Path::new(f.args.dir.as_ref().unwrap()).join("base.txt"),
"base\n",
)
.unwrap();
fs::create_dir_all(paths::task_dir(f.id.as_str()).join("result.md")).unwrap();
f.settle().await;
assert_delivery_failed(&f);
}
#[tokio::test]
async fn scope_cut_fix_replacement_after_validation_is_not_published_to_any_sink() {
let _permit = test_subprocess::acquire();
let home = tempfile::tempdir().unwrap();
let _guard = paths::AidHomeGuard::set(home.path());
let f = Fixture::new(home.path(), true, false, false);
fs::write(
Path::new(f.args.dir.as_ref().unwrap()).join("base.txt"),
"base\n",
)
.unwrap();
let source = staged(&f, "agent-result.md");
let replacement = home.path().join("replacement.md");
fs::write(&replacement, "REPLACEMENT MUST NOT BE PUBLISHED").unwrap();
read_only_delivery::AFTER_VALIDATE.with(|hook| {
*hook.borrow_mut() = Some(Box::new(move || {
fs::remove_file(&source).unwrap();
std::os::unix::fs::symlink(replacement, source).unwrap();
}));
});
f.settle().await;
assert_eq!(
fs::read_to_string(f.args.result_file.as_ref().unwrap()).unwrap(),
REPORT
);
assert_eq!(
fs::read_to_string(paths::task_dir(f.id.as_str()).join("result.md")).unwrap(),
REPORT
);
}
#[tokio::test]
async fn scope_cut_fix_hard_linked_staged_report_is_refused() {
let _permit = test_subprocess::acquire();
let home = tempfile::tempdir().unwrap();
let _guard = paths::AidHomeGuard::set(home.path());
let f = Fixture::new(home.path(), true, false, false);
fs::write(
Path::new(f.args.dir.as_ref().unwrap()).join("base.txt"),
"base\n",
)
.unwrap();
fs::hard_link(staged(&f, "agent-result.md"), home.path().join("alias.md")).unwrap();
f.settle().await;
assert_delivery_failed(&f);
assert!(!Path::new(f.args.result_file.as_ref().unwrap()).exists());
}