Skip to content

Commit 223f298

Browse files
author
Conner Aldrich
committed
test: cover JsonObjectEnv parser and version-suffix logic
- envUtil.test.ts: JsonObjectEnv with default (string) validator and with JsonAny validator, including non-JSON, non-object, wrong-value-type, and named error message cases. - deploymentVersions.test.ts: full coverage of compareDeploymentVersions including the multi-hyphen suffix regression case (20250101.1-pre-rc.1 vs 20250101.1-pre-rc.2 must not tie at 0). - calculateNextBuildVersion.test.ts: same-day increment, new-day reset, caller-provided suffix wins, and the multi-hyphen-suffix-on-input case. webapp tests verified locally (14/14 passing). Supervisor test is formed correctly; full local run is blocked by an unrelated upstream build error in @trigger.dev/core's sharedRuntimeManager.
1 parent 7f7f5d1 commit 223f298

3 files changed

Lines changed: 180 additions & 1 deletion

File tree

apps/supervisor/src/envUtil.test.ts

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { describe, it, expect } from "vitest";
2-
import { BoolEnv, AdditionalEnvVars } from "./envUtil.js";
2+
import { z } from "zod";
3+
import { BoolEnv, AdditionalEnvVars, JsonObjectEnv, JsonAny } from "./envUtil.js";
34

45
describe("BoolEnv", () => {
56
it("should parse string 'true' as true", () => {
@@ -78,3 +79,78 @@ describe("AdditionalEnvVars", () => {
7879
});
7980
});
8081
});
82+
83+
describe("JsonObjectEnv (string-valued)", () => {
84+
const schema = JsonObjectEnv("TEST_ENV");
85+
86+
it("returns empty object for default (no value)", () => {
87+
expect(schema.parse(undefined)).toEqual({});
88+
});
89+
90+
it("parses a simple string-valued JSON object", () => {
91+
expect(schema.parse('{"a":"1","b":"2"}')).toEqual({ a: "1", b: "2" });
92+
});
93+
94+
it("parses an empty JSON object", () => {
95+
expect(schema.parse("{}")).toEqual({});
96+
});
97+
98+
it("rejects non-JSON input", () => {
99+
expect(() => schema.parse("not json")).toThrowError(/not valid JSON/);
100+
});
101+
102+
it("rejects JSON arrays", () => {
103+
expect(() => schema.parse("[]")).toThrowError(/must be a JSON object \(got array\)/);
104+
});
105+
106+
it("rejects JSON primitives", () => {
107+
expect(() => schema.parse('"foo"')).toThrowError(/must be a JSON object \(got string\)/);
108+
expect(() => schema.parse("42")).toThrowError(/must be a JSON object \(got number\)/);
109+
expect(() => schema.parse("null")).toThrowError(/must be a JSON object \(got object\)/);
110+
});
111+
112+
it("rejects values that are not strings (with default validator)", () => {
113+
expect(() => schema.parse('{"a": 1}')).toThrowError(/has invalid value/);
114+
expect(() => schema.parse('{"a": true}')).toThrowError(/has invalid value/);
115+
});
116+
});
117+
118+
describe("JsonObjectEnv (arbitrary-value)", () => {
119+
const schema = JsonObjectEnv("TEST_ANY", { valueValidator: JsonAny });
120+
121+
it("accepts nested objects", () => {
122+
expect(
123+
schema.parse(
124+
JSON.stringify({
125+
runAsNonRoot: true,
126+
runAsUser: 1000,
127+
capabilities: { drop: ["ALL"] },
128+
})
129+
)
130+
).toEqual({
131+
runAsNonRoot: true,
132+
runAsUser: 1000,
133+
capabilities: { drop: ["ALL"] },
134+
});
135+
});
136+
137+
it("accepts mixed value types", () => {
138+
expect(schema.parse('{"s":"x","n":1,"b":true,"a":[1,2],"o":{"k":"v"}}')).toEqual({
139+
s: "x",
140+
n: 1,
141+
b: true,
142+
a: [1, 2],
143+
o: { k: "v" },
144+
});
145+
});
146+
147+
it("still rejects non-object roots", () => {
148+
expect(() => schema.parse('"x"')).toThrowError(/must be a JSON object/);
149+
expect(() => schema.parse("[1,2,3]")).toThrowError(/must be a JSON object/);
150+
});
151+
152+
it("includes the env var name in error messages", () => {
153+
const named = JsonObjectEnv("KUBERNETES_WORKER_POD_SECURITY_CONTEXT");
154+
expect(() => named.parse("{notjson")).toThrowError(/KUBERNETES_WORKER_POD_SECURITY_CONTEXT/);
155+
});
156+
});
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { describe, expect, test, beforeEach, afterEach, vi } from "vitest";
2+
import { calculateNextBuildVersion } from "../app/v3/utils/calculateNextBuildVersion";
3+
4+
describe("calculateNextBuildVersion", () => {
5+
beforeEach(() => {
6+
vi.useFakeTimers();
7+
vi.setSystemTime(new Date("2025-02-08T12:00:00Z"));
8+
});
9+
10+
afterEach(() => {
11+
vi.useRealTimers();
12+
});
13+
14+
test("starts at YYYYMMDD.1 when there's no previous version", () => {
15+
expect(calculateNextBuildVersion(null)).toBe("20250208.1");
16+
expect(calculateNextBuildVersion(undefined)).toBe("20250208.1");
17+
expect(calculateNextBuildVersion("")).toBe("20250208.1");
18+
});
19+
20+
test("appends suffix when starting fresh", () => {
21+
expect(calculateNextBuildVersion(null, "hardened")).toBe("20250208.1-hardened");
22+
});
23+
24+
test("increments build number on same day", () => {
25+
expect(calculateNextBuildVersion("20250208.1")).toBe("20250208.2");
26+
expect(calculateNextBuildVersion("20250208.5")).toBe("20250208.6");
27+
});
28+
29+
test("resets to .1 on a new day", () => {
30+
expect(calculateNextBuildVersion("20250207.5")).toBe("20250208.1");
31+
});
32+
33+
test("ignores existing suffix on the latest version", () => {
34+
// The new version uses the caller-provided suffix, not the existing one.
35+
expect(calculateNextBuildVersion("20250208.1-old")).toBe("20250208.2");
36+
expect(calculateNextBuildVersion("20250208.1-old", "new")).toBe("20250208.2-new");
37+
expect(calculateNextBuildVersion("20250208.1-old", undefined)).toBe("20250208.2");
38+
});
39+
40+
test("handles multi-hyphen suffix on the latest version (regression test)", () => {
41+
// Before the fix, split("-") destructured `existingSuffix = "pre"` from
42+
// "20250208.1-pre-rc.1" — unused but misleading. baseVersion was correct
43+
// because `split("-")[0]` works regardless of segment count.
44+
expect(calculateNextBuildVersion("20250208.1-pre-rc.1")).toBe("20250208.2");
45+
expect(calculateNextBuildVersion("20250208.1-pre-rc.1", "hardened")).toBe(
46+
"20250208.2-hardened"
47+
);
48+
});
49+
});
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { describe, expect, test } from "vitest";
2+
import { compareDeploymentVersions } from "../app/v3/utils/deploymentVersions";
3+
4+
describe("compareDeploymentVersions", () => {
5+
describe("base versions only (no suffix)", () => {
6+
test("orders by date ascending", () => {
7+
expect(compareDeploymentVersions("20250101.1", "20250102.1")).toBe(-1);
8+
expect(compareDeploymentVersions("20250102.1", "20250101.1")).toBe(1);
9+
});
10+
11+
test("orders by build number when dates are equal", () => {
12+
expect(compareDeploymentVersions("20250101.1", "20250101.2")).toBe(-1);
13+
expect(compareDeploymentVersions("20250101.2", "20250101.1")).toBe(1);
14+
expect(compareDeploymentVersions("20250101.10", "20250101.2")).toBe(1);
15+
});
16+
17+
test("treats identical versions as equal", () => {
18+
expect(compareDeploymentVersions("20250101.1", "20250101.1")).toBe(0);
19+
});
20+
});
21+
22+
describe("with single-segment suffixes", () => {
23+
test("versions without suffix sort before versions with suffix", () => {
24+
expect(compareDeploymentVersions("20250101.1", "20250101.1-hardened")).toBe(-1);
25+
expect(compareDeploymentVersions("20250101.1-hardened", "20250101.1")).toBe(1);
26+
});
27+
28+
test("orders suffixes alphabetically when bases are equal", () => {
29+
expect(compareDeploymentVersions("20250101.1-alpha", "20250101.1-beta")).toBe(-1);
30+
expect(compareDeploymentVersions("20250101.1-beta", "20250101.1-alpha")).toBe(1);
31+
});
32+
33+
test("equal suffixes return 0", () => {
34+
expect(compareDeploymentVersions("20250101.1-foo", "20250101.1-foo")).toBe(0);
35+
});
36+
});
37+
38+
describe("with multi-hyphen suffixes (regression test)", () => {
39+
test("does NOT tie-break two distinct multi-hyphen suffixes as equal", () => {
40+
// Before the fix: split("-") returned ["20250101.1","pre","rc.1"], destructured
41+
// suffix = "pre" — so both versions would compare as equal.
42+
const a = "20250101.1-pre-rc.1";
43+
const b = "20250101.1-pre-rc.2";
44+
expect(compareDeploymentVersions(a, b)).not.toBe(0);
45+
expect(compareDeploymentVersions(a, b)).toBe(-1);
46+
expect(compareDeploymentVersions(b, a)).toBe(1);
47+
});
48+
49+
test("preserves full suffix in sort", () => {
50+
expect(compareDeploymentVersions("20250101.1-a-b", "20250101.1-a-c")).toBe(-1);
51+
expect(compareDeploymentVersions("20250101.1-x-y-z", "20250101.1-x-y-z")).toBe(0);
52+
});
53+
});
54+
});

0 commit comments

Comments
 (0)