g1t/services/deployments/src/environments.test.ts
Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.
| Merge main: Deployments panel in the About, project homepage, both sides' operations | 1 | import assert from "node:assert/strict"; |
| 2 | import { test } from "node:test"; | |
| 3 | ||
| 4 | import { | |
| 5 | actionsTransition, | |
| 6 | address, | |
| 7 | buildState, | |
| 8 | commitState, | |
| 9 | compareEnvironments, | |
| 10 | environmentName, | |
| 11 | payloadOf, | |
| 12 | runOutcome, | |
| 13 | shortRef, | |
| 14 | statusContext, | |
| 15 | } from "./environments.ts"; | |
| 16 | import { buildStatuses, fromBuild, type BuildRow } from "./environments.ts"; | |
| 17 | ||
| 18 | const appUrl = (script: string) => `https://${script}.g1t.page`; | |
| 19 | ||
| 20 | test("an environment is named as given, production when not", () => { | |
| 21 | assert.equal(environmentName(undefined), "production"); | |
| 22 | assert.equal(environmentName(" "), "production"); | |
| 23 | assert.equal(environmentName(" staging "), "staging"); | |
| 24 | assert.equal(environmentName("review/feature-x"), "review/feature-x"); | |
| 25 | assert.deepEqual(environmentName("a\nb"), { error: "`environment` cannot hold control characters." }); | |
| 26 | assert.ok(typeof environmentName("x".repeat(256)) === "object"); | |
| 27 | assert.ok(typeof environmentName(3) === "object"); | |
| 28 | }); | |
| 29 | ||
| 30 | test("addresses are http(s), and a payload is an object", () => { | |
| 31 | assert.equal(address("https://g1t.sh", "environment_url"), "https://g1t.sh"); | |
| 32 | assert.equal(address(null, "log_url"), null); | |
| 33 | assert.ok(typeof address("ftp://example.com", "log_url") === "object"); | |
| 34 | assert.ok(typeof address("not a url", "log_url") === "object"); | |
| 35 | assert.deepEqual(payloadOf(undefined), { value: {} }); | |
| 36 | assert.deepEqual(payloadOf({ error: "kept" }), { value: { error: "kept" } }); | |
| 37 | assert.deepEqual(payloadOf('{"units":["api"]}'), { value: { units: ["api"] } }); | |
| 38 | assert.ok("error" in payloadOf("[1]")); | |
| 39 | assert.ok("error" in payloadOf("{")); | |
| 40 | }); | |
| 41 | ||
| 42 | test("refs read as their branch or tag", () => { | |
| 43 | assert.equal(shortRef("refs/heads/main"), "main"); | |
| 44 | assert.equal(shortRef("refs/tags/v1.2.0"), "v1.2.0"); | |
| 45 | assert.equal(shortRef("main"), "main"); | |
| 46 | }); | |
| 47 | ||
| 48 | test("each state says something on the commit, but inactive", () => { | |
| 49 | assert.equal(statusContext("production"), "deploy / production"); | |
| 50 | assert.equal(commitState("queued"), "pending"); | |
| 51 | assert.equal(commitState("in_progress"), "pending"); | |
| 52 | assert.equal(commitState("success"), "success"); | |
| 53 | assert.equal(commitState("failure"), "failure"); | |
| 54 | assert.equal(commitState("error"), "error"); | |
| 55 | assert.equal(commitState("inactive"), null); | |
| 56 | }); | |
| 57 | ||
| 58 | test("a g1t.page build's status reads as a deployment state", () => { | |
| 59 | assert.equal(buildState("queued"), "queued"); | |
| 60 | assert.equal(buildState("building"), "in_progress"); | |
| 61 | assert.equal(buildState("ready"), "success"); | |
| 62 | assert.equal(buildState("failed"), "failure"); | |
| 63 | assert.equal(buildState("skipped"), "error"); | |
| 64 | assert.equal(buildState("replaced"), "inactive"); | |
| 65 | assert.equal(buildState("down"), "inactive"); | |
| 66 | }); | |
| 67 | ||
| 68 | test("a run's deployment moves on, and a failure sticks until the run ends", () => { | |
| 69 | assert.equal(actionsTransition(null, "in_progress", false), "in_progress"); | |
| 70 | assert.equal(actionsTransition("in_progress", "in_progress", false), null); | |
| 71 | assert.equal(actionsTransition("in_progress", "failure", false), "failure"); | |
| 72 | assert.equal(actionsTransition("failure", "in_progress", false), null, "a later job does not undo a failure"); | |
| 73 | assert.equal(actionsTransition("failure", "success", true), "success", "the run's outcome is the last word"); | |
| 74 | assert.equal(actionsTransition("success", "in_progress", false), null); | |
| 75 | assert.equal(actionsTransition("in_progress", "success", true), "success"); | |
| 76 | }); | |
| 77 | ||
| 78 | test("a run's outcome for an environment comes from the jobs that ran", () => { | |
| 79 | assert.equal(runOutcome(["success", "skipped"]), "success"); | |
| 80 | assert.equal(runOutcome(["success", "failure"]), "failure"); | |
| 81 | assert.equal(runOutcome(["success", "cancelled"]), "error"); | |
| 82 | assert.equal(runOutcome(["skipped", "skipped"]), null); | |
| 83 | assert.equal(runOutcome([]), null); | |
| 84 | }); | |
| 85 | ||
| 86 | test("production comes first, then the newest", () => { | |
| 87 | const env = (name: string, production: boolean, at: string) => ({ name, production_environment: production, updated_at: at }); | |
| 88 | const sorted = [env("preview", false, "2026-10-07"), env("staging", false, "2026-10-06"), env("Production", true, "2026-10-01"), env("eu", true, "2026-10-05")].sort( | |
| 89 | compareEnvironments, | |
| 90 | ); | |
| 91 | assert.deepEqual( | |
| 92 | sorted.map((e) => e.name), | |
| 93 | ["Production", "eu", "preview", "staging"], | |
| 94 | ); | |
| 95 | }); | |
| 96 | ||
| 97 | const build = (over: Partial<BuildRow>): BuildRow => ({ | |
| 98 | id: "dpl_1", | |
| 99 | workspace: "acme", | |
| 100 | slug: "web", | |
| 101 | repo_id: "rep_1", | |
| 102 | kind: "production", | |
| 103 | branch: null, | |
| 104 | number: null, | |
| 105 | commit_sha: "a".repeat(40), | |
| 106 | script: "web-acme", | |
| 107 | status: "ready", | |
| 108 | error: null, | |
| 109 | created_by: "usr_1", | |
| 110 | created_at: "2026-10-07T10:00:00.000Z", | |
| 111 | started_at: "2026-10-07T10:00:05.000Z", | |
| 112 | finished_at: "2026-10-07T10:01:00.000Z", | |
| 113 | ...over, | |
| 114 | }); | |
| 115 | ||
| 116 | test("a g1t.page build is a deployment in the same model", () => { | |
| 117 | const live = fromBuild(build({}), "main", "https://g1t.sh", appUrl, { usr_1: "ada" }); | |
| 118 | assert.equal(live.environment, "production"); | |
| 119 | assert.equal(live.ref, "main"); | |
| 120 | assert.equal(live.state, "success"); | |
| 121 | assert.equal(live.source, "g1t_page"); | |
| 122 | assert.equal(live.creator, "ada"); | |
| 123 | assert.equal(live.environment_url, "https://web-acme.g1t.page"); | |
| 124 | assert.equal(live.log_url, "https://g1t.sh/acme/web/deployments/dpl_1"); | |
| 125 | assert.equal(live.production_environment, true); | |
| 126 | const preview = fromBuild(build({ kind: "preview", branch: "fix", number: 7, status: "failed", error: "The build failed.", finished_at: null }), "main", "https://g1t.sh", appUrl); | |
| 127 | assert.equal(preview.ref, "fix"); | |
| 128 | assert.equal(preview.number, 7); | |
| 129 | assert.equal(preview.transient_environment, true); | |
| 130 | assert.equal(preview.environment_url, null, "a failed build is not served"); | |
| 131 | assert.equal(preview.creator, "g1t", "an id no one could name is g1t"); | |
| 132 | }); | |
| 133 | ||
| 134 | test("a build's statuses come from its own timestamps", () => { | |
| 135 | const row = build({ status: "replaced" }); | |
| 136 | const statuses = buildStatuses(row, fromBuild(row, "main", "https://g1t.sh", appUrl)); | |
| 137 | assert.deepEqual( | |
| 138 | statuses.map((s) => s.state), | |
| 139 | ["queued", "in_progress", "success", "inactive"], | |
| 140 | ); | |
| 141 | const queued = build({ status: "queued", started_at: null, finished_at: null }); | |
| 142 | assert.deepEqual( | |
| 143 | buildStatuses(queued, fromBuild(queued, "main", "https://g1t.sh", appUrl)).map((s) => s.state), | |
| 144 | ["queued"], | |
| 145 | ); | |
| 146 | }); |
This file's history is long; its oldest lines are credited to the oldest commit read.