Files
2026-07-13 13:39:12 +08:00

168 lines
5.8 KiB
TypeScript

import { describe, it, afterEach } from "node:test";
import assert from "node:assert/strict";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import {
applyCompression,
maybePersistRtkRawOutput,
processRtkText,
readRtkRawOutput,
redactRtkRawOutput,
} from "../../../open-sse/services/compression/index.ts";
const originalDataDir = process.env.DATA_DIR;
afterEach(() => {
if (originalDataDir === undefined) delete process.env.DATA_DIR;
else process.env.DATA_DIR = originalDataDir;
});
describe("RTK raw output retention", () => {
it("redacts secrets before raw output persistence and exposes a pointer", () => {
const tempData = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-rtk-raw-"));
process.env.DATA_DIR = tempData;
const raw = [
"Error: failed with token=sk-1234567890abcdefghijklmnop",
...Array.from({ length: 40 }, () => "same noisy line"),
].join("\n");
const result = processRtkText(raw, {
command: "custom",
config: {
rawOutputRetention: "always",
maxLinesPerResult: 2,
maxCharsPerResult: 120,
},
});
assert.equal(result.compressed, true);
assert.equal(result.rawOutputPointers?.length, 1);
const pointer = result.rawOutputPointers?.[0];
assert.ok(pointer);
const recovered = readRtkRawOutput(pointer.id);
assert.ok(recovered);
assert.ok(recovered.includes("[REDACTED"));
assert.ok(!recovered.includes("sk-1234567890abcdefghijklmnop"));
});
it("keeps raw output disabled by default", () => {
const redacted = redactRtkRawOutput("Authorization: Bearer secret-token-value");
assert.equal(redacted.redacted, true);
assert.ok(!redacted.text.includes("secret-token-value"));
const result = processRtkText("line\nline\nline\nline", {
config: { maxLinesPerResult: 1 },
});
assert.equal(result.rawOutputPointers, undefined);
});
it("retains only configured failure output and enforces byte caps", () => {
const tempData = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-rtk-raw-cap-"));
process.env.DATA_DIR = tempData;
assert.equal(
maybePersistRtkRawOutput("ordinary successful output", { retention: "failures" }),
null
);
const pointer = maybePersistRtkRawOutput("error: " + "x".repeat(5000), {
retention: "failures",
maxBytes: 64,
});
assert.ok(pointer);
const recovered = readRtkRawOutput(pointer.id);
assert.ok(recovered?.includes("truncated at 1024 bytes"));
assert.equal(readRtkRawOutput("0123456789abcdef01234567"), null);
});
it("propagates raw output pointers from stacked RTK runs", () => {
const tempData = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-rtk-stacked-raw-"));
process.env.DATA_DIR = tempData;
const result = applyCompression(
{
messages: [
{
role: "tool",
content: [
"Error: failed with Authorization: Bearer secret-token-value",
...Array.from({ length: 40 }, () => "same noisy line"),
].join("\n"),
},
],
},
"stacked",
{
config: {
enabled: true,
defaultMode: "stacked",
autoTriggerTokens: 0,
cacheMinutes: 5,
preserveSystemPrompt: true,
comboOverrides: {},
rtkConfig: {
enabled: true,
intensity: "standard",
applyToToolResults: true,
applyToCodeBlocks: false,
applyToAssistantMessages: false,
enabledFilters: [],
disabledFilters: [],
maxLinesPerResult: 2,
maxCharsPerResult: 120,
deduplicateThreshold: 3,
customFiltersEnabled: true,
trustProjectFilters: false,
rawOutputRetention: "always",
rawOutputMaxBytes: 1_048_576,
},
stackedPipeline: [{ engine: "rtk", intensity: "standard" }],
},
}
);
const pointer = result.stats?.rtkRawOutputPointers?.[0];
assert.ok(pointer);
assert.ok(readRtkRawOutput(pointer.id)?.includes("[REDACTED"));
});
it("never throws when the raw-output write fails (disk error → null pointer)", () => {
// Point DATA_DIR underneath a regular FILE so mkdirSync(recursive) throws ENOTDIR.
// maybePersistRtkRawOutput is best-effort capture: a write failure must degrade to a
// skipped capture (null), never propagate into the compression pipeline (F5.3).
const blocker = path.join(os.tmpdir(), `omniroute-rtk-blocked-${process.pid}-${Date.now()}`);
fs.writeFileSync(blocker, "x");
process.env.DATA_DIR = path.join(blocker, "nested");
let pointer: ReturnType<typeof maybePersistRtkRawOutput> | undefined;
assert.doesNotThrow(() => {
pointer = maybePersistRtkRawOutput("error: boom\n" + "noise\n".repeat(8), {
retention: "always",
});
});
assert.equal(pointer, null);
fs.rmSync(blocker, { force: true });
});
it("redacts Basic/Proxy auth headers and key fields the base patterns miss", () => {
const basic = redactRtkRawOutput("> Authorization: Basic dXNlcjpwYXNzd29yZA==");
assert.equal(basic.redacted, true);
assert.ok(!basic.text.includes("dXNlcjpwYXNzd29yZA=="), "Authorization: Basic base64 redacted");
const proxy = redactRtkRawOutput("Proxy-Authorization: Basic c2VjcmV0OnBhc3M=");
assert.equal(proxy.redacted, true);
assert.ok(!proxy.text.includes("c2VjcmV0OnBhc3M="), "Proxy-Authorization redacted");
const pkey = redactRtkRawOutput("private_key=abc123def456ghi");
assert.equal(pkey.redacted, true);
assert.ok(!pkey.text.includes("abc123def456ghi"), "private_key value redacted");
const cred = redactRtkRawOutput("credential: my-credential-value-x");
assert.equal(cred.redacted, true);
assert.ok(!cred.text.includes("my-credential-value-x"), "credential value redacted");
});
});