1// @effect-diagnostics nodeBuiltinImport:off - a one-off maintenance script with no
2// Effect runtime; it only spawns vitest and reads and writes two files.
3// Run with: node apps/server/scripts/update-test-shard-weights.ts
4// Runs the whole server suite once and records how long each test file takes,
5// so CI can split the suite into shards of equal duration. See
6// src/testUtils/weightedShardSequencer.ts. Rerun it when a shard in CI runs
7// much longer than the others.
8import * as NodeChildProcess from "node:child_process";
9import * as NodeFS from "node:fs";
10import * as NodeOS from "node:os";
11import * as NodePath from "node:path";
12import * as NodeURL from "node:url";
13
14// Files faster than this are all treated alike, which keeps the weights file short.
15const MIN_RECORDED_SECONDS = 0.5;
16
17interface VitestJsonReport {
18 readonly testResults: ReadonlyArray<{
19 readonly name: string;
20 readonly startTime: number;
21 readonly endTime: number;
22 }>;
23}
24
25const serverDir = NodeURL.fileURLToPath(new URL("..", import.meta.url));
26const weightsPath = NodePath.join(serverDir, "src/testUtils/shardWeights.json");
27const reportDir = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "t3-server-test-report-"));
28const reportPath = NodePath.join(reportDir, "report.json");
29
30// `node --run` puts the package's own `vp` on PATH, on every platform. Failing
31// tests still report their duration, so the exit code is not checked.
32NodeChildProcess.spawnSync(
33 process.execPath,
34 ["--run", "test", "--", "--reporter=json", `--outputFile=${reportPath}`],
35 { cwd: serverDir, stdio: "inherit" },
36);
37
38const report: VitestJsonReport = JSON.parse(NodeFS.readFileSync(reportPath, "utf8"));
39NodeFS.rmSync(reportDir, { recursive: true, force: true });
40
41const weights = Object.fromEntries(
42 report.testResults
43 .map(
44 (file) =>
45 [
46 NodePath.relative(serverDir, file.name).replaceAll("\\", "/"),
47 Math.round((file.endTime - file.startTime) / 100) / 10,
48 ] as const,
49 )
50 .filter(([, seconds]) => seconds >= MIN_RECORDED_SECONDS)
51 .toSorted(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)),
52);
53
54NodeFS.writeFileSync(weightsPath, `${JSON.stringify(weights, null, 2)}\n`);