fabro/lib/crates/fabro-cli/tests/it/cmd/fork.rs
Bryan Helmkamp 5eeacd7864 fmt
2026-04-11 11:27:46 -04:00

153 lines
5 KiB
Rust

use fabro_test::{fabro_snapshot, run_and_format, test_context};
use insta::assert_snapshot;
use super::support::{
git_filters, git_show_json, git_stdout, metadata_run_ids, run_branch_commits,
run_branch_commits_since_base, setup_git_backed_changed_run,
};
#[test]
fn help() {
let context = test_context!();
let mut cmd = context.command();
cmd.args(["fork", "--help"]);
fabro_snapshot!(context.filters(), cmd, @"
success: true
exit_code: 0
----- stdout -----
Fork a workflow run from an earlier checkpoint into a new run
Usage: fabro fork [OPTIONS] <RUN_ID> [TARGET]
Arguments:
<RUN_ID> Run ID (or unambiguous prefix)
[TARGET] Target checkpoint: node name, node@visit, or @ordinal (omit to fork from latest)
Options:
--json Output as JSON [env: FABRO_JSON=]
--server <SERVER> Fabro server target: http(s) URL or absolute Unix socket path [env: FABRO_SERVER=]
--debug Enable DEBUG-level logging (default is INFO) [env: FABRO_DEBUG=]
--list Show the checkpoint timeline instead of forking
--no-push Skip pushing new branches to the remote
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
-h, --help Print help
----- stderr -----
");
}
#[test]
fn fork_outside_git_repo_errors() {
let context = test_context!();
let mut cmd = context.command();
cmd.args(["fork", "01ARZ3NDEKTSV4RRFFQ69G5FAW"]);
fabro_snapshot!(context.filters(), cmd, @"
success: false
exit_code: 1
----- stdout -----
----- stderr -----
error: not in a git repository
> could not find repository at '.'; class=Repository (6); code=NotFound (-3)
");
}
#[test]
fn fork_latest_prints_new_run_and_resume_hint() {
let context = test_context!();
let setup = setup_git_backed_changed_run(&context);
let before = metadata_run_ids(&setup.repo_dir);
let mut cmd = context.command();
cmd.current_dir(&setup.repo_dir);
cmd.args(["fork", &setup.run.run_id, "--no-push"]);
let (snapshot, output) = run_and_format(&mut cmd, &git_filters(&context));
assert_snapshot!(snapshot, @"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Forked run [RUN_PREFIX] -> [RUN_PREFIX]
To resume: fabro resume [RUN_PREFIX]
");
assert!(output.status.success(), "fork should succeed");
let after = metadata_run_ids(&setup.repo_dir);
let new_run_ids: Vec<_> = after.difference(&before).cloned().collect();
assert_eq!(
new_run_ids.len(),
1,
"fork should create one new run branch"
);
let new_run_id = &new_run_ids[0];
let new_head = git_stdout(&setup.repo_dir, &[
"rev-parse",
&format!("fabro/run/{new_run_id}"),
]);
let expected_head = run_branch_commits(&setup.repo_dir, &setup.run.run_id)
.into_iter()
.last()
.expect("source run should have a last run commit");
assert_eq!(new_head.trim(), expected_head);
}
#[test]
fn fork_from_earlier_checkpoint_uses_expected_sha() {
let context = test_context!();
let setup = setup_git_backed_changed_run(&context);
let before = metadata_run_ids(&setup.repo_dir);
let expected_head =
run_branch_commits_since_base(&setup.repo_dir, &setup.run.run_id, &setup.base_sha)
.into_iter()
.next()
.expect("source run should have a first run commit");
let output = context
.command()
.current_dir(&setup.repo_dir)
.args(["fork", &setup.run.run_id, "@1", "--no-push"])
.output()
.expect("fork should execute");
assert!(
output.status.success(),
"fork should succeed\nstdout:\n{}\nstderr:\n{}",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
);
let after = metadata_run_ids(&setup.repo_dir);
let new_run_ids: Vec<_> = after.difference(&before).cloned().collect();
assert_eq!(
new_run_ids.len(),
1,
"fork should create one new run branch"
);
let new_run_id = &new_run_ids[0];
let new_head = git_stdout(&setup.repo_dir, &[
"rev-parse",
&format!("fabro/run/{new_run_id}"),
]);
assert_eq!(new_head.trim(), expected_head);
let checkpoint = git_show_json(
&setup.repo_dir,
&format!("fabro/meta/{new_run_id}:checkpoint.json"),
);
assert_eq!(checkpoint["current_node"].as_str(), Some("step_one"));
assert_eq!(
checkpoint["git_commit_sha"].as_str(),
Some(expected_head.as_str())
);
let start = git_show_json(
&setup.repo_dir,
&format!("fabro/meta/{new_run_id}:start.json"),
);
let expected_branch = format!("fabro/run/{new_run_id}");
assert_eq!(start["run_branch"].as_str(), Some(expected_branch.as_str()));
}