mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-08 03:10:26 +00:00
Return canonical Run payloads across run list, board, create, and lifecycle endpoints. Move archive state out of RunStatus and into lifecycle metadata, split sandbox runtime from planned sandbox data, and separate static pull request records from live pull request details. Regenerate the TypeScript API client and migrate web, CLI, server, store, workflow, and API tests to the new contract.
94 lines
3 KiB
Rust
94 lines
3 KiB
Rust
use fabro_test::test_context;
|
|
use serde_json::Value;
|
|
|
|
use crate::cmd::support::setup_completed_fast_dry_run;
|
|
|
|
fn ps_runs(context: &fabro_test::TestContext, include_archived: bool) -> Vec<Value> {
|
|
let label = context.test_case_label();
|
|
let mut cmd = context.ps();
|
|
if include_archived {
|
|
cmd.arg("-a");
|
|
}
|
|
cmd.args(["--json", "--label", &label]);
|
|
let output = cmd.output().expect("ps should execute");
|
|
assert!(
|
|
output.status.success(),
|
|
"ps failed\nstdout:\n{}\nstderr:\n{}",
|
|
String::from_utf8_lossy(&output.stdout),
|
|
String::from_utf8_lossy(&output.stderr)
|
|
);
|
|
serde_json::from_slice(&output.stdout).expect("ps JSON should parse")
|
|
}
|
|
|
|
#[test]
|
|
fn archive_lifecycle_end_to_end() {
|
|
let context = test_context!();
|
|
let run = setup_completed_fast_dry_run(&context);
|
|
|
|
let visible = ps_runs(&context, true);
|
|
assert_eq!(visible.len(), 1);
|
|
assert_eq!(visible[0]["run_id"], run.run_id);
|
|
assert_eq!(visible[0]["status"]["kind"], "succeeded");
|
|
assert_eq!(visible[0]["status"]["reason"], "completed");
|
|
|
|
let archive = context
|
|
.command()
|
|
.args(["archive", &run.run_id])
|
|
.output()
|
|
.expect("archive should execute");
|
|
assert!(
|
|
archive.status.success(),
|
|
"archive failed\nstderr:\n{}",
|
|
String::from_utf8_lossy(&archive.stderr)
|
|
);
|
|
|
|
let default_visible = ps_runs(&context, false);
|
|
assert!(
|
|
default_visible.is_empty(),
|
|
"default ps should hide archived, got {default_visible:?}"
|
|
);
|
|
|
|
let with_archived = ps_runs(&context, true);
|
|
assert_eq!(with_archived.len(), 1);
|
|
assert_eq!(with_archived[0]["run_id"], run.run_id);
|
|
assert_eq!(with_archived[0]["status"]["kind"], "succeeded");
|
|
assert_eq!(with_archived[0]["status"]["reason"], "completed");
|
|
|
|
let unarchive = context
|
|
.command()
|
|
.args(["unarchive", &run.run_id])
|
|
.output()
|
|
.expect("unarchive should execute");
|
|
assert!(
|
|
unarchive.status.success(),
|
|
"unarchive failed\nstderr:\n{}",
|
|
String::from_utf8_lossy(&unarchive.stderr)
|
|
);
|
|
let restored = ps_runs(&context, true);
|
|
assert_eq!(restored[0]["status"]["kind"], "succeeded");
|
|
assert_eq!(restored[0]["status"]["reason"], "completed");
|
|
|
|
// `rm` must remain available on archived runs — archive and delete are
|
|
// orthogonal per the plan's Scope Boundaries.
|
|
context
|
|
.command()
|
|
.args(["archive", &run.run_id])
|
|
.output()
|
|
.expect("re-archive should execute");
|
|
let rm = context
|
|
.command()
|
|
.args(["rm", &run.run_id])
|
|
.output()
|
|
.expect("rm should execute");
|
|
assert!(
|
|
rm.status.success(),
|
|
"rm on archived run should succeed\nstderr:\n{}",
|
|
String::from_utf8_lossy(&rm.stderr)
|
|
);
|
|
assert!(!run.run_dir.exists(), "run directory should be deleted");
|
|
let after_delete = ps_runs(&context, true);
|
|
assert!(
|
|
after_delete.is_empty(),
|
|
"run should be gone after rm, got {after_delete:?}"
|
|
);
|
|
}
|