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

219 lines
7.9 KiB
TypeScript

/**
* TDD tests for RTK filter learning (R6/R7/N7).
*
* Scope: pure functions operating on in-memory CommandSample arrays.
* No DB reads, no I/O. DB wiring (reading from call_logs) is a follow-up task.
*/
import { describe, it } from "node:test";
import assert from "node:assert/strict";
import { discoverRepeatedNoise } from "../../../open-sse/services/compression/engines/rtk/discover.ts";
import { suggestFilter } from "../../../open-sse/services/compression/engines/rtk/learn.ts";
// ---------------------------------------------------------------------------
// Synthetic test fixture
// ---------------------------------------------------------------------------
/**
* Build 10 `npm install` output samples.
*
* Each sample has:
* - A noisy deprecated-warning line whose package name and version vary.
* - A unique summary line indicating how many packages were added.
* - An error line with ERR!/E404.
* - A line that appears only in one sample (truly unique — should NOT be suggested).
*/
function makeSamples() {
const packages = [
["left-pad", "1.3.0"],
["moment", "2.29.4"],
["lodash", "4.17.21"],
["underscore", "1.13.6"],
["request", "2.88.2"],
["express", "4.18.2"],
["debug", "4.3.4"],
["chalk", "5.2.0"],
["semver", "7.5.1"],
["bluebird", "3.7.2"],
];
return packages.map(([pkg, ver], i) => ({
command: "npm install",
output: [
`npm WARN deprecated ${pkg}@${ver}: Use a newer version`,
`added ${100 + i * 3} packages in ${i + 1}s`,
`npm ERR! code E404`,
`npm ERR! 404 Not Found - GET https://registry.npmjs.org/${pkg}`,
// One truly unique line appears only in sample index 3
...(i === 3 ? ["unique line only in sample 3"] : []),
].join("\n"),
}));
}
const samples = makeSamples();
// ---------------------------------------------------------------------------
// discoverRepeatedNoise
// ---------------------------------------------------------------------------
describe("discoverRepeatedNoise", () => {
it("surfaces the deprecated-warning template as a high-hit candidate", () => {
const hits = discoverRepeatedNoise(samples);
// The normalised form of 'npm WARN deprecated <pkg>@<ver>: Use a newer version'
// should appear with hits === samples.length (10).
const deprecatedCandidate = hits.find((h) => h.pattern.includes("WARN deprecated"));
assert.ok(
deprecatedCandidate !== undefined,
"should surface a deprecated-warning drop candidate"
);
assert.ok(
deprecatedCandidate.hits >= samples.length,
`expected hits >= ${samples.length}, got ${deprecatedCandidate.hits}`
);
});
it("does NOT surface a line that appears only once as a drop candidate", () => {
const hits = discoverRepeatedNoise(samples);
const uniqueCandidate = hits.find((h) => h.pattern.includes("unique line only in sample"));
assert.equal(uniqueCandidate, undefined, "unique lines must not appear as drop candidates");
});
it("returns candidates sorted descending by hits", () => {
const hits = discoverRepeatedNoise(samples);
for (let i = 1; i < hits.length; i++) {
assert.ok(hits[i].hits <= hits[i - 1].hits, "results should be sorted descending by hits");
}
});
it("returns an empty array for an empty sample set", () => {
const hits = discoverRepeatedNoise([]);
assert.deepEqual(hits, []);
});
});
// ---------------------------------------------------------------------------
// suggestFilter
// ---------------------------------------------------------------------------
describe("suggestFilter", () => {
it("returns a filter whose match.commands pattern anchors the command", () => {
const filter = suggestFilter("npm install", samples);
assert.ok(filter.match.commands.length > 0, "should have at least one command pattern");
const pattern = filter.match.commands[0];
assert.ok(
new RegExp(pattern).test("npm install"),
`command pattern '${pattern}' should match 'npm install'`
);
});
it("includes a dropPattern that matches the deprecated warnings", () => {
const filter = suggestFilter("npm install", samples);
const hasDeprecatedDrop = filter.rules.dropPatterns.some((p) => {
try {
return new RegExp(p, "i").test("npm WARN deprecated left-pad@1.3.0: Use a newer version");
} catch {
return false;
}
});
assert.ok(hasDeprecatedDrop, "dropPatterns should cover deprecated warnings");
});
it("includes an errorPattern that covers the ERR!/E404 line", () => {
const filter = suggestFilter("npm install", samples);
const errLine = "npm ERR! code E404";
const hasErrorPattern = filter.preserve.errorPatterns.some((p) => {
try {
return new RegExp(p, "i").test(errLine);
} catch {
return false;
}
});
assert.ok(hasErrorPattern, `errorPatterns should cover '${errLine}'`);
});
it("includes a summaryPattern that covers the 'added N packages' line", () => {
const filter = suggestFilter("npm install", samples);
const summaryLine = "added 120 packages in 5s";
const hasSummaryPattern = filter.preserve.summaryPatterns.some((p) => {
try {
return new RegExp(p, "i").test(summaryLine);
} catch {
return false;
}
});
assert.ok(hasSummaryPattern, `summaryPatterns should cover '${summaryLine}'`);
});
it("no dropPattern matches a preserved error line (no preserve-vs-drop conflict)", () => {
const filter = suggestFilter("npm install", samples);
const preservedLines = [
"npm ERR! code E404",
"npm ERR! 404 Not Found - GET https://registry.npmjs.org/left-pad",
"added 120 packages in 5s",
];
const allPreservePatterns = [
...filter.preserve.errorPatterns,
...filter.preserve.summaryPatterns,
];
for (const line of preservedLines) {
const matchedByPreserve = allPreservePatterns.some((p) => {
try {
return new RegExp(p, "i").test(line);
} catch {
return false;
}
});
if (!matchedByPreserve) continue; // line not in preserve scope — skip conflict check
const droppedByDrop = filter.rules.dropPatterns.some((p) => {
try {
return new RegExp(p, "i").test(line);
} catch {
return false;
}
});
assert.equal(
droppedByDrop,
false,
`preserved line '${line}' must NOT be matched by a dropPattern`
);
}
});
it("unique line is NOT in dropPatterns", () => {
const filter = suggestFilter("npm install", samples);
const uniqueLine = "unique line only in sample 3";
const droppedByDrop = filter.rules.dropPatterns.some((p) => {
try {
return new RegExp(p, "i").test(uniqueLine);
} catch {
return false;
}
});
assert.equal(droppedByDrop, false, "unique lines must not appear in dropPatterns");
});
it("returns a filter with the expected RtkFilterPack shape", () => {
const filter = suggestFilter("npm install", samples);
// Check required top-level shape fields
assert.equal(typeof filter.id, "string");
assert.equal(typeof filter.label, "string");
assert.ok(filter.id.length > 0);
assert.ok(Array.isArray(filter.match.commands));
assert.ok(Array.isArray(filter.match.patterns));
assert.ok(Array.isArray(filter.match.outputTypes));
assert.ok(Array.isArray(filter.rules.dropPatterns));
assert.ok(Array.isArray(filter.preserve.errorPatterns));
assert.ok(Array.isArray(filter.preserve.summaryPatterns));
assert.equal(typeof filter.rules.stripAnsi, "boolean");
});
it("returns an empty-patterns filter for an empty sample set without throwing", () => {
const filter = suggestFilter("unknown-command", []);
assert.equal(filter.rules.dropPatterns.length, 0);
assert.ok(Array.isArray(filter.preserve.errorPatterns));
assert.ok(Array.isArray(filter.preserve.summaryPatterns));
});
});