CodeIssuesDiscussionsWikiPull RequestsProjectsCommitsActionsReleasesContributorsPulse● GatesSecuritySettingsDeploymentsPipelineInsightsAgents✨ Explain✨ Ask AI✨ Workspace✨ Spec✨ Tests▓ Debt Map✨ NL Search🏛 Archaeology
Blame · Line-by-line history
ai-incident.test.ts
Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.
| 1e162a8 | 1 | /** |
| 2 | * Block D4 — AI Incident Responder tests. | |
| 3 | * | |
| 4 | * These tests exercise the pure helper (no I/O) and confirm that | |
| 5 | * `onDeployFailure` degrades gracefully when the repository does not exist | |
| 6 | * (or the DB is unavailable) without ever throwing. | |
| 7 | */ | |
| 8 | ||
| 9 | import { describe, it, expect } from "bun:test"; | |
| 10 | import { | |
| 11 | onDeployFailure, | |
| 12 | summariseCommitsForIncident, | |
| 13 | } from "../lib/ai-incident"; | |
| 14 | ||
| 15 | describe("lib/ai-incident — module shape", () => { | |
| 16 | it("imports cleanly and exports the expected functions", () => { | |
| 17 | expect(typeof summariseCommitsForIncident).toBe("function"); | |
| 18 | expect(typeof onDeployFailure).toBe("function"); | |
| 19 | }); | |
| 20 | }); | |
| 21 | ||
| 22 | describe("lib/ai-incident — summariseCommitsForIncident", () => { | |
| 23 | it("formats each commit as `- <sha7> <subject> — <author>`", () => { | |
| 24 | const out = summariseCommitsForIncident([ | |
| 25 | { sha: "abcdef1234567890", message: "fix: handle null", author: "alice" }, | |
| 26 | { sha: "fedcba0987654321", message: "feat: new thing", author: "bob" }, | |
| 27 | ]); | |
| 28 | expect(out).toBe( | |
| 29 | "- abcdef1 fix: handle null — alice\n" + | |
| 30 | "- fedcba0 feat: new thing — bob" | |
| 31 | ); | |
| 32 | }); | |
| 33 | ||
| 34 | it("keeps only the first line of multi-line commit messages", () => { | |
| 35 | const out = summariseCommitsForIncident([ | |
| 36 | { | |
| 37 | sha: "1111111aaaaaaa", | |
| 38 | message: "first line\n\nbody goes here", | |
| 39 | author: "carol", | |
| 40 | }, | |
| 41 | ]); | |
| 42 | expect(out).toBe("- 1111111 first line — carol"); | |
| 43 | }); | |
| 44 | ||
| 45 | it("handles an empty list as an empty string", () => { | |
| 46 | expect(summariseCommitsForIncident([])).toBe(""); | |
| 47 | }); | |
| 48 | ||
| 49 | it("tolerates missing fields without throwing", () => { | |
| 50 | const out = summariseCommitsForIncident([ | |
| 51 | { sha: "", message: "", author: "" }, | |
| 52 | ]); | |
| 53 | // Expect a renderable line even when fields are blank. | |
| 54 | expect(out).toContain(" — "); | |
| 55 | }); | |
| 56 | }); | |
| 57 | ||
| 58 | describe("lib/ai-incident — onDeployFailure", () => { | |
| 59 | it("returns { issueNumber: null, reason: <non-empty> } for an unknown repositoryId without throwing", async () => { | |
| 60 | const result = await onDeployFailure({ | |
| 61 | repositoryId: "00000000-0000-0000-0000-000000000000", | |
| 62 | deploymentId: "00000000-0000-0000-0000-000000000000", | |
| 63 | ref: "refs/heads/main", | |
| 64 | commitSha: "0".repeat(40), | |
| 9ecf5a4 | 65 | target: "vapron", |
| 1e162a8 | 66 | errorMessage: "HTTP 500", |
| 67 | }); | |
| 68 | expect(result).toBeDefined(); | |
| 69 | expect(result.issueNumber).toBeNull(); | |
| 70 | expect(typeof result.reason).toBe("string"); | |
| 71 | expect(result.reason.length).toBeGreaterThan(0); | |
| 72 | }); | |
| 73 | ||
| 74 | it("never throws even when all optional fields are missing", async () => { | |
| 75 | await expect( | |
| 76 | onDeployFailure({ | |
| 77 | repositoryId: "00000000-0000-0000-0000-000000000000", | |
| 78 | deploymentId: "00000000-0000-0000-0000-000000000000", | |
| 79 | }) | |
| 80 | ).resolves.toBeDefined(); | |
| 81 | }); | |
| 82 | }); |