File size: 8,853 Bytes
88c4c60 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 | // Tests for PR #1175: build output filter + porcelain regex fix
// Covers edge cases: porcelain workdir-only, cargo misdetection, false positives, compression
import { describe, it, expect } from "vitest";
import { autoDetectFilter } from "../../open-sse/rtk/autodetect.js";
import { buildOutput } from "../../open-sse/rtk/filters/buildOutput.js";
import { gitStatus } from "../../open-sse/rtk/filters/gitStatus.js";
describe("PR #1175 - buildOutput filter detection", () => {
it("detects npm install output", () => {
const input = [
"npm warn deprecated har-validator@5.1.5: this library is no longer supported",
"npm warn deprecated uuid@3.4.0: uuid@10 and below is no longer supported",
"npm warn deprecated request@2.88.2: request has been deprecated",
"added 47 packages, and audited 48 packages in 13s",
"3 packages are looking for funding",
" run `npm fund` for details",
"4 vulnerabilities (2 moderate, 2 critical)",
"Run `npm audit` for details."
].join("\n");
const filter = autoDetectFilter(input);
expect(filter).toBe(buildOutput);
});
it("detects cargo build output (no longer misdetected as git-status)", () => {
const input = [
" Compiling proc-macro2 v1.0.95",
" Compiling unicode-ident v1.0.18",
" Compiling quote v1.0.40",
" Compiling syn v2.0.104",
" Compiling my-project v0.1.0 (/home/user/my-project)",
" Finished `dev` profile [unoptimized + debuginfo] target(s) in 12.34s"
].join("\n");
const filter = autoDetectFilter(input);
expect(filter).toBe(buildOutput);
expect(filter).not.toBe(gitStatus);
});
});
describe("PR #1175 - buildOutput compression behavior", () => {
it("compresses npm install with deprecations", () => {
const input = [
"npm warn deprecated har-validator@5.1.5: this library is no longer supported",
"npm warn deprecated uuid@3.4.0: uuid@10 and below is no longer supported",
"npm warn deprecated request@2.88.2: request has been deprecated",
"added 47 packages, and audited 48 packages in 13s",
"3 packages are looking for funding",
" run `npm fund` for details",
"4 vulnerabilities (2 moderate, 2 critical)",
"Run `npm audit` for details."
].join("\n");
const out = buildOutput(input);
// Minimal fix: keep first 3 deprecations verbatim (no truncation needed since count == 3)
expect(out).toContain("har-validator@5.1.5");
expect(out).toContain("uuid@3.4.0");
expect(out).toContain("request@2.88.2");
expect(out).toContain("added 47 packages");
expect(out).toContain("4 vulnerabilities");
// 3 deprecations kept verbatim → no size win on this small input
expect(out.length).toBeLessThanOrEqual(input.length);
});
it("compresses cargo build output", () => {
const input = [
" Compiling proc-macro2 v1.0.95",
" Compiling unicode-ident v1.0.18",
" Compiling quote v1.0.40",
" Compiling syn v2.0.104",
" Compiling serde v1.0.219",
" Compiling serde_derive v1.0.219",
" Compiling serde_json v1.0.140",
" Compiling tokio v1.45.0",
" Compiling hyper v1.6.0",
" Compiling my-project v0.1.0 (/home/user/my-project)",
" Finished `dev` profile [unoptimized + debuginfo] target(s) in 12.34s"
].join("\n");
const out = buildOutput(input);
expect(out).toContain("Compiled 10 packages");
expect(out).toContain("Finished");
expect(out.length).toBeLessThan(input.length * 0.5);
});
it("keeps cargo errors verbatim", () => {
const input = [
" Compiling foo v0.1.0",
"error[E0308]: mismatched types",
" --> src/main.rs:5:9",
" |",
"5 | let x: u32 = \"hello\";",
" | --- ^^^^^^^ expected `u32`, found `&str`",
"error: aborting due to previous error"
].join("\n");
const out = buildOutput(input);
expect(out).toContain("error[E0308]");
expect(out).toContain("error: aborting");
});
it("keeps maven BUILD FAILED as error", () => {
const input = [
"[INFO] Scanning for projects...",
"[ERROR] Failed to execute goal",
"[ERROR] Could not resolve dependencies",
"BUILD FAILED"
].join("\n");
const out = buildOutput(input);
expect(out).toContain("[ERROR] Failed to execute goal");
expect(out).toContain("BUILD FAILED");
});
});
describe("PR #1175 - porcelain regex fix edge cases", () => {
it("git status --porcelain workdir-only (space first char) STILL detects as gitStatus (minimal fix preserved old regex)", () => {
const input = [
" M src/a.js",
" M src/b.js",
" D src/c.js",
"?? new.js"
].join("\n");
const filter = autoDetectFilter(input);
expect(filter).toBe(gitStatus);
});
it("git status --porcelain with staged (status code first char) still detects", () => {
const input = [
"M src/a.js",
"A src/new.js",
"?? untracked.js",
"M src/b.js"
].join("\n");
const filter = autoDetectFilter(input);
expect(filter).toBe(gitStatus);
});
it("cargo Compiling lines NOT detected as git-status (porcelain false positive fix)", () => {
const input = [
" Compiling proc-macro2 v1.0.95",
" Compiling unicode-ident v1.0.18",
" Compiling quote v1.0.40"
].join("\n");
const filter = autoDetectFilter(input);
expect(filter).not.toBe(gitStatus);
});
it("long-form git status with 'On branch' always detects", () => {
const input = [
"On branch main",
"Your branch is up to date with 'origin/main'.",
"",
"Changes not staged for commit:",
"\tmodified: src/a.js"
].join("\n");
const filter = autoDetectFilter(input);
expect(filter).toBe(gitStatus);
});
});
describe("PR #1175 - false positive risks", () => {
it("generic app log with 'ERROR:' triggers buildOutput (potential false positive)", () => {
const input = [
"2026-05-16 10:00:00 INFO Server started on port 3000",
"2026-05-16 10:00:05 INFO Request received: GET /api/users",
"ERROR: Database connection timeout",
"2026-05-16 10:00:10 INFO Retrying connection",
"2026-05-16 10:00:15 INFO Connection restored"
].join("\n");
const filter = autoDetectFilter(input);
// Document actual behavior — may detect as buildOutput
console.log("[generic-error-log] detected:", filter?.filterName || "null");
// Whatever the detection, the filter should NOT corrupt the data
if (filter === buildOutput) {
const out = buildOutput(input);
expect(out).toContain("ERROR: Database connection timeout");
}
});
it("generic 'Compiling templates' (non-build context) triggers buildOutput", () => {
const input = [
"[INFO] Starting application",
"[INFO] Compiling templates for view layer",
"[INFO] Compiling assets for production",
"[INFO] Application ready"
].join("\n");
const filter = autoDetectFilter(input);
console.log("[compiling-templates] detected:", filter?.filterName || "null");
if (filter === buildOutput) {
const out = buildOutput(input);
// Should at least preserve structure (count compiling)
expect(out).toContain("Compiled");
}
});
it("plain text with no patterns falls through (no false positive)", () => {
const input = [
"Hello world",
"This is a normal message",
"Nothing special here"
].join("\n");
const filter = autoDetectFilter(input);
expect(filter).not.toBe(buildOutput);
});
});
describe("PR #1175 - safety: no data corruption", () => {
it("empty input returns input", () => {
expect(buildOutput("")).toBe("");
});
it("input with only errors preserves all errors", () => {
const input = "npm ERR! code ENOENT\nnpm ERR! syscall open\nnpm ERR! path /tmp/foo";
const out = buildOutput(input);
expect(out).toContain("npm ERR! code ENOENT");
expect(out).toContain("npm ERR! syscall open");
expect(out).toContain("npm ERR! path /tmp/foo");
});
it("input with no recognized patterns returns input (fallback)", () => {
const input = "random text\nmore random\nstill random";
const out = buildOutput(input);
// When nothing matches, returns input via `|| input`
expect(out).toBe(input);
});
it("limits warnings to 5 + summary line", () => {
const warnings = [];
for (let i = 0; i < 10; i++) warnings.push(`npm warn config foo${i} something`);
const input = warnings.join("\n");
const out = buildOutput(input);
expect(out).toContain("npm warn config foo0");
expect(out).toContain("npm warn config foo4");
expect(out).toContain("... +5 more warnings");
expect(out).not.toContain("npm warn config foo9");
});
});
|