| 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. |
| 8 | import * as NodeChildProcess from "node:child_process"; |
| 9 | import * as NodeFS from "node:fs"; |
| 10 | import * as NodeOS from "node:os"; |
| 11 | import * as NodePath from "node:path"; |
| 12 | import * as NodeURL from "node:url"; |
| 13 | |
| 14 | // Files faster than this are all treated alike, which keeps the weights file short. |
| 15 | const MIN_RECORDED_SECONDS = 0.5; |
| 16 | |
| 17 | interface VitestJsonReport { |
| 18 | readonly testResults: ReadonlyArray<{ |
| 19 | readonly name: string; |
| 20 | readonly startTime: number; |
| 21 | readonly endTime: number; |
| 22 | }>; |
| 23 | } |
| 24 | |
| 25 | const serverDir = NodeURL.fileURLToPath(new URL("..", import.meta.url)); |
| 26 | const weightsPath = NodePath.join(serverDir, "src/testUtils/shardWeights.json"); |
| 27 | const reportDir = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "t3-server-test-report-")); |
| 28 | const 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. |
| 32 | NodeChildProcess.spawnSync( |
| 33 | process.execPath, |
| 34 | ["--run", "test", "--", "--reporter=json", `--outputFile=${reportPath}`], |
| 35 | { cwd: serverDir, stdio: "inherit" }, |
| 36 | ); |
| 37 | |
| 38 | const report: VitestJsonReport = JSON.parse(NodeFS.readFileSync(reportPath, "utf8")); |
| 39 | NodeFS.rmSync(reportDir, { recursive: true, force: true }); |
| 40 | |
| 41 | const 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 | |
| 54 | NodeFS.writeFileSync(weightsPath, `${JSON.stringify(weights, null, 2)}\n`); |