mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-07 04:12:04 +00:00
Vitest's --shard cut the file list into slices of equal length, which gave the integration shards 954 s to 1 449 s. A sequencer now deals files out by their measured duration, plus a fixed per-file cost for import and setup, longest first to the shard with the least work, and starts the longest files first inside a shard. Timings live in tests/durations/*.json and are regenerated from Vitest JSON reports with scripts/test-durations.mjs; every CI shard uploads its report. Integration shards run three workers each, one cloned database per worker. The unit suite runs as three projects decided per file from its source: guards and unit share a module graph inside a worker, isolated keeps a fresh one for every file that mocks, spies, stubs or resets. In CI it is three duration-balanced shards next to a job for lint, format, types and the OpenAPI drift check. The required check names stay as they were. "Lint, Typecheck & Test" is now the job that answers for the static checks and every unit shard, the way "integration" already answers for its shards. The browser-install guard recognises a direct, flags-only vitest run as a run of the unit suite.
78 lines
2.7 KiB
JavaScript
78 lines
2.7 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Regenerate a timing file for the duration-balanced sequencer
|
|
* (`tests/durations/sequencer.ts`) from Vitest JSON reports.
|
|
*
|
|
* node scripts/test-durations.mjs <suite> <report.json> [<report.json> …]
|
|
*
|
|
* `<suite>` is `unit` or `integration` and names the file written,
|
|
* `tests/durations/<suite>.json`. Pass every shard's report of one run; a
|
|
* file measured twice keeps the longer reading. Files the reports do not
|
|
* mention keep their previous value, so a partial run refines the table
|
|
* rather than emptying it, and files that no longer exist are dropped.
|
|
*
|
|
* Produce a report with
|
|
* pnpm exec vitest run [--config …] --reporter=default \
|
|
* --reporter=json --outputFile.json=report.json
|
|
* The nightly shuffled workflow uploads the reports of a CI run, which is
|
|
* the better source: CI is where the shards have to balance.
|
|
*/
|
|
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
import { relative, resolve } from "node:path";
|
|
|
|
const root = resolve(import.meta.dirname, "..");
|
|
const [suite, ...reports] = process.argv.slice(2);
|
|
|
|
if (!["unit", "integration"].includes(suite ?? "") || reports.length === 0) {
|
|
console.error(
|
|
"usage: node scripts/test-durations.mjs <unit|integration> <report.json>…",
|
|
);
|
|
process.exit(2);
|
|
}
|
|
|
|
const target = resolve(root, "tests/durations", `${suite}.json`);
|
|
const previous = existsSync(target)
|
|
? JSON.parse(readFileSync(target, "utf8")).files
|
|
: {};
|
|
|
|
const measured = new Map();
|
|
for (const report of reports) {
|
|
const parsed = JSON.parse(readFileSync(report, "utf8"));
|
|
for (const result of parsed.testResults ?? []) {
|
|
if (typeof result.startTime !== "number") continue;
|
|
if (typeof result.endTime !== "number") continue;
|
|
const file = relative(root, result.name).split("\\").join("/");
|
|
const seconds = Math.max(0, (result.endTime - result.startTime) / 1000);
|
|
measured.set(file, Math.max(measured.get(file) ?? 0, seconds));
|
|
}
|
|
}
|
|
|
|
if (measured.size === 0) {
|
|
console.error("the reports carry no per-file timings; nothing written");
|
|
process.exit(1);
|
|
}
|
|
|
|
const files = {};
|
|
const keys = new Set([...Object.keys(previous), ...measured.keys()]);
|
|
for (const file of [...keys].sort()) {
|
|
if (!existsSync(resolve(root, file))) continue;
|
|
const seconds = measured.get(file) ?? previous[file];
|
|
files[file] = Math.round(seconds * 10) / 10;
|
|
}
|
|
|
|
writeFileSync(
|
|
target,
|
|
JSON.stringify(
|
|
{
|
|
source: `vitest JSON report of ${measured.size} files, written ${new Date().toISOString().slice(0, 10)}`,
|
|
files,
|
|
},
|
|
null,
|
|
2,
|
|
) + "\n",
|
|
);
|
|
const total = Object.values(files).reduce((a, b) => a + b, 0);
|
|
console.log(
|
|
`${target}: ${Object.keys(files).length} files, ${Math.round(total)} s in total`,
|
|
);
|