mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-08 03:10:26 +00:00
Default TestContext::command() working directory to temp_dir
Tests were flaky because command() inherited the real repo as the working directory. When the repo was clean, detached runs attempted git worktree creation against it, sometimes failing and injecting extra warning lines into snapshots. Now command() defaults to the non-git temp_dir, eliminating this class of flakiness. Tests needing a specific directory override with .current_dir(). Also canonicalizes fixture paths and adds a [FIXTURES] snapshot filter via test_context!() macro. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e95f1a0386
commit
b5e5b91d51
21 changed files with 73 additions and 70 deletions
|
|
@ -103,7 +103,6 @@ fn attach_before_completion_streams_to_finished_state() {
|
|||
let gate = write_gated_workflow(&context.temp_dir.join("slow.fabro"), "slow", "Run slowly");
|
||||
|
||||
let mut run_cmd = context.command();
|
||||
run_cmd.current_dir(&context.temp_dir);
|
||||
run_cmd.env("OPENAI_API_KEY", "test");
|
||||
run_cmd.args([
|
||||
"run",
|
||||
|
|
@ -136,7 +135,6 @@ fn attach_before_completion_streams_to_finished_state() {
|
|||
gate.release();
|
||||
});
|
||||
let mut attach_cmd = context.command();
|
||||
attach_cmd.current_dir(&context.temp_dir);
|
||||
attach_cmd.args(["attach", &run_id]);
|
||||
let (snapshot, _output) = run_and_format(&mut attach_cmd, &filters);
|
||||
release_gate.join().expect("gate releaser should join");
|
||||
|
|
@ -176,7 +174,6 @@ fn attach_json_errors_without_prompting_for_human_input() {
|
|||
|
||||
let run_output = context
|
||||
.command()
|
||||
.current_dir(&context.temp_dir)
|
||||
.env("OPENAI_API_KEY", "test")
|
||||
.args([
|
||||
"run",
|
||||
|
|
|
|||
|
|
@ -187,7 +187,6 @@ digraph Test {
|
|||
|
||||
let run = context
|
||||
.command()
|
||||
.current_dir(&context.temp_dir)
|
||||
.args([
|
||||
"run",
|
||||
"--dry-run",
|
||||
|
|
|
|||
|
|
@ -75,7 +75,6 @@ fn exec_missing_api_key_exits_with_error() {
|
|||
cmd.arg("test prompt");
|
||||
cmd.env_clear();
|
||||
cmd.env("HOME", &context.home_dir);
|
||||
cmd.current_dir(&context.temp_dir);
|
||||
fabro_snapshot!(context.filters(), cmd, @"
|
||||
success: false
|
||||
exit_code: 1
|
||||
|
|
@ -99,7 +98,6 @@ fn exec_uses_user_config_defaults() {
|
|||
cmd.env("HOME", &context.home_dir);
|
||||
cmd.env("FABRO_STORAGE_DIR", &context.storage_dir);
|
||||
cmd.env("FABRO_NO_UPGRADE_CHECK", "true");
|
||||
cmd.current_dir(&context.temp_dir);
|
||||
|
||||
fabro_snapshot!(context.filters(), cmd, @"
|
||||
success: false
|
||||
|
|
@ -125,7 +123,6 @@ fn exec_creates_file() {
|
|||
"claude-haiku-4-5",
|
||||
"Create a file called hello.txt containing exactly 'Hello'",
|
||||
])
|
||||
.current_dir(&context.temp_dir)
|
||||
.timeout(std::time::Duration::from_secs(120))
|
||||
.assert()
|
||||
.success();
|
||||
|
|
@ -153,7 +150,6 @@ fn exec_shell_command() {
|
|||
"claude-haiku-4-5",
|
||||
"Run the shell command `echo arc_test_marker_42` and tell me what it printed",
|
||||
])
|
||||
.current_dir(&context.temp_dir)
|
||||
.timeout(std::time::Duration::from_secs(120))
|
||||
.assert()
|
||||
.success();
|
||||
|
|
@ -174,7 +170,6 @@ fn exec_read_only_blocks_write() {
|
|||
"claude-haiku-4-5",
|
||||
"Create a file called forbidden.txt containing 'should not exist'",
|
||||
])
|
||||
.current_dir(&context.temp_dir)
|
||||
.timeout(std::time::Duration::from_secs(120))
|
||||
.assert()
|
||||
.success();
|
||||
|
|
@ -201,7 +196,6 @@ fn exec_json_output_format() {
|
|||
"claude-haiku-4-5",
|
||||
"Create a file called test.txt containing 'test'",
|
||||
])
|
||||
.current_dir(&context.temp_dir)
|
||||
.timeout(std::time::Duration::from_secs(120))
|
||||
.assert()
|
||||
.success()
|
||||
|
|
@ -237,7 +231,6 @@ fn exec_read_and_edit() {
|
|||
"claude-haiku-4-5",
|
||||
"Read data.txt then replace its entire content with 'new content'",
|
||||
])
|
||||
.current_dir(&context.temp_dir)
|
||||
.timeout(std::time::Duration::from_secs(120))
|
||||
.assert()
|
||||
.success();
|
||||
|
|
|
|||
|
|
@ -42,7 +42,6 @@ fn help() {
|
|||
fn fork_outside_git_repo_errors() {
|
||||
let context = test_context!();
|
||||
let mut cmd = context.command();
|
||||
cmd.current_dir(&context.temp_dir);
|
||||
cmd.args(["fork", "01ARZ3NDEKTSV4RRFFQ69G5FAW"]);
|
||||
|
||||
fabro_snapshot!(context.filters(), cmd, @"
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@ fn parse_valid_workflow_prints_ast_json() {
|
|||
"digraph Tiny {\n graph [goal=\"Parse a tiny workflow\"]\n start [shape=Mdiamond]\n exit [shape=Msquare]\n main [label=\"Main\", prompt=\"Do the thing\"]\n start -> main -> exit\n}\n",
|
||||
);
|
||||
let mut cmd = context.command();
|
||||
cmd.current_dir(&context.temp_dir);
|
||||
cmd.args(["parse", "tiny.fabro"]);
|
||||
|
||||
fabro_snapshot!(context.filters(), cmd, @r###"
|
||||
|
|
@ -125,7 +124,6 @@ fn parse_invalid_dot_fails_cleanly() {
|
|||
"digraph Bad {\n start [shape=Mdiamond]\n exit [shape=Msquare]\n start -> exit\n",
|
||||
);
|
||||
let mut cmd = context.command();
|
||||
cmd.current_dir(&context.temp_dir);
|
||||
cmd.args(["parse", "bad.fabro"]);
|
||||
|
||||
fabro_snapshot!(context.filters(), cmd, @"
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ fn preflight_invalid_workflow_fails_with_validation_output() {
|
|||
----- stdout -----
|
||||
----- stderr -----
|
||||
Workflow: Invalid (2 nodes, 1 edges)
|
||||
Graph: ../../../test/invalid.fabro
|
||||
Graph: [FIXTURES]/invalid.fabro
|
||||
error: Pipeline must have exactly one start node (shape=Mdiamond or id start/Start) (start_node)
|
||||
error [node: exit]: Exit node 'exit' has 1 outgoing edge(s) but must have none (exit_no_outgoing)
|
||||
error: Validation failed
|
||||
|
|
|
|||
|
|
@ -46,12 +46,7 @@ fn test_repo_deinit_removes_fabro_toml_and_dir() {
|
|||
assert!(context.temp_dir.join("fabro.toml").exists());
|
||||
assert!(context.temp_dir.join("fabro").exists());
|
||||
|
||||
context
|
||||
.repo()
|
||||
.arg("deinit")
|
||||
.current_dir(&context.temp_dir)
|
||||
.assert()
|
||||
.success();
|
||||
context.repo().arg("deinit").assert().success();
|
||||
|
||||
assert!(
|
||||
!context.temp_dir.join("fabro.toml").exists(),
|
||||
|
|
@ -70,7 +65,6 @@ fn test_repo_deinit_fails_when_not_initialized() {
|
|||
|
||||
let mut cmd = context.repo();
|
||||
cmd.arg("deinit");
|
||||
cmd.current_dir(&context.temp_dir);
|
||||
fabro_snapshot!(context.filters(), cmd, @"
|
||||
success: false
|
||||
exit_code: 1
|
||||
|
|
@ -85,12 +79,7 @@ fn test_repo_init_skill_installs_skill_files() {
|
|||
let context = test_context!();
|
||||
context.git_init();
|
||||
|
||||
context
|
||||
.repo()
|
||||
.args(["init", "--skill"])
|
||||
.current_dir(&context.temp_dir)
|
||||
.assert()
|
||||
.success();
|
||||
context.repo().args(["init", "--skill"]).assert().success();
|
||||
|
||||
// Skill files should be installed under .claude/skills/fabro-create-workflow/
|
||||
let skill_dir = context
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ fn repo_init_creates_fabro_toml_and_hello_workflow() {
|
|||
context.git_init();
|
||||
|
||||
let mut cmd = context.command();
|
||||
cmd.current_dir(&context.temp_dir);
|
||||
cmd.args(["repo", "init"]);
|
||||
|
||||
fabro_snapshot!(context.filters(), cmd, @"
|
||||
|
|
@ -111,7 +110,6 @@ fn repo_init_rejects_already_initialized_repo() {
|
|||
std::fs::write(context.temp_dir.join("fabro.toml"), "version = 1\n").unwrap();
|
||||
|
||||
let mut cmd = context.command();
|
||||
cmd.current_dir(&context.temp_dir);
|
||||
cmd.args(["repo", "init"]);
|
||||
|
||||
fabro_snapshot!(context.filters(), cmd, @"
|
||||
|
|
@ -127,7 +125,6 @@ fn repo_init_rejects_already_initialized_repo() {
|
|||
fn repo_init_errors_outside_git_repo() {
|
||||
let context = test_context!();
|
||||
let mut cmd = context.command();
|
||||
cmd.current_dir(&context.temp_dir);
|
||||
cmd.args(["repo", "init"]);
|
||||
|
||||
fabro_snapshot!(context.filters(), cmd, @"
|
||||
|
|
|
|||
|
|
@ -42,7 +42,6 @@ fn help() {
|
|||
fn rewind_outside_git_repo_errors() {
|
||||
let context = test_context!();
|
||||
let mut cmd = context.command();
|
||||
cmd.current_dir(&context.temp_dir);
|
||||
cmd.args(["rewind", "01ARZ3NDEKTSV4RRFFQ69G5FAW", "--list"]);
|
||||
|
||||
fabro_snapshot!(context.filters(), cmd, @"
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ fn dry_run_simple() {
|
|||
----- stdout -----
|
||||
----- stderr -----
|
||||
Workflow: Simple (4 nodes, 3 edges)
|
||||
Graph: ../../../test/simple.fabro
|
||||
Graph: [FIXTURES]/simple.fabro
|
||||
Goal: Run tests and report results
|
||||
|
||||
Sandbox: local (ready in [TIME])
|
||||
|
|
@ -80,11 +80,9 @@ fn dry_run_simple() {
|
|||
fn dry_run_writes_jsonl_and_live_json() {
|
||||
let context = test_context!();
|
||||
let run_id = "01ARZ3NDEKTSV4RRFFQ69G5FB8";
|
||||
let workflow = example_fixture("simple.fabro");
|
||||
|
||||
context
|
||||
.command()
|
||||
.current_dir(&context.temp_dir)
|
||||
.args([
|
||||
"run",
|
||||
"--dry-run",
|
||||
|
|
@ -93,7 +91,7 @@ fn dry_run_writes_jsonl_and_live_json() {
|
|||
"local",
|
||||
"--run-id",
|
||||
run_id,
|
||||
workflow.to_str().unwrap(),
|
||||
example_fixture("simple.fabro").to_str().unwrap(),
|
||||
])
|
||||
.assert()
|
||||
.success();
|
||||
|
|
@ -427,7 +425,7 @@ fn run_id_passthrough_uses_provided_ulid() {
|
|||
"--auto-approve",
|
||||
"--run-id",
|
||||
run_id,
|
||||
"../../../test/simple.fabro",
|
||||
example_fixture("simple.fabro").to_str().unwrap(),
|
||||
])
|
||||
.assert()
|
||||
.success();
|
||||
|
|
@ -461,7 +459,6 @@ fn json_run_implies_auto_approve_for_human_gates() {
|
|||
|
||||
let output = context
|
||||
.command()
|
||||
.current_dir(&context.temp_dir)
|
||||
.args([
|
||||
"--json",
|
||||
"run",
|
||||
|
|
@ -811,7 +808,7 @@ fn detach_prints_ulid_and_exits() {
|
|||
"--detach",
|
||||
"--dry-run",
|
||||
"--auto-approve",
|
||||
"../../../test/simple.fabro",
|
||||
example_fixture("simple.fabro").to_str().unwrap(),
|
||||
]);
|
||||
fabro_snapshot!(context.filters(), cmd, @"
|
||||
success: true
|
||||
|
|
@ -835,7 +832,7 @@ fn detach_creates_run_dir_with_detach_log() {
|
|||
"--auto-approve",
|
||||
"--run-id",
|
||||
run_id,
|
||||
"../../../test/simple.fabro",
|
||||
example_fixture("simple.fabro").to_str().unwrap(),
|
||||
])
|
||||
.assert()
|
||||
.success();
|
||||
|
|
|
|||
|
|
@ -163,7 +163,6 @@ fn start_rejects_already_active_or_completed_run() {
|
|||
let gate = write_gated_workflow(&context.temp_dir.join("slow.fabro"), "slow", "Run slowly");
|
||||
|
||||
let mut create_cmd = context.command();
|
||||
create_cmd.current_dir(&context.temp_dir);
|
||||
create_cmd.env("OPENAI_API_KEY", "test");
|
||||
create_cmd.args([
|
||||
"create",
|
||||
|
|
@ -185,7 +184,6 @@ fn start_rejects_already_active_or_completed_run() {
|
|||
let run = resolve_run(&context, &run_id);
|
||||
|
||||
let mut start_cmd = context.command();
|
||||
start_cmd.current_dir(&context.temp_dir);
|
||||
start_cmd.env("OPENAI_API_KEY", "test");
|
||||
start_cmd.args(["start", &run_id]);
|
||||
start_cmd.assert().success();
|
||||
|
|
@ -193,7 +191,6 @@ fn start_rejects_already_active_or_completed_run() {
|
|||
wait_for_status(&run.run_dir, &["running"]);
|
||||
|
||||
let mut active_cmd = context.command();
|
||||
active_cmd.current_dir(&context.temp_dir);
|
||||
active_cmd.args(["start", &run_id]);
|
||||
fabro_snapshot!(context.filters(), active_cmd, @"
|
||||
success: false
|
||||
|
|
@ -207,7 +204,6 @@ fn start_rejects_already_active_or_completed_run() {
|
|||
wait_for_status(&run.run_dir, &["succeeded"]);
|
||||
|
||||
let mut completed_cmd = context.command();
|
||||
completed_cmd.current_dir(&context.temp_dir);
|
||||
completed_cmd.args(["start", &run_id]);
|
||||
fabro_snapshot!(context.filters(), completed_cmd, @"
|
||||
success: false
|
||||
|
|
|
|||
|
|
@ -41,7 +41,10 @@ enum GitWorkflowKind {
|
|||
}
|
||||
|
||||
pub(crate) fn fixture(name: &str) -> PathBuf {
|
||||
Path::new(env!("CARGO_MANIFEST_DIR")).join(format!("../../../test/{name}"))
|
||||
Path::new(env!("CARGO_MANIFEST_DIR"))
|
||||
.join(format!("../../../test/{name}"))
|
||||
.canonicalize()
|
||||
.expect("fixture path should exist")
|
||||
}
|
||||
|
||||
pub(crate) fn output_stderr(output: &Output) -> String {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
use fabro_test::{fabro_snapshot, test_context};
|
||||
|
||||
fn fixture(name: &str) -> std::path::PathBuf {
|
||||
std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join(format!("../../../test/{name}"))
|
||||
std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
|
||||
.join(format!("../../../test/{name}"))
|
||||
.canonicalize()
|
||||
.expect("fixture path should exist")
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -43,7 +46,7 @@ fn simple() {
|
|||
----- stdout -----
|
||||
----- stderr -----
|
||||
Workflow: Simple (4 nodes, 3 edges)
|
||||
Graph: ../../../test/simple.fabro
|
||||
Graph: [FIXTURES]/simple.fabro
|
||||
Validation: OK
|
||||
");
|
||||
}
|
||||
|
|
@ -59,7 +62,7 @@ fn branching() {
|
|||
----- stdout -----
|
||||
----- stderr -----
|
||||
Workflow: Branch (6 nodes, 6 edges)
|
||||
Graph: ../../../test/branching.fabro
|
||||
Graph: [FIXTURES]/branching.fabro
|
||||
warning [node: implement]: Node 'implement' has goal_gate=true but no retry_target or fallback_retry_target (goal_gate_has_retry)
|
||||
Validation: OK
|
||||
");
|
||||
|
|
@ -76,7 +79,7 @@ fn conditions() {
|
|||
----- stdout -----
|
||||
----- stderr -----
|
||||
Workflow: Conditions (5 nodes, 5 edges)
|
||||
Graph: ../../../test/conditions.fabro
|
||||
Graph: [FIXTURES]/conditions.fabro
|
||||
Validation: OK
|
||||
");
|
||||
}
|
||||
|
|
@ -92,7 +95,7 @@ fn parallel() {
|
|||
----- stdout -----
|
||||
----- stderr -----
|
||||
Workflow: Parallel (7 nodes, 7 edges)
|
||||
Graph: ../../../test/parallel.fabro
|
||||
Graph: [FIXTURES]/parallel.fabro
|
||||
Validation: OK
|
||||
");
|
||||
}
|
||||
|
|
@ -108,7 +111,7 @@ fn styled() {
|
|||
----- stdout -----
|
||||
----- stderr -----
|
||||
Workflow: Styled (5 nodes, 4 edges)
|
||||
Graph: ../../../test/styled.fabro
|
||||
Graph: [FIXTURES]/styled.fabro
|
||||
Validation: OK
|
||||
");
|
||||
}
|
||||
|
|
@ -124,7 +127,7 @@ fn legacy_tool() {
|
|||
----- stdout -----
|
||||
----- stderr -----
|
||||
Workflow: LegacyTool (3 nodes, 2 edges)
|
||||
Graph: ../../../test/legacy_tool.fabro
|
||||
Graph: [FIXTURES]/legacy_tool.fabro
|
||||
Validation: OK
|
||||
");
|
||||
}
|
||||
|
|
@ -140,7 +143,7 @@ fn invalid() {
|
|||
----- stdout -----
|
||||
----- stderr -----
|
||||
Workflow: Invalid (2 nodes, 1 edges)
|
||||
Graph: ../../../test/invalid.fabro
|
||||
Graph: [FIXTURES]/invalid.fabro
|
||||
error: Pipeline must have exactly one start node (shape=Mdiamond or id start/Start) (start_node)
|
||||
error [node: exit]: Exit node 'exit' has 1 outgoing edge(s) but must have none (exit_no_outgoing)
|
||||
error: Validation failed
|
||||
|
|
|
|||
|
|
@ -43,7 +43,6 @@ fn list() {
|
|||
|
||||
let mut cmd = context.command();
|
||||
cmd.args(["workflow", "list"]);
|
||||
cmd.current_dir(&context.temp_dir);
|
||||
fabro_snapshot!(context.filters(), cmd, @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
|
|
|
|||
|
|
@ -147,7 +147,6 @@ fn workflow_create_rejects_existing_workflow() {
|
|||
fn workflow_create_errors_without_project_config() {
|
||||
let context = test_context!();
|
||||
let mut cmd = context.command();
|
||||
cmd.current_dir(&context.temp_dir);
|
||||
cmd.args(["workflow", "create", "hello-world"]);
|
||||
|
||||
fabro_snapshot!(context.filters(), cmd, @"
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@ fn help() {
|
|||
fn workflow_list_errors_without_project_config() {
|
||||
let context = test_context!();
|
||||
let mut cmd = context.command();
|
||||
cmd.current_dir(&context.temp_dir);
|
||||
cmd.args(["workflow", "list"]);
|
||||
|
||||
fabro_snapshot!(context.filters(), cmd, @"
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ fn test_exec_creates_file() {
|
|||
"claude-haiku-4-5",
|
||||
"Create a file called hello.txt containing exactly 'Hello from exec scenario'",
|
||||
]);
|
||||
cmd.current_dir(&context.temp_dir);
|
||||
cmd.timeout(Duration::from_secs(120));
|
||||
cmd.assert().success();
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use fabro_test::test_context;
|
|||
use serde_json::Value;
|
||||
|
||||
use super::{fixture, read_json, timeout_for};
|
||||
use crate::support::fabro_json_snapshot;
|
||||
use crate::support::{example_fixture, fabro_json_snapshot};
|
||||
|
||||
#[fabro_macros::e2e_test()]
|
||||
fn local_run_lifecycle() {
|
||||
|
|
@ -112,7 +112,7 @@ fn dry_run_create_start_attach_works_with_default_run_lookup() {
|
|||
"--auto-approve",
|
||||
"--run-id",
|
||||
run_id,
|
||||
"../../../test/simple.fabro",
|
||||
example_fixture("simple.fabro").to_str().unwrap(),
|
||||
])
|
||||
.assert()
|
||||
.success();
|
||||
|
|
@ -155,7 +155,7 @@ fn dry_run_detach_attach_works_with_default_run_lookup() {
|
|||
"--auto-approve",
|
||||
"--run-id",
|
||||
run_id,
|
||||
"../../../test/simple.fabro",
|
||||
example_fixture("simple.fabro").to_str().unwrap(),
|
||||
])
|
||||
.assert()
|
||||
.success();
|
||||
|
|
|
|||
|
|
@ -32,7 +32,10 @@ macro_rules! fabro_json_snapshot {
|
|||
pub(crate) use fabro_json_snapshot;
|
||||
|
||||
pub(crate) fn example_fixture(name: &str) -> PathBuf {
|
||||
Path::new(env!("CARGO_MANIFEST_DIR")).join(format!("../../../test/{name}"))
|
||||
Path::new(env!("CARGO_MANIFEST_DIR"))
|
||||
.join(format!("../../../test/{name}"))
|
||||
.canonicalize()
|
||||
.expect("fixture path should exist")
|
||||
}
|
||||
|
||||
pub(crate) fn read_json(path: impl AsRef<Path>) -> Value {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ fn dry_run_branching() {
|
|||
----- stdout -----
|
||||
----- stderr -----
|
||||
Workflow: Branch (6 nodes, 6 edges)
|
||||
Graph: ../../../test/branching.fabro
|
||||
Graph: [FIXTURES]/branching.fabro
|
||||
Goal: Implement and validate a feature
|
||||
|
||||
warning [node: implement]: Node 'implement' has goal_gate=true but no retry_target or fallback_retry_target (goal_gate_has_retry)
|
||||
|
|
@ -49,7 +49,7 @@ fn dry_run_conditions() {
|
|||
----- stdout -----
|
||||
----- stderr -----
|
||||
Workflow: Conditions (5 nodes, 5 edges)
|
||||
Graph: ../../../test/conditions.fabro
|
||||
Graph: [FIXTURES]/conditions.fabro
|
||||
Goal: Test condition evaluation with OR and parentheses
|
||||
|
||||
Sandbox: local (ready in [TIME])
|
||||
|
|
@ -81,7 +81,7 @@ fn dry_run_parallel() {
|
|||
----- stdout -----
|
||||
----- stderr -----
|
||||
Workflow: Parallel (7 nodes, 7 edges)
|
||||
Graph: ../../../test/parallel.fabro
|
||||
Graph: [FIXTURES]/parallel.fabro
|
||||
Goal: Test parallel and fan-in execution
|
||||
|
||||
Sandbox: local (ready in [TIME])
|
||||
|
|
@ -114,7 +114,7 @@ fn dry_run_styled() {
|
|||
----- stdout -----
|
||||
----- stderr -----
|
||||
Workflow: Styled (5 nodes, 4 edges)
|
||||
Graph: ../../../test/styled.fabro
|
||||
Graph: [FIXTURES]/styled.fabro
|
||||
Goal: Build a styled pipeline
|
||||
|
||||
Sandbox: local (ready in [TIME])
|
||||
|
|
@ -147,7 +147,7 @@ fn dry_run_legacy_tool() {
|
|||
----- stdout -----
|
||||
----- stderr -----
|
||||
Workflow: LegacyTool (3 nodes, 2 edges)
|
||||
Graph: ../../../test/legacy_tool.fabro
|
||||
Graph: [FIXTURES]/legacy_tool.fabro
|
||||
Goal: Verify backwards compatibility with old tool naming
|
||||
|
||||
Sandbox: local (ready in [TIME])
|
||||
|
|
|
|||
|
|
@ -1,9 +1,21 @@
|
|||
use std::path::PathBuf;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Output;
|
||||
|
||||
use assert_cmd::Command;
|
||||
use regex::Regex;
|
||||
|
||||
/// Walk up from `start` to find the repo-level `test/` fixtures directory.
|
||||
pub fn find_test_fixtures_dir(start: &Path) -> Option<PathBuf> {
|
||||
let mut dir = start;
|
||||
loop {
|
||||
let candidate = dir.join("test");
|
||||
if candidate.is_dir() {
|
||||
return candidate.canonicalize().ok();
|
||||
}
|
||||
dir = dir.parent()?;
|
||||
}
|
||||
}
|
||||
|
||||
/// Static filters applied to every snapshot.
|
||||
static INSTA_FILTERS: &[(&str, &str)] = &[
|
||||
(r"fabro \d+\.\d+\.\d+", "fabro [VERSION]"),
|
||||
|
|
@ -127,6 +139,12 @@ impl TestContext {
|
|||
}
|
||||
}
|
||||
|
||||
/// Register a custom filter (regex pattern → replacement).
|
||||
pub fn add_filter(&mut self, pattern: &str, replacement: &str) {
|
||||
self.filters
|
||||
.push((regex::escape(pattern), replacement.to_string()));
|
||||
}
|
||||
|
||||
/// Returns the combined static + context-specific filters.
|
||||
pub fn filters(&self) -> Vec<(String, String)> {
|
||||
let mut filters = self.filters.clone();
|
||||
|
|
@ -139,8 +157,14 @@ impl TestContext {
|
|||
}
|
||||
|
||||
/// Build a base `Command` with all isolation env vars set.
|
||||
///
|
||||
/// The working directory defaults to `self.temp_dir` (a non-git temp
|
||||
/// directory) so tests never accidentally interact with the real repo.
|
||||
/// Tests that need a specific working directory can override this with
|
||||
/// a subsequent `.current_dir(path)` call.
|
||||
pub fn command(&self) -> Command {
|
||||
let mut cmd = Command::new(&self.fabro_bin);
|
||||
cmd.current_dir(&self.temp_dir);
|
||||
cmd.env("NO_COLOR", "1");
|
||||
cmd.env("HOME", &self.home_dir);
|
||||
cmd.env("FABRO_NO_UPGRADE_CHECK", "true");
|
||||
|
|
@ -399,11 +423,21 @@ pub fn apply_filters(snapshot: &str, filters: &[(String, String)]) -> String {
|
|||
}
|
||||
|
||||
/// Create a `TestContext` using the `fabro` binary built by cargo.
|
||||
///
|
||||
/// Automatically registers a `[FIXTURES]` snapshot filter for the `test/`
|
||||
/// directory at the repository root (found by walking up from `CARGO_MANIFEST_DIR`).
|
||||
#[macro_export]
|
||||
macro_rules! test_context {
|
||||
() => {
|
||||
$crate::TestContext::new(std::path::PathBuf::from(env!("CARGO_BIN_EXE_fabro")))
|
||||
};
|
||||
() => {{
|
||||
let mut ctx =
|
||||
$crate::TestContext::new(std::path::PathBuf::from(env!("CARGO_BIN_EXE_fabro")));
|
||||
if let Some(fixtures_dir) =
|
||||
$crate::find_test_fixtures_dir(std::path::Path::new(env!("CARGO_MANIFEST_DIR")))
|
||||
{
|
||||
ctx.add_filter(fixtures_dir.to_str().unwrap(), "[FIXTURES]");
|
||||
}
|
||||
ctx
|
||||
}};
|
||||
}
|
||||
|
||||
/// Snapshot test macro that runs a command and compares output using insta.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue