mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-10 03:30:59 +00:00
fix(web): cover new archived RunStatus in Files empty-state taxonomy
origin/main introduced a new `archived` lifecycle status as a terminal state reached by explicit user action on a previously terminal run. deriveEmptyKind didn't know about it — archived runs with files would have rendered "The diff for this run is no longer available. If you expect files here, please report it.", which is wrong; the diff was captured normally, the run was just archived later. Adds `archived` to the terminal-success branch so archived runs show the correct empty-state copy (R4b or R4c2) based on total_changed, same as a succeeded run. The regression-guard test is also tightened: it now iterates over `RunStatus` from @qltysh/fabro-api-client rather than a hand- maintained list, so any future addition to the OpenAPI spec fails this test until the decision table grows a branch. This exact class of silent-regression is what made me miss archived in the first place. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
1f6eb5b336
commit
3435c02dca
4 changed files with 17 additions and 27 deletions
|
|
@ -1,6 +1,7 @@
|
|||
import { describe, expect, test } from "bun:test";
|
||||
import TestRenderer from "react-test-renderer";
|
||||
|
||||
import { RunStatus } from "@qltysh/fabro-api-client";
|
||||
import {
|
||||
deriveEmptyKind,
|
||||
emptyStateCopy,
|
||||
|
|
@ -59,7 +60,7 @@ describe("deriveEmptyKind", () => {
|
|||
|
||||
// Terminal-success + teardown states → R4(b) or R4(c2) depending on
|
||||
// whether files were ever changed
|
||||
test.each(["succeeded", "removing"])(
|
||||
test.each(["succeeded", "removing", "archived"])(
|
||||
"%s with changes but no data is R4(c2) 'diff_lost'",
|
||||
(status) => {
|
||||
expect(
|
||||
|
|
@ -72,7 +73,7 @@ describe("deriveEmptyKind", () => {
|
|||
},
|
||||
);
|
||||
|
||||
test.each(["succeeded", "removing"])(
|
||||
test.each(["succeeded", "removing", "archived"])(
|
||||
"%s with no changes is R4(b)",
|
||||
(status) => {
|
||||
expect(
|
||||
|
|
@ -105,24 +106,13 @@ describe("deriveEmptyKind", () => {
|
|||
).toBe("unknown");
|
||||
});
|
||||
|
||||
test("every documented RunStatus gets a non-unknown empty kind when applicable", () => {
|
||||
// Regression guard: if a new RunStatus appears in
|
||||
// `apps/fabro-web/app/data/runs.ts` without a matching branch here,
|
||||
// the decision table silently returns "unknown" ("not available right
|
||||
// now") — misleading copy for a cancelled or paused run.
|
||||
const knownRunStatuses = [
|
||||
"submitted",
|
||||
"queued",
|
||||
"starting",
|
||||
"running",
|
||||
"blocked",
|
||||
"paused",
|
||||
"removing",
|
||||
"succeeded",
|
||||
"failed",
|
||||
"dead",
|
||||
];
|
||||
for (const status of knownRunStatuses) {
|
||||
test("every documented RunStatus gets a non-unknown empty kind", () => {
|
||||
// Regression guard sourced from the generated API client enum so any
|
||||
// new RunStatus added to the OpenAPI spec fails this test until the
|
||||
// decision table grows a branch. Without this guard, unhandled
|
||||
// statuses silently render as "unknown" ("not available right now") —
|
||||
// misleading copy for e.g. a paused or archived run.
|
||||
for (const status of Object.values(RunStatus)) {
|
||||
const result = deriveEmptyKind({
|
||||
runStatus: status,
|
||||
totalChanged: 0,
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ export function emptyStateCopy(kind: EmptyKind): string {
|
|||
///
|
||||
/// The full RunStatus enum (per fabro-types/src/status.rs) is:
|
||||
/// submitted, queued, starting, running, blocked, paused, removing,
|
||||
/// succeeded, failed, dead
|
||||
/// succeeded, failed, dead, archived
|
||||
/// partial_success is a stage status, not a run status.
|
||||
export function deriveEmptyKind(args: {
|
||||
runStatus: string | undefined;
|
||||
|
|
@ -81,10 +81,10 @@ export function deriveEmptyKind(args: {
|
|||
return degraded ? "unknown" : "failed_before_checkpoint";
|
||||
}
|
||||
|
||||
// Terminal-success + teardown states: the run ran to completion or is
|
||||
// shutting down. Distinguish "succeeded with no changes" (R4b) from
|
||||
// "succeeded but diff lost" (R4c2) via total_changed.
|
||||
if (s === "succeeded" || s === "removing") {
|
||||
// Terminal-success, teardown, and archive states: the run ran to
|
||||
// completion (possibly long ago). Distinguish "had no changes" (R4b)
|
||||
// from "diff captured then lost" (R4c2) via total_changed.
|
||||
if (s === "succeeded" || s === "removing" || s === "archived") {
|
||||
if (degraded) {
|
||||
// We have a patch; the component renders PatchDiff instead of an
|
||||
// empty state. Shouldn't reach here in practice.
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
2
lib/crates/fabro-spa/assets/index.html
generated
2
lib/crates/fabro-spa/assets/index.html
generated
|
|
@ -61,7 +61,7 @@
|
|||
<script type="module" src="/assets/chunk-sadshphz.js"></script>
|
||||
<script type="module" src="/assets/chunk-pmthkscp.js"></script>
|
||||
<script type="module" src="/assets/chunk-v61ks9f7.js"></script>
|
||||
<script type="module" src="/assets/entry-fqcfxfyb.js"></script>
|
||||
<script type="module" src="/assets/entry-pmqz8d3n.js"></script>
|
||||
<script type="module" src="/assets/chunk-n1k68xa8.js"></script>
|
||||
<script type="module" src="/assets/chunk-rsph5pvm.js"></script>
|
||||
<script type="module" src="/assets/chunk-9t57pdty.js"></script>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue