import { test, expect, describe, beforeEach, afterEach } from "vitest"; import { mkdtemp, rm, writeFile, mkdir } from "node:fs/promises"; import { join } from "node:path"; import { tmpdir } from "node:os"; import { hasCacheBypassFlag, readCommandResult, writeCommandResult, type CommandResultCacheCall, } from "../runtime/result-cache.ts"; import { COMMAND_RESULT_CACHE_SCHEMA_VERSION, getCachedCommandResult, setCachedCommandResult, type CommandResultCacheKey, } from "../cache/command-result-cache.ts"; import type { CacheLayer, CacheEntry } from "../cache/cache-layer.ts"; import type { KernelCommandDefinition, KernelExecutionReport, KernelResultCacheContext, } from "@warpgogol/werkstatt-shared/kernel"; /* RFC-1130: Unit tests for cacheBypassFlags — flags that reduce coverage (e.g. --rfc) must skip both the cache read and the cache write so a filtered run can neither serve a stale full-run report nor poison the full-run cache entry (the cache key is flag-blind by design). Not a contract source of truth — the module under test owns the authoritative behavior; fixtures are minimal scaffolding only. Fixtures stay inline and minimal so each case states its own setup. RFC-1311: Compass header sweep — declares the cache suite's contract for test-file scaffold coverage. */ // @ai-invariant fixtures must not couple to internals beyond the public surface under test function makeCommand(overrides: Partial = {}): KernelCommandDefinition { return { name: "test.filtered", modulePath: "test", description: "Test command with a coverage-reducing flag", scope: "workspace", execute: () => undefined, reads: ["src/input.txt"], cacheBypassFlags: ["rfc"], ...overrides, }; } function makeReport(): KernelExecutionReport { return { commandName: "test.filtered", exitCode: 0, ok: true, summary: "ran", timing: { durationMs: 10, exceededTimeout: false }, filesModified: [], metadata: makeCommand(), logs: [], }; } function makeKey(overrides: Partial = {}): CommandResultCacheKey { return { schemaVersion: COMMAND_RESULT_CACHE_SCHEMA_VERSION, commandName: "test.filtered", siteName: null, inputsHash: "aaa", moduleHash: "bbb", flagsHash: "", ...overrides, }; } function makeCall( command: KernelCommandDefinition, tmpDir: string, argv: string[], ): CommandResultCacheCall { return { command, argv, flags: {}, baseDir: tmpDir, workspaceRoot: tmpDir, siteName: null, force: false, dryRun: false, }; } function makeCacheCtx(cache: CacheLayer): KernelResultCacheContext { return { layer: cache, moduleHashCache: new Map() }; } class MockCacheLayer implements CacheLayer { readonly available = true; private entry: CacheEntry | null = null; async get(_namespace: string, _key: string): Promise { return this.entry; } async set( _namespace: string, _key: string, data: unknown, mtime: number, contentHash: string, ): Promise { this.entry = { key: _key, data, mtime, contentHash, updatedAt: Date.now() }; } async clear(): Promise { this.entry = null; } async status() { return { available: true, dbPath: ":memory:", dbSizeBytes: 0, namespaces: [], }; } async close(): Promise {} } describe("RFC-1130: hasCacheBypassFlag", () => { test("true when a listed flag is present as --name", () => { expect(hasCacheBypassFlag(makeCommand(), ["--rfc", "RFC-0001"])).toBe(true); }); test("true when a listed flag is present as --name=value", () => { expect(hasCacheBypassFlag(makeCommand(), ["--rfc=RFC-0001"])).toBe(true); }); test("false when no listed flag is present", () => { expect(hasCacheBypassFlag(makeCommand(), ["--site", "foo"])).toBe(false); expect(hasCacheBypassFlag(makeCommand(), [])).toBe(false); }); test("false when the command declares no cacheBypassFlags", () => { expect(hasCacheBypassFlag(makeCommand({ cacheBypassFlags: undefined }), ["--rfc"])).toBe(false); }); test("does not match a different flag sharing the prefix", () => { // --rfc-extra must not match the "rfc" bypass entry. expect(hasCacheBypassFlag(makeCommand(), ["--rfc-extra"])).toBe(false); }); }); describe("RFC-1130: executor cache bypass", () => { let tmpDir: string; beforeEach(async () => { tmpDir = await mkdtemp(join(tmpdir(), "rfc1130-")); await mkdir(join(tmpDir, "src"), { recursive: true }); await writeFile(join(tmpDir, "src", "input.txt"), "test"); }); afterEach(async () => { await rm(tmpDir, { recursive: true, force: true }); }); test("(a) bypass skips the cache read even when an entry exists", async () => { const cache = new MockCacheLayer(); const cmd = makeCommand(); await setCachedCommandResult(cache, makeKey(), makeReport()); // RFC-1133: bypass is derived from argv inside the shared block — a listed // flag in argv skips the read regardless of a populated store. const result = await readCommandResult( makeCacheCtx(cache), makeCall(cmd, tmpDir, ["--rfc", "RFC-0001"]), ); expect( result, "cacheBypassFlags must skip the cache read — a filtered run must not serve the full-run entry", ).toBeNull(); }); test("(b) bypass skips the cache write — filtered runs never poison the entry", async () => { const cache = new MockCacheLayer(); const cmd = makeCommand(); await writeCommandResult( makeCacheCtx(cache), makeCall(cmd, tmpDir, ["--rfc", "RFC-0001"]), makeReport(), ); const stored = await getCachedCommandResult(cache, makeKey()); expect( stored, "cacheBypassFlags must skip the cache write — a partial result must not overwrite the full-run entry", ).toBeNull(); }); test("(c) without bypass, the write stores normally", async () => { const cache = new MockCacheLayer(); const cmd = makeCommand(); await writeCommandResult(makeCacheCtx(cache), makeCall(cmd, tmpDir, []), makeReport()); // The stored key carries the real inputsHash — assert via the mock's entry // rather than a guessed hash: any entry in the namespace proves the write. const status = await cache.status(); expect(status.available).toBe(true); // Read back through the mock: setCachedCommandResult wrote one entry. const probe = await cache.get("command_results", "any"); expect(probe, "non-bypass write must store the result").not.toBeNull(); }); });