fabro/lib/crates/fabro-cli/tests/it/scenario/artifacts.rs
Bryan Helmkamp bd8f0fe5ee refactor(storage): unify scratch paths and key schema
- centralize FABRO_HOME and storage path resolution in fabro-config
- rename store types, extract ArtifactStore, and simplify run key layout
- switch run scratch to scratch/, remove RuntimeState, and refresh docs/clients
2026-04-06 10:13:37 -04:00

174 lines
4.9 KiB
Rust

use std::time::Duration;
use fabro_test::{fabro_snapshot, test_context};
use crate::cmd::support::{read_text, setup_artifact_run, text_tree};
fn artifact_filters(context: &fabro_test::TestContext) -> Vec<(String, String)> {
let mut filters = context.filters();
filters.push((
r"\[STORAGE_DIR\]/scratch/\d{8}-\[ULID\]".to_string(),
"[RUN_DIR]".to_string(),
));
filters
}
#[test]
fn artifact_commands_share_populated_run_fixture() {
let context = test_context!();
let setup = setup_artifact_run(&context);
let filters = artifact_filters(&context);
let mut list_json = context.command();
list_json.args(["artifact", "list", &setup.run.run_id, "--json"]);
fabro_snapshot!(filters.clone(), list_json, @r#"
success: true
exit_code: 0
----- stdout -----
[
{
"node_slug": "create_assets",
"retry": 1,
"relative_path": "assets/node_a/summary.txt",
"size": 5
},
{
"node_slug": "create_assets",
"retry": 1,
"relative_path": "assets/shared/report.txt",
"size": 3
},
{
"node_slug": "create_colliding",
"retry": 1,
"relative_path": "assets/other/summary.txt",
"size": 4
},
{
"node_slug": "create_colliding",
"retry": 1,
"relative_path": "assets/retry/report.txt",
"size": 6
},
{
"node_slug": "retry_assets",
"retry": 1,
"relative_path": "assets/retry/report.txt",
"size": 5
},
{
"node_slug": "retry_assets",
"retry": 2,
"relative_path": "assets/retry/report.txt",
"size": 6
}
]
----- stderr -----
"#);
let mut list_filtered = context.command();
list_filtered.args([
"artifact",
"list",
&setup.run.run_id,
"--node",
"retry_assets",
"--retry",
"2",
"--json",
]);
fabro_snapshot!(filters.clone(), list_filtered, @r#"
success: true
exit_code: 0
----- stdout -----
[
{
"node_slug": "retry_assets",
"retry": 2,
"relative_path": "assets/retry/report.txt",
"size": 6
}
]
----- stderr -----
"#);
let single_dest = context.temp_dir.join("artifact-one");
let mut cp_single = context.command();
cp_single.args([
"artifact",
"cp",
&format!("{}:assets/shared/report.txt", setup.run.run_id),
single_dest.to_str().unwrap(),
"--node",
"create_assets",
]);
fabro_snapshot!(context.filters(), cp_single, @"
success: true
exit_code: 0
----- stdout -----
Copied assets/shared/report.txt to [TEMP_DIR]/artifact-one/report.txt
----- stderr -----
");
assert_eq!(read_text(&single_dest.join("report.txt")), "one");
let tree_dest = context.temp_dir.join("artifact-tree");
let mut cp_tree = context.command();
cp_tree.args([
"artifact",
"cp",
&setup.run.run_id,
tree_dest.to_str().unwrap(),
"--tree",
]);
cp_tree.timeout(Duration::from_secs(30));
fabro_snapshot!(context.filters(), cp_tree, @"
success: true
exit_code: 0
----- stdout -----
Copied 6 artifact(s) to [TEMP_DIR]/artifact-tree
----- stderr -----
");
insta::assert_snapshot!(
text_tree(&tree_dest).join("\n"),
@r"
create_assets/retry_1/assets/node_a/summary.txt = alpha
create_assets/retry_1/assets/shared/report.txt = one
create_colliding/retry_1/assets/other/summary.txt = beta
create_colliding/retry_1/assets/retry/report.txt = second
retry_assets/retry_1/assets/retry/report.txt = first
retry_assets/retry_2/assets/retry/report.txt = second
"
);
let ambiguous_dest = context.temp_dir.join("artifact-ambiguous");
let mut cp_ambiguous = context.command();
cp_ambiguous.args([
"artifact",
"cp",
&format!("{}:assets/retry/report.txt", setup.run.run_id),
ambiguous_dest.to_str().unwrap(),
]);
fabro_snapshot!(context.filters(), cp_ambiguous, @"
success: false
exit_code: 1
----- stdout -----
----- stderr -----
error: Path 'assets/retry/report.txt' matches multiple artifacts: create_colliding:retry_1, retry_assets:retry_1, retry_assets:retry_2. Use --node and/or --retry to disambiguate.
");
let flat_dest = context.temp_dir.join("artifact-flat");
let mut cp_flat = context.command();
cp_flat.args([
"artifact",
"cp",
&setup.run.run_id,
flat_dest.to_str().unwrap(),
]);
fabro_snapshot!(context.filters(), cp_flat, @"
success: false
exit_code: 1
----- stdout -----
----- stderr -----
error: Filename collision: 'summary.txt' exists in both create_assets:retry_1 and create_colliding:retry_1. Use --tree to preserve directory structure, or --node and/or --retry to filter.
");
}