runCached

tcmts.cli.testing.runCached · #kexxljgm · term · runs on the server

uses tcmts.cli.vitest.keysForResults, which uses process

Run tests, reusing cached results. A test's hash covers its code and, through its dependencies, everything it calls (pinned npm versions included), so a cached result is only stale if the test depends on something outside the codebase, like the filesystem, the clock, or the network. fresh ignores the cache. A test whose code reaches files, the environment or other processes is kept for the platform too (keysForResults).

Only passes are reused. A failure can be the environment's or the runner's (a variable a test inherited, a bug in how tcm runs tests), which no key holds, and a failure kept would outlive its fix: a test that failed runs again, and says when it failed last time too with nothing it runs changed since (a fix that didn't save shows as this).

import type { Codebase } from "#codebase/tcmts/cli/codebase";
import type { TestOutcome } from "#codebase/tcmts/cli/testing";
import { keysForResults, runTestsWithVitest } from "#codebase/tcmts/cli/vitest";
import type { DefRecord } from "#codebase/tcmts/core/types";
import type { DefSource } from "#codebase/tcmts/loader";
import type { TestResult } from "#codebase/tcmts/store";

export function runCached(
  cb: Codebase,
  items: { path: string; rec: DefRecord }[],
  src: DefSource,
  fresh: boolean,
): TestOutcome[] {
  // results depend on the runtime and the test framework too (and some, the platform)
  const keys = keysForResults(
    cb,
    items.map(({ rec }) => rec.hash),
    src,
  );
  const cached = new Map<string, TestResult>();
  const failedBefore = new Set<string>();
  if (!fresh)
    for (const { rec } of items) {
      const r = cb.store.testResult(rec.hash, keys.get(rec.hash)!);
      if (r?.ok) cached.set(rec.hash, r);
      else if (r) failedBefore.add(rec.hash);
    }
  const toRun = [
    ...new Map(
      items.filter(({ rec }) => !cached.has(rec.hash)).map(({ rec }) => [rec.hash, rec]),
    ).values(),
  ];
  const ran = new Map(runTestsWithVitest(cb, toRun, src).map((r) => [r.hash, r]));
  // (kept under the keys as they are after running: the first run installs the test framework)
  const after = ran.size ? keysForResults(cb, ran.keys(), src) : keys;
  for (const r of ran.values())
    if (!r.infrastructure) cb.store.putTestResult(r, after.get(r.hash)!);
  return items.map(({ path, rec }) => {
    const result = (cached.get(rec.hash) ?? ran.get(rec.hash))!;
    return {
      path,
      result,
      cached: cached.has(rec.hash),
      ...(!result.ok && failedBefore.has(rec.hash) ? { failedBefore: true } : {}),
    };
  });
}

uses

used by