Improve test coverage for CI run lifecycle
Exclude mirror.rs from coverage (module is going away). Add tests for
dot-prefixed entry filtering in scan_orphans and pre-stamped
started_at preservation in transitions. Refactor test match arms to
let-else with unreachable! (already excluded by coverage config).
Mark untestable defensive branches with cov-excl-line.

Assisted-by: GLM-5.1 via pi
change mopoqkpxukpwvxkwpzpvprwvzxrntyys
commit 43d3f7daf5189e47d4a7d5b38b4cc7c4097ee5f0
author Alpha Chen <alpha@kejadlen.dev>
date
parent xspxsmkt
diff --git a/src/ci/run.rs b/src/ci/run.rs
index 46a91e7..83314a4 100644
--- a/src/ci/run.rs
+++ b/src/ci/run.rs
@@ -113,14 +113,14 @@ impl Runs {
             let entries = match fs_err::read_dir(&state_path) {
                 Ok(entries) => entries,
                 Err(e) if e.kind() == std::io::ErrorKind::NotFound => continue,
-                Err(e) => return Err(e.into()),
+                Err(e) => return Err(e.into()), // cov-excl-line
             };
 
             for entry in entries {
                 let entry = entry?;
                 let name = match entry.file_name().to_str() {
                     Some(n) => n.to_string(),
-                    None => continue,
+                    None => continue, // cov-excl-line
                 };
 
                 if name.starts_with('.') {
@@ -163,9 +163,9 @@ impl Runs {
     pub fn reconcile_orphans(&self) -> Result<()> {
         let orphans = self.scan_orphans()?;
         for orphan in &orphans {
-            tracing::warn!(
-                run_id = %orphan.id(),
-                state = ?orphan.state(),
+            tracing::warn!( // cov-excl-line
+                run_id = %orphan.id(), // cov-excl-line
+                state = ?orphan.state(), // cov-excl-line
                 "found orphaned run"
             );
         }
@@ -173,33 +173,33 @@ impl Runs {
         for mut orphan in orphans {
             match orphan.state() {
                 RunState::Pending => {
-                    tracing::warn!(
-                        run_id = %orphan.id(),
+                    tracing::warn!( // cov-excl-line
+                        run_id = %orphan.id(), // cov-excl-line
                         "completing orphaned pending run"
                     );
                     if let Err(e) = orphan.transition(RunState::Complete) {
-                        tracing::error!(
-                            run_id = %orphan.id(),
+                        tracing::error!( // cov-excl-line
+                            run_id = %orphan.id(), // cov-excl-line
                             %e,
                             "failed to transition orphaned pending run"
                         );
                     }
                 }
                 RunState::Active => {
-                    tracing::warn!(
-                        run_id = %orphan.id(),
+                    tracing::warn!( // cov-excl-line
+                        run_id = %orphan.id(), // cov-excl-line
                         "marking orphaned active run as failed"
                     );
                     if let Err(e) = orphan.transition(RunState::Failed) {
-                        tracing::error!(
-                            run_id = %orphan.id(),
+                        tracing::error!( // cov-excl-line
+                            run_id = %orphan.id(), // cov-excl-line
                             %e,
                             "failed to transition orphaned active run to failed"
                         );
                     }
                 }
                 RunState::Complete | RunState::Failed => {
-                    unreachable!("scan_orphans only returns pending/active")
+                    unreachable!("scan_orphans only returns pending/active") // cov-excl-line
                 }
             }
         }
@@ -346,7 +346,7 @@ impl Run {
             RunState::Complete | RunState::Failed if times.finished_at.is_none() => {
                 times.finished_at = Some(now)
             }
-            _ => {}
+            _ => {} // cov-excl-line
         }
         self.write_times(&times)?;
         Ok(())
@@ -531,6 +531,26 @@ mod tests {
         assert_eq!(complete_times.started_at, started, "started_at preserved");
     }
 
+    #[test]
+    fn transition_keeps_existing_started_at() {
+        let (_dir, quire) = tmp_quire();
+        let runs = test_runs(&quire);
+        let mut run = runs.create(&test_meta()).expect("create");
+
+        // Pre-stamp started_at before transitioning to Active.
+        let pre: Timestamp = "2026-04-28T12:00:00Z".parse().unwrap();
+        run.write_times(&RunTimes {
+            started_at: Some(pre),
+            finished_at: None,
+        })
+        .expect("write times");
+
+        run.transition(RunState::Active).expect("to active");
+
+        let times = run.read_times().expect("read times");
+        assert_eq!(times.started_at, Some(pre), "should keep pre-set started_at");
+    }
+
     #[test]
     fn transition_full_lifecycle() {
         let (_dir, quire) = tmp_quire();
@@ -619,6 +639,21 @@ mod tests {
         assert!(runs.scan_orphans().expect("scan").is_empty());
     }
 
+    #[test]
+    fn scan_orphans_skips_dot_prefixed_entries() {
+        let (_dir, quire) = tmp_quire();
+        let runs = test_runs(&quire);
+        let run = runs.create(&test_meta()).expect("create");
+
+        // Drop a dot-prefixed directory into pending/ alongside the real run.
+        let pending_dir = runs.base.join(RunState::Pending.dir_name());
+        fs_err::create_dir_all(pending_dir.join(".tmp-stale")).expect("mkdir dot");
+
+        let orphans = runs.scan_orphans().expect("scan");
+        assert_eq!(orphans.len(), 1);
+        assert_eq!(orphans[0].id(), run.id());
+    }
+
     #[test]
     fn write_times_updates_in_place() {
         let (_dir, quire) = tmp_quire();
@@ -808,17 +843,13 @@ mod tests {
         let err = run
             .execute(pipeline, HashMap::new())
             .expect_err("expected failure");
-        match err {
-            Error::JobFailed { job, source } => {
-                assert_eq!(job, "grab");
-                let msg = source.to_string();
-                assert!(
-                    msg.contains("not in transitive inputs") && msg.contains("nope"),
-                    "expected 'not in transitive inputs' error, got: {msg}"
-                );
-            }
-            other => panic!("expected JobFailed, got: {other:?}"),
-        }
+        let Error::JobFailed { job, source } = err else { unreachable!() };
+        assert_eq!(job, "grab");
+        let msg = source.to_string();
+        assert!(
+            msg.contains("not in transitive inputs") && msg.contains("nope"),
+            "expected 'not in transitive inputs' error, got: {msg}"
+        );
     }
 
     #[test]
@@ -837,16 +868,12 @@ mod tests {
         let err = run
             .execute(pipeline, HashMap::new())
             .expect_err("expected failure");
-        match err {
-            Error::JobFailed { source, .. } => {
-                let msg = source.to_string();
-                assert!(
-                    msg.contains("not in transitive inputs") && msg.contains("peer"),
-                    "expected non-ancestor error, got: {msg}"
-                );
-            }
-            other => panic!("expected JobFailed, got: {other:?}"),
-        }
+        let Error::JobFailed { source, .. } = err else { unreachable!() };
+        let msg = source.to_string();
+        assert!(
+            msg.contains("not in transitive inputs") && msg.contains("peer"),
+            "expected non-ancestor error, got: {msg}"
+        );
     }
 
     #[test]
@@ -863,16 +890,12 @@ mod tests {
         let err = run
             .execute(pipeline, HashMap::new())
             .expect_err("expected failure");
-        match err {
-            Error::JobFailed { source, .. } => {
-                let msg = source.to_string();
-                assert!(
-                    msg.contains("cannot read its own outputs"),
-                    "expected self-lookup error, got: {msg}"
-                );
-            }
-            other => panic!("expected JobFailed, got: {other:?}"),
-        }
+        let Error::JobFailed { source, .. } = err else { unreachable!() };
+        let msg = source.to_string();
+        assert!(
+            msg.contains("cannot read its own outputs"),
+            "expected self-lookup error, got: {msg}"
+        );
     }
 
     #[test]
diff --git a/src/mirror.rs b/src/mirror.rs
index 145898a..ee86562 100644
--- a/src/mirror.rs
+++ b/src/mirror.rs
@@ -1,3 +1,4 @@
+// cov-excl-start
 //! Mirror push: replicate ref updates to a configured remote.
 
 use crate::Quire;
@@ -414,3 +415,4 @@ mod tests {
         String::from_utf8(output.stdout).unwrap().trim().to_string()
     }
 }
+// cov-excl-stop