From 2fc82cf4d893c7752d8d676bb3fe2a6eb1d59bec Mon Sep 17 00:00:00 2001 From: GhostieMostie Date: Sun, 24 May 2026 00:49:15 -0700 Subject: [PATCH 1/2] test: add infra stage parser coverage --- infra/package.json | 7 +++++-- infra/sst.config.ts | 13 +++---------- infra/stage.test.ts | 20 ++++++++++++++++++++ infra/stage.ts | 15 +++++++++++++++ pnpm-lock.yaml | 3 +++ 5 files changed, 46 insertions(+), 12 deletions(-) create mode 100644 infra/stage.test.ts create mode 100644 infra/stage.ts diff --git a/infra/package.json b/infra/package.json index 0ad762a12fc..1d1844a487e 100644 --- a/infra/package.json +++ b/infra/package.json @@ -1,10 +1,13 @@ { "name": "@cap/infra", + "type": "module", "devDependencies": { - "sst": "3.17.14" + "sst": "3.17.14", + "vitest": "~2.1.9" }, "scripts": { - "sst": "sst" + "sst": "sst", + "test": "vitest run" }, "dependencies": { "@pulumi/github": "^6.7.0", diff --git a/infra/sst.config.ts b/infra/sst.config.ts index c148670f801..3f2c044a971 100644 --- a/infra/sst.config.ts +++ b/infra/sst.config.ts @@ -1,5 +1,7 @@ /// +import { parseStageName } from "./stage"; + const GITHUB_ORG = "CapSoftware"; const _GITHUB_REPO = "Cap"; const _GITHUB_APP_ID = "1196731"; @@ -12,16 +14,7 @@ const _CLOUDFLARE_ACCOUNT_ID = "3de2dd633194481d80f68f55257bdbaa"; const AXIOM_API_TOKEN = "xaat-c0704be6-e942-4935-b068-3b491d7cc00f"; const AXIOM_DATASET = "cap-otel"; -const parsedStage = () => { - if ($app.stage === "staging") return { variant: "staging" } as const; - if ($app.stage === "production") return { variant: "production" } as const; - if ($app.stage.startsWith("git-branch-")) - return { - variant: "git-branch", - branch: $app.stage.slice("git-branch-".length), - } as const; - throw new Error("Unsupported stage"); -}; +const parsedStage = () => parseStageName($app.stage); export default $config({ app(input) { diff --git a/infra/stage.test.ts b/infra/stage.test.ts new file mode 100644 index 00000000000..0eaaa5a60c0 --- /dev/null +++ b/infra/stage.test.ts @@ -0,0 +1,20 @@ +import { describe, expect, it } from "vitest"; +import { parseStageName } from "./stage"; + +describe("parseStageName", () => { + it("recognizes fixed deployment stages", () => { + expect(parseStageName("staging")).toEqual({ variant: "staging" }); + expect(parseStageName("production")).toEqual({ variant: "production" }); + }); + + it("extracts branch names from git branch preview stages", () => { + expect(parseStageName("git-branch-add-infra-tests")).toEqual({ + variant: "git-branch", + branch: "add-infra-tests", + }); + }); + + it("rejects unsupported stages", () => { + expect(() => parseStageName("preview")).toThrow("Unsupported stage"); + }); +}); diff --git a/infra/stage.ts b/infra/stage.ts new file mode 100644 index 00000000000..c129ba4333f --- /dev/null +++ b/infra/stage.ts @@ -0,0 +1,15 @@ +export type ParsedStage = + | { variant: "staging" } + | { variant: "production" } + | { variant: "git-branch"; branch: string }; + +export function parseStageName(stage: string): ParsedStage { + if (stage === "staging") return { variant: "staging" }; + if (stage === "production") return { variant: "production" }; + if (stage.startsWith("git-branch-")) + return { + variant: "git-branch", + branch: stage.slice("git-branch-".length), + }; + throw new Error("Unsupported stage"); +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2810356dc57..3991e7810d1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -918,6 +918,9 @@ importers: sst: specifier: 3.17.14 version: 3.17.14 + vitest: + specifier: ~2.1.9 + version: 2.1.9(@types/node@22.15.17)(jsdom@26.1.0)(lightningcss@1.32.0)(terser@5.44.0) packages/config: dependencies: From f70b8ca2b5c93a61b720997feafd63125dc0f3c3 Mon Sep 17 00:00:00 2001 From: GhostieMostie Date: Sun, 24 May 2026 00:56:51 -0700 Subject: [PATCH 2/2] test: reject empty infra branch stages --- infra/stage.test.ts | 1 + infra/stage.ts | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/infra/stage.test.ts b/infra/stage.test.ts index 0eaaa5a60c0..ceb1e63f45d 100644 --- a/infra/stage.test.ts +++ b/infra/stage.test.ts @@ -16,5 +16,6 @@ describe("parseStageName", () => { it("rejects unsupported stages", () => { expect(() => parseStageName("preview")).toThrow("Unsupported stage"); + expect(() => parseStageName("git-branch-")).toThrow("Unsupported stage"); }); }); diff --git a/infra/stage.ts b/infra/stage.ts index c129ba4333f..e0766f5fc48 100644 --- a/infra/stage.ts +++ b/infra/stage.ts @@ -6,10 +6,10 @@ export type ParsedStage = export function parseStageName(stage: string): ParsedStage { if (stage === "staging") return { variant: "staging" }; if (stage === "production") return { variant: "production" }; - if (stage.startsWith("git-branch-")) - return { - variant: "git-branch", - branch: stage.slice("git-branch-".length), - }; + if (stage.startsWith("git-branch-")) { + const branch = stage.slice("git-branch-".length); + if (!branch) throw new Error("Unsupported stage"); + return { variant: "git-branch", branch }; + } throw new Error("Unsupported stage"); }