/** * Structural regression test for the embed.ts → worker-pool migration * (v0.41.15.0, T3, REGRESSION per IRON RULE). * * What this test asserts: * 1. embed.ts imports `runSlidingPool` from `../core/worker-pool.ts`. * 2. Both pre-migration sliding-pool sites are GONE: * - the `let nextIdx = 0; async function worker() {}` shape * - the `Promise.all(Array.from({ length: numWorkers }, () => worker()))` * shape (which paired with the above). * 3. Both migration sites call `runSlidingPool({ items: ..., workers: CONCURRENCY, ... })`. * 4. The legacy `CONCURRENCY = parseInt(process.env.GBRAIN_EMBED_CONCURRENCY...)` * default is preserved (codex finding #13 — embed's pre-existing default * pre-dates autoConcurrency; the migration must NOT route it through * resolveWorkersWithClamp). This is the load-bearing back-compat for * every existing brain that relies on the 20-worker embed sweep. * * Why structural rather than end-to-end: * Per codex finding #16/#17, embed.ts byte-equality via stubbed transport * was overclaimed in the original plan — it can't prove provider retry * behavior, SDK usage accounting, or progress event ordering preservation. * The pre-migration sliding pool didn't have any of those guarantees either; * it just had a small inline `while (nextIdx < pages.length)` loop. The * honest contract is "the same pages still get embedded by the same workers * reading from the same queue, just through a helper now." That's a * structural property — easiest to pin by source grep. * * The helper's contracts (atomic claim, abort propagation, failures[] * shape, BudgetExhausted bypass) are exhaustively tested in * test/worker-pool.test.ts; embed inherits those by import. */ import { describe, test, expect } from 'bun:test'; import { readFileSync } from 'fs'; import { resolve } from 'path'; const REPO_ROOT = resolve(import.meta.dir, '..'); const EMBED_PATH = resolve(REPO_ROOT, 'src/commands/embed.ts'); const EMBED_SOURCE = readFileSync(EMBED_PATH, 'utf-8'); describe('embed.ts → worker-pool migration (T3)', () => { test('imports runSlidingPool from worker-pool helper', () => { expect(EMBED_SOURCE).toMatch( /import\s*\{\s*runSlidingPool\s*\}\s*from\s*['"]\.\.\/core\/worker-pool\.ts['"]/, ); }); test('calls runSlidingPool at least twice (embedAll + embedAllStale paths)', () => { const matches = EMBED_SOURCE.match(/runSlidingPool\(/g) ?? []; expect(matches.length).toBeGreaterThanOrEqual(2); }); test('pre-migration `let nextIdx = 0; async function worker()` shape is gone', () => { // The exact shape the migration replaced. Either token alone could // legitimately appear in a comment or string literal; both together // on adjacent lines indicates a regression to the inline pool. const inlinePool = /let\s+nextIdx\s*=\s*0\s*;\s*\n\s*async\s+function\s+worker\s*\(/; expect(EMBED_SOURCE).not.toMatch(inlinePool); }); test('pre-migration `Promise.all(Array.from({ length: numWorkers }, () => worker()))` is gone', () => { // Migration-specific shape from the original inline pool. The // generic `Promise.all(...)` pattern is still allowed elsewhere // (gateway, etc.); only the worker-pool-fanout shape is banned. const fanout = /Promise\.all\(Array\.from\(\{\s*length:\s*numWorkers\s*\}/; expect(EMBED_SOURCE).not.toMatch(fanout); }); test('preserves GBRAIN_EMBED_CONCURRENCY default of 20 (codex #13)', () => { // The pre-migration default must survive: env override or 20. // Routing through resolveWorkersWithClamp would change this behavior // (autoConcurrency returns 1 for small file counts even on Postgres), // breaking every existing brain that relies on the 20-worker default. expect(EMBED_SOURCE).toMatch( /parseInt\(process\.env\.GBRAIN_EMBED_CONCURRENCY\s*\|\|\s*['"]20['"]/, ); }); test('runSlidingPool call sites pass `workers: CONCURRENCY`', () => { // The migrated calls must thread the pre-existing CONCURRENCY value // through, not invent a new default. Catches the regression where // a future contributor swaps `workers: CONCURRENCY` for a literal. // Allow optional commas/whitespace — match both call sites' shape. const callSites = EMBED_SOURCE.match( /runSlidingPool\(\s*\{[\s\S]*?workers:\s*CONCURRENCY/g, ); expect(callSites?.length ?? 0).toBeGreaterThanOrEqual(2); }); test('embedAllStale path still threads the cancellation signal into pool', () => { // The pre-migration code checked `!budgetSignal.aborted` in the worker // loop. The migration moves that check into the helper via the `signal` // option. #1737 then composed the wall-clock budget with the caller's // abort into `effectiveSignal` (anySignal(budgetSignal, externalSignal)) so // a killed job stops the pool too. If a future refactor drops the signal, // both wall-clock budget AND cooperative abort cancellation regress. expect(EMBED_SOURCE).toMatch( /runSlidingPool\(\s*\{[\s\S]*?signal:\s*effectiveSignal[\s\S]*?\}\)/, ); }); test('failureLabel projector uses page.slug (memory bound on large brains)', () => { // Per codex #10 + D7, failures[] must not store full Page objects. // We can't test the runtime behavior without a full mock engine, but // we CAN assert the call sites pass the projector explicitly. expect(EMBED_SOURCE).toMatch(/failureLabel:\s*\(page\)\s*=>\s*page\.slug/); }); });