Skip to content

Commit a21144d

Browse files
fix(cron): preserve enabled-with-defaults failure alert through store roundtrip (fixes #96589) (AI-assisted) (#96615)
Summary: - The PR preserves `failure_alert_disabled === 0` as the enabled-with-defaults failure-alert state and adds focused codec roundtrip tests. - PR surface: Source +2, Tests +54. Total +56 across 2 files. - Reproducibility: yes. At source level, current main encodes `failureAlert: {}` with `failure_alert_disabled = 0`, then decodes it as `undefined` when all explicit alert option columns are null. Automerge notes: - No ClawSweeper repair was needed after automerge opt-in. Validation: - ClawSweeper review passed for head bd9b2a1. - Required merge gates passed before the squash merge. Prepared head SHA: bd9b2a1 Review: #96615 (comment) Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com> Co-authored-by: liuhao1024 <[email protected]> Approved-by: takhoffman
1 parent 8a5cb85 commit a21144d

2 files changed

Lines changed: 57 additions & 1 deletion

File tree

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Unit tests for failure-alert SQLite column codec roundtrip.
2+
import { describe, expect, it } from "vitest";
3+
import { bindFailureAlertColumns, failureAlertFromRow } from "./failure-alert-codec.js";
4+
import type { CronJobRow } from "./schema.js";
5+
6+
function roundtrip(
7+
input: Parameters<typeof bindFailureAlertColumns>[0],
8+
): ReturnType<typeof failureAlertFromRow> {
9+
const columns = bindFailureAlertColumns(input);
10+
return failureAlertFromRow(columns as CronJobRow);
11+
}
12+
13+
describe("failureAlertFromRow", () => {
14+
it("round-trips disabled config (false)", () => {
15+
expect(roundtrip(false)).toBe(false);
16+
});
17+
18+
it("round-trips undefined (no alert config) as undefined", () => {
19+
expect(roundtrip(undefined)).toBeUndefined();
20+
});
21+
22+
it("round-trips enabled-with-defaults ({}) as {}", () => {
23+
const result = roundtrip({});
24+
expect(result).toEqual({});
25+
});
26+
27+
it("round-trips populated config with all fields", () => {
28+
const config = {
29+
after: 3,
30+
cooldownMs: 120_000,
31+
channel: "telegram" as const,
32+
to: "@user",
33+
mode: "announce" as const,
34+
accountId: "acc-1",
35+
includeSkipped: true,
36+
};
37+
expect(roundtrip(config)).toEqual(config);
38+
});
39+
40+
it("round-trips partial config (only after)", () => {
41+
expect(roundtrip({ after: 5 })).toEqual({ after: 5 });
42+
});
43+
44+
it("enabled-with-defaults does not collapse to undefined on read", () => {
45+
const columns = bindFailureAlertColumns({});
46+
const row = columns as CronJobRow;
47+
expect(row.failure_alert_disabled).toBe(0);
48+
expect(row.failure_alert_after).toBeNull();
49+
const decoded = failureAlertFromRow(row);
50+
expect(decoded).toEqual({});
51+
expect(decoded).not.toBeUndefined();
52+
expect(decoded).toBeTruthy();
53+
});
54+
});

src/cron/store/failure-alert-codec.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,16 @@ export function failureAlertFromRow(row: CronJobRow): CronFailureAlert | false |
4646
if (row.failure_alert_disabled === 1) {
4747
return false;
4848
}
49+
const failureAlertExplicitlyEnabled = row.failure_alert_disabled === 0;
4950
if (
5051
row.failure_alert_after == null &&
5152
!row.failure_alert_channel &&
5253
!row.failure_alert_to &&
5354
row.failure_alert_cooldown_ms == null &&
5455
row.failure_alert_include_skipped == null &&
5556
!row.failure_alert_mode &&
56-
!row.failure_alert_account_id
57+
!row.failure_alert_account_id &&
58+
!failureAlertExplicitlyEnabled
5759
) {
5860
return undefined;
5961
}

0 commit comments

Comments
 (0)