From 5f2293643771bf1b6fd549287c2df8adfcbb233a Mon Sep 17 00:00:00 2001 From: Scott Werner Date: Thu, 1 Oct 2026 13:10:13 -0400 Subject: [PATCH] Expect checkpoint commits from a retried local run Retry now starts the workflow over, and a local-folder run commits its checkpoints, so the retry scenario checks that every stage commits again under the retry's run id. The scenario for retrying without Git checkpoints goes: local runs have them now, and the retry scenario covers starting over. Co-Authored-By: Claude Opus 5.5 --- lib/apps/fabro-cli/tests/it/scenario/petri.rs | 40 ------------------- .../fabro-cli/tests/it/scenario/petri_fork.rs | 28 ++++++++++++- 2 files changed, 26 insertions(+), 42 deletions(-) diff --git a/lib/apps/fabro-cli/tests/it/scenario/petri.rs b/lib/apps/fabro-cli/tests/it/scenario/petri.rs index 4e3c53bf0..7beb2476c 100644 --- a/lib/apps/fabro-cli/tests/it/scenario/petri.rs +++ b/lib/apps/fabro-cli/tests/it/scenario/petri.rs @@ -1792,43 +1792,3 @@ async fn built_in_host_runs_and_prunes_without_plugins() { assert!(!scope.exists(), "prune removed the managed Host workspace"); server.shutdown(); } - -/// Retry works without a Git checkpoint and starts the workflow again. -#[tokio::test(flavor = "multi_thread")] -async fn a_retry_without_git_checkpoints_starts_the_workflow_again() { - let context = test_context!(); - let server = RunningServer::start().await; - let counter = context.temp_dir.join("retry-runs.txt"); - let workspace = write_petri_workflow( - &context, - &format!( - r#"digraph Retry {{ - graph [goal="Retry from the start", default_max_retries=0] - start [shape=Mdiamond] - write [shape=parallelogram, script="echo run >> '{}'", goal_gate=true] - exit [shape=Msquare] - start -> write -> exit - }}"#, - counter.display() - ), - ); - let original = run_detached(&context, &server, &workspace); - wait_for_success(&server, &original).await; - assert!(server.checkpoints(&original).await.is_empty()); - let response = fabro_test::test_http_client() - .post(format!( - "{}/api/v1/runs/{original}/retry", - server.api_base_url - )) - .bearer_auth(TEST_DEV_TOKEN) - .send() - .await - .unwrap(); - let retried = expect_reqwest_json(response, fabro_http::StatusCode::CREATED, "retry").await; - let run_id = retried["id"].as_str().expect("the retry run id"); - assert_ne!(run_id, original); - wait_for_success(&server, run_id).await; - assert_eq!(std::fs::read_to_string(counter).unwrap(), "run\nrun\n"); - assert!(server.checkpoints(run_id).await.is_empty()); - server.shutdown(); -} diff --git a/lib/apps/fabro-cli/tests/it/scenario/petri_fork.rs b/lib/apps/fabro-cli/tests/it/scenario/petri_fork.rs index efe8d7f70..6e3459a96 100644 --- a/lib/apps/fabro-cli/tests/it/scenario/petri_fork.rs +++ b/lib/apps/fabro-cli/tests/it/scenario/petri_fork.rs @@ -13,7 +13,7 @@ )] use std::path::{Path, PathBuf}; -use std::process::Output; +use std::process::{Command, Output}; use fabro_test::test_context; @@ -133,6 +133,24 @@ fn workspace(server: &RunningServer, run_id: &str) -> PathBuf { workspaces.remove(0) } +/// The subjects of the commits on the workspace's branch, oldest first. +fn commit_subjects(workspace: &Path) -> Vec { + let output = Command::new("git") + .args(["log", "--reverse", "--format=%s"]) + .current_dir(workspace) + .output() + .expect("git runs"); + assert!( + output.status.success(), + "git log failed: {}", + String::from_utf8_lossy(&output.stderr) + ); + String::from_utf8_lossy(&output.stdout) + .lines() + .map(ToOwned::to_owned) + .collect() +} + fn read(workspace: &Path, name: &str) -> String { std::fs::read_to_string(workspace.join(name)) .unwrap_or_else(|err| panic!("{name} in {}: {err}", workspace.display())) @@ -188,7 +206,13 @@ async fn a_retry_starts_over_and_succeeds_when_the_failure_was_transient() { assert_eq!(read(&retry_workspace, "one.txt"), "one\n"); assert_eq!(read(&retry_workspace, "flaky.txt"), "flaky\n"); assert_eq!(read(&retry_workspace, "three.txt"), "three\n"); - assert!(!retry_workspace.join(".git").exists()); + assert_eq!(commit_subjects(&retry_workspace), [ + format!("fabro({retry}): start (success)"), + format!("fabro({retry}): one (success)"), + format!("fabro({retry}): flaky (success)"), + format!("fabro({retry}): three (success)"), + format!("fabro({retry}): exit (success)"), + ]); let state = run_json(&server, &format!("runs/{retry}/state")).await; assert_eq!(state["retried_from"], source); assert!(state["spec"]["fork_source_ref"].is_null());