import { describe, expect, it } from "vitest"; import { buildInjection, collectEditedFiles, ProgressiveSink } from "./progressive.js"; describe("collectEditedFiles", () => { it("collects paths from edit, write and lsp_fix tool results", () => { const files = collectEditedFiles([ { toolName: "edit", input: { path: "/w/a.ts" } }, { toolName: "write", input: { path: "/w/b.ts" } }, { toolName: "lsp_fix", input: { path: "/w/c.ts" } }, { toolName: "read", input: { path: "/w/d.ts" } }, ]); expect(files).toEqual(["/w/a.ts", "/w/b.ts", "/w/c.ts"]); }); it("extracts redirect targets from bash commands", () => { const files = collectEditedFiles([{ toolName: "bash", input: { command: "echo x >> /w/out.log" } }]); expect(files).toContain("/w/out.log"); }); }); describe("buildInjection", () => { it("formats a compact diagnostics summary", () => { const text = buildInjection( { "/w/a.ts": [ { range: { start: { line: 0, character: 0 }, end: { line: 0, character: 1 } }, severity: 1, message: "boom", }, ], }, 20, ); expect(text).toContain("boom"); expect(text).toContain("a.ts"); }); it("returns undefined when there are no diagnostics", () => { expect(buildInjection({}, 20)).toBeUndefined(); }); }); describe("ProgressiveSink", () => { it("touches edited files and injects throttled diagnostics", async () => { const touched: string[] = []; const manager = { touchFile: async (f: string) => { touched.push(f); }, diagnostics: async () => ({ "/w/a.ts": [ { range: { start: { line: 0, character: 0 }, end: { line: 0, character: 1 } }, severity: 1, message: "boom", }, ], }), getClients: async () => [], } as any; const injections: string[] = []; const sink = new ProgressiveSink({ manager, config: { enabled: true, inject: "conversation", maxDiagnostics: 10, quietMs: 1000 }, onInjection: (t) => injections.push(t), now: () => 1, }); await sink.handleTurn([{ toolName: "edit", input: { path: "/w/a.ts" } }]); expect(touched).toEqual(["/w/a.ts"]); expect(injections).toHaveLength(1); // Throttle: a second turn within quietMs injects nothing. await sink.handleTurn([{ toolName: "edit", input: { path: "/w/a.ts" } }]); expect(injections).toHaveLength(1); }); it("does nothing when progressive is disabled", async () => { const manager = { touchFile: async () => {}, diagnostics: async () => ({}), getClients: async () => [], } as any; const sink = new ProgressiveSink({ manager, config: { enabled: false, inject: "status", maxDiagnostics: 10, quietMs: 1000 }, onInjection: () => {}, now: () => 1, }); const injected = await sink.handleTurn([{ toolName: "edit", input: { path: "/w/a.ts" } }]); expect(injected).toBeUndefined(); }); });