g1t/services/deployments/src/moves.test.ts

79 lines3,274 bytesCodeBlame
1import assert from "node:assert/strict";
2import { test } from "node:test";
3
4import { moveTargets, ownerOf, rebuildOutcome, retryDue, type MovedApp } from "./moves.ts";
5
6const app = (over: Partial<MovedApp>): MovedApp => ({
7 script: "lab-api-syntaqx",
8 project_id: "prj_api",
9 kind: "production",
10 branch: null,
11 number: null,
12 commit_sha: "aaa",
13 deployed_at: "2026-10-05T00:26:29Z",
14 ...over,
15});
16
17test("each stale app is one target, paused or not, with the names it replaces", () => {
18 const targets = moveTargets([
19 app({}),
20 app({ script: "lab-api-git-v2-syntaqx", kind: "preview", branch: "v2", number: 1, commit_sha: "bbb" }),
21 app({ script: "automation-lab-syntaqx", project_id: "prj_lab", commit_sha: "ccc" }),
22 ]);
23 assert.deepEqual(
24 targets.map((t) => [t.projectId, t.kind, t.branch, t.number, t.commit, t.from]),
25 [
26 ["prj_api", "production", null, null, "aaa", ["lab-api-syntaqx"]],
27 ["prj_api", "preview", "v2", 1, "bbb", ["lab-api-git-v2-syntaqx"]],
28 ["prj_lab", "production", null, null, "ccc", ["automation-lab-syntaqx"]],
29 ],
30 );
31});
32
33test("an app under two old names (moved twice) is built once, replacing both", () => {
34 const targets = moveTargets([
35 app({ script: "lab-api-acme", deployed_at: "2026-10-01T00:00:00Z", commit_sha: "old" }),
36 app({ script: "lab-api-syntaqx", deployed_at: "2026-10-05T00:00:00Z", commit_sha: "new" }),
37 ]);
38 assert.equal(targets.length, 1);
39 assert.equal(targets[0].commit, "new");
40 assert.deepEqual(targets[0].from.sort(), ["lab-api-acme", "lab-api-syntaqx"]);
41});
42
43test("a build a move dropped is built again, at its newer commit", () => {
44 const targets = moveTargets(
45 [app({})],
46 [
47 { project_id: "prj_api", kind: "production", branch: null, number: null, commit_sha: "newer", created_at: "2026-10-06T01:00:00Z" },
48 { project_id: "prj_web", kind: "preview", branch: "x", number: 4, commit_sha: "w", created_at: "2026-10-06T01:00:00Z" },
49 ],
50 );
51 assert.deepEqual(
52 targets.map((t) => [t.projectId, t.commit, t.from]),
53 [
54 ["prj_api", "newer", ["lab-api-syntaqx"]],
55 ["prj_web", "w", []],
56 ],
57 );
58});
59
60test("only a queued build counts as rebuilt; refusals and errors are tried again", () => {
61 assert.equal(rebuildOutcome({ ok: true, value: { status: "queued" } }), "queued");
62 assert.equal(rebuildOutcome({ ok: true, value: { status: "skipped" } }), "failed");
63 assert.equal(rebuildOutcome({ ok: true, value: { status: "failed" } }), "failed");
64 assert.equal(rebuildOutcome({ ok: false }), "failed");
65 assert.equal(rebuildOutcome(null), "none");
66});
67
68test("a rebuild that could not start waits before it is tried again", () => {
69 const now = Date.parse("2026-10-06T02:00:00Z");
70 assert.equal(retryDue(null, now, 3600_000), true);
71 assert.equal(retryDue("2026-10-06T01:30:00Z", now, 3600_000), false);
72 assert.equal(retryDue("2026-10-06T01:00:00Z", now, 3600_000), true);
73});
74
75test("an app is its project's current workspace's, not the one its row names", () => {
76 const owners = new Map([["prj_api", "flagon-io"]]);
77 assert.equal(ownerOf({ project_id: "prj_api", workspace: "syntaqx" }, owners), "flagon-io");
78 assert.equal(ownerOf({ project_id: "prj_gone", workspace: "syntaqx" }, owners), "syntaqx");
79});