@unerr-ai/unerr 0.1.6 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +70 -194
- package/dist/cli.js +39149 -36991
- package/package.json +9 -2
- package/dist/__tests__/architecture-guard.test.js +0 -122
- package/dist/__tests__/arg-validator.test.js +0 -205
- package/dist/__tests__/ast-extractor.test.js +0 -203
- package/dist/__tests__/auto-bootstrap.test.js +0 -280
- package/dist/__tests__/background-indexer.test.js +0 -228
- package/dist/__tests__/blast-radius-engine.test.js +0 -200
- package/dist/__tests__/bridge-isolation.test.js +0 -37
- package/dist/__tests__/budget-enforcer.test.js +0 -53
- package/dist/__tests__/cfg-test-detection-perf.test.js +0 -82
- package/dist/__tests__/change-narrative.test.js +0 -190
- package/dist/__tests__/check-commit.test.js +0 -258
- package/dist/__tests__/checksum.test.js +0 -34
- package/dist/__tests__/commit-watcher.test.js +0 -154
- package/dist/__tests__/community-detection.test.js +0 -179
- package/dist/__tests__/community-tools.test.js +0 -299
- package/dist/__tests__/components.test.js +0 -449
- package/dist/__tests__/compression-log.test.js +0 -174
- package/dist/__tests__/compression-quality-monitor.test.js +0 -40
- package/dist/__tests__/config-healer.test.js +0 -165
- package/dist/__tests__/context-ledger.test.js +0 -58
- package/dist/__tests__/convention-detector.test.js +0 -99
- package/dist/__tests__/convention-learner.test.js +0 -86
- package/dist/__tests__/correction-detector.test.js +0 -330
- package/dist/__tests__/daemon-autostart-install.test.js +0 -283
- package/dist/__tests__/daemon-bridge.test.js +0 -222
- package/dist/__tests__/daemon-dashboard.test.js +0 -202
- package/dist/__tests__/daemon-registry.test.js +0 -240
- package/dist/__tests__/daemon-supervisor.test.js +0 -318
- package/dist/__tests__/daemon-version-check.test.js +0 -275
- package/dist/__tests__/decision-point-detector.test.js +0 -98
- package/dist/__tests__/deep-link.test.js +0 -143
- package/dist/__tests__/disallowed-tools.test.js +0 -115
- package/dist/__tests__/drift-tracker.test.js +0 -582
- package/dist/__tests__/durability-scorer.test.js +0 -152
- package/dist/__tests__/efficiency-tracker.test.js +0 -65
- package/dist/__tests__/enrich.test.js +0 -144
- package/dist/__tests__/entity-rewind.test.js +0 -248
- package/dist/__tests__/ephemeral.test.js +0 -111
- package/dist/__tests__/exploration-cost.test.js +0 -93
- package/dist/__tests__/fact-generator.test.js +0 -197
- package/dist/__tests__/file-l0-graph.test.js +0 -244
- package/dist/__tests__/file-logger.test.js +0 -82
- package/dist/__tests__/file-outline.test.js +0 -141
- package/dist/__tests__/file-read-protocol.test.js +0 -188
- package/dist/__tests__/format-encoder.test.js +0 -233
- package/dist/__tests__/git-attribution.test.js +0 -259
- package/dist/__tests__/graph-temporal-joiner.test.js +0 -219
- package/dist/__tests__/health-grade-enhanced.test.js +0 -138
- package/dist/__tests__/health-map-data.test.js +0 -173
- package/dist/__tests__/helpers/mcp-harness.js +0 -45
- package/dist/__tests__/helpers/mcp-harness.test.js +0 -68
- package/dist/__tests__/hook-dedup.test.js +0 -112
- package/dist/__tests__/hook-runner.test.js +0 -253
- package/dist/__tests__/indexer-cfg.test.js +0 -185
- package/dist/__tests__/indexer-cross-file.test.js +0 -172
- package/dist/__tests__/indexer-extraction.test.js +0 -245
- package/dist/__tests__/indexer-incremental.test.js +0 -232
- package/dist/__tests__/indexer-language-expansion.test.js +0 -165
- package/dist/__tests__/init-push.test.js +0 -131
- package/dist/__tests__/instruction-writer.test.js +0 -179
- package/dist/__tests__/intelligence-integration.test.js +0 -217
- package/dist/__tests__/intent-correlator.test.js +0 -175
- package/dist/__tests__/intent-detector.test.js +0 -235
- package/dist/__tests__/intent-encoder.test.js +0 -167
- package/dist/__tests__/java-build-tool-detection.test.js +0 -174
- package/dist/__tests__/layer3-sprint-q.test.js +0 -160
- package/dist/__tests__/layer3-sprint-r.test.js +0 -91
- package/dist/__tests__/layer3-sprint-s.test.js +0 -183
- package/dist/__tests__/layer3-sprint-t.test.js +0 -201
- package/dist/__tests__/layer3-sprint-u.test.js +0 -174
- package/dist/__tests__/layer4-sprint-ba2.test.js +0 -354
- package/dist/__tests__/layer4-sprint-ba4.test.js +0 -84
- package/dist/__tests__/layer4-sprint-vs.test.js +0 -105
- package/dist/__tests__/ledger-chains.test.js +0 -162
- package/dist/__tests__/lifecycle-machine.test.js +0 -226
- package/dist/__tests__/local-chat-provider.test.js +0 -170
- package/dist/__tests__/local-convention-detector.test.js +0 -308
- package/dist/__tests__/local-embeddings.test.js +0 -422
- package/dist/__tests__/local-graph.test.js +0 -540
- package/dist/__tests__/local-indexer.test.js +0 -228
- package/dist/__tests__/local-intelligence-l3.test.js +0 -332
- package/dist/__tests__/local-llm.test.js +0 -253
- package/dist/__tests__/local-mode-offline.test.js +0 -187
- package/dist/__tests__/local-mode-stats.test.js +0 -273
- package/dist/__tests__/local-mode-tui.test.js +0 -343
- package/dist/__tests__/local-parse.test.js +0 -199
- package/dist/__tests__/log-tailer.test.js +0 -208
- package/dist/__tests__/loop-breaker.test.js +0 -276
- package/dist/__tests__/loop-miner.test.js +0 -226
- package/dist/__tests__/mcp-config.test.js +0 -126
- package/dist/__tests__/mcp-content-json.test.js +0 -10
- package/dist/__tests__/mcp-envelope.test.js +0 -124
- package/dist/__tests__/metrics-store.test.js +0 -223
- package/dist/__tests__/native-watcher.test.js +0 -191
- package/dist/__tests__/navigation-hooks-agent-aware.test.js +0 -145
- package/dist/__tests__/negative-knowledge.test.js +0 -116
- package/dist/__tests__/network-boundary.test.js +0 -190
- package/dist/__tests__/network-firewall.test.js +0 -112
- package/dist/__tests__/nudge-invariants.test.js +0 -160
- package/dist/__tests__/nudge-v2.test.js +0 -225
- package/dist/__tests__/offline-rewind.test.js +0 -251
- package/dist/__tests__/open-threads.test.js +0 -89
- package/dist/__tests__/output-compressor.test.js +0 -93
- package/dist/__tests__/pending-violations.test.js +0 -112
- package/dist/__tests__/persistence-effectiveness.test.js +0 -143
- package/dist/__tests__/provider-factory.test.js +0 -42
- package/dist/__tests__/providers.test.js +0 -24
- package/dist/__tests__/proxy.test.js +0 -314
- package/dist/__tests__/query-router.test.js +0 -1018
- package/dist/__tests__/reasoning-quality-route.test.js +0 -138
- package/dist/__tests__/redactor.test.js +0 -120
- package/dist/__tests__/resource-monitor.test.js +0 -57
- package/dist/__tests__/response-envelope.test.js +0 -100
- package/dist/__tests__/risk-classifier.test.js +0 -101
- package/dist/__tests__/risk-signal-scope.test.js +0 -75
- package/dist/__tests__/rule-evaluator.test.js +0 -280
- package/dist/__tests__/scip-decoder.test.js +0 -49
- package/dist/__tests__/scip-downloader.test.js +0 -201
- package/dist/__tests__/scip-merger.test.js +0 -103
- package/dist/__tests__/search-index.test.js +0 -422
- package/dist/__tests__/semantic-enrichment.test.js +0 -360
- package/dist/__tests__/session-brief-builder.test.js +0 -187
- package/dist/__tests__/session-context.test.js +0 -221
- package/dist/__tests__/session-continuity.test.js +0 -144
- package/dist/__tests__/session-dedup.test.js +0 -74
- package/dist/__tests__/session-event-wiring.test.js +0 -206
- package/dist/__tests__/session-events.test.js +0 -149
- package/dist/__tests__/session-legend.test.js +0 -20
- package/dist/__tests__/session-persistence.test.js +0 -131
- package/dist/__tests__/session-resume-block.test.js +0 -107
- package/dist/__tests__/session-resume.test.js +0 -97
- package/dist/__tests__/session-summary-writer.test.js +0 -134
- package/dist/__tests__/shadow-ledger.test.js +0 -203
- package/dist/__tests__/shell-classifier.test.js +0 -151
- package/dist/__tests__/shell-compression-floor.test.js +0 -189
- package/dist/__tests__/shell-compression-v2.test.js +0 -339
- package/dist/__tests__/shell-compressor.test.js +0 -35
- package/dist/__tests__/shell-hooks.test.js +0 -128
- package/dist/__tests__/shell-strategies.test.js +0 -644
- package/dist/__tests__/shell-tee.test.js +0 -133
- package/dist/__tests__/signal-dedup.test.js +0 -158
- package/dist/__tests__/signal-reinforcer.test.js +0 -77
- package/dist/__tests__/signal-scorer.test.js +0 -251
- package/dist/__tests__/signal-show-store.test.js +0 -108
- package/dist/__tests__/smart-truncate.test.js +0 -215
- package/dist/__tests__/snapshot-v2.test.js +0 -113
- package/dist/__tests__/sprint-l1-local-mode.test.js +0 -130
- package/dist/__tests__/sprint-l10-boot.test.js +0 -220
- package/dist/__tests__/sprint-l9-offline-commands.test.js +0 -189
- package/dist/__tests__/sprint-q-persistent-context.test.js +0 -198
- package/dist/__tests__/sprint-s1-wiring.test.js +0 -215
- package/dist/__tests__/sprint-s2-wiring.test.js +0 -256
- package/dist/__tests__/sprint-s3-wiring.test.js +0 -195
- package/dist/__tests__/sprint-s4-wiring.test.js +0 -213
- package/dist/__tests__/sprint-s6-hooks.test.js +0 -222
- package/dist/__tests__/sprint-s7-persistent.test.js +0 -263
- package/dist/__tests__/sprint-s8-value.test.js +0 -167
- package/dist/__tests__/sprint-s9-behavioral.test.js +0 -179
- package/dist/__tests__/sprint3-intelligence.test.js +0 -297
- package/dist/__tests__/sprint5-mcp-server.test.js +0 -136
- package/dist/__tests__/startup-display.test.js +0 -302
- package/dist/__tests__/startup-log-file.test.js +0 -97
- package/dist/__tests__/stash-manager.test.js +0 -229
- package/dist/__tests__/state-detector.test.js +0 -92
- package/dist/__tests__/status-dashboard.test.js +0 -142
- package/dist/__tests__/temporal-facts.test.js +0 -292
- package/dist/__tests__/temporal-routes.test.js +0 -142
- package/dist/__tests__/test-detector.test.js +0 -174
- package/dist/__tests__/theme.test.js +0 -72
- package/dist/__tests__/timeline-agents.test.js +0 -122
- package/dist/__tests__/timeline-bootstrap.test.js +0 -176
- package/dist/__tests__/timeline-filters.test.js +0 -193
- package/dist/__tests__/timeline-markers.test.js +0 -151
- package/dist/__tests__/timeline-routes.test.js +0 -156
- package/dist/__tests__/timeline-store.test.js +0 -171
- package/dist/__tests__/token-counter.test.js +0 -86
- package/dist/__tests__/token-estimator.test.js +0 -96
- package/dist/__tests__/token-flow-api.test.js +0 -239
- package/dist/__tests__/token-flow-instrumentation.test.js +0 -437
- package/dist/__tests__/token-flow-persistence.test.js +0 -356
- package/dist/__tests__/token-flow-routes.test.js +0 -199
- package/dist/__tests__/token-flow.test.js +0 -695
- package/dist/__tests__/tool-clusters.test.js +0 -177
- package/dist/__tests__/transport-mux.test.js +0 -283
- package/dist/__tests__/turn-segmenter.test.js +0 -166
- package/dist/__tests__/uninstall.test.js +0 -141
- package/dist/__tests__/warm-start-policy.test.js +0 -271
- package/dist/__tests__/wire-cap-nudge.test.js +0 -77
- package/dist/__tests__/worker-pool.test.js +0 -101
- package/dist/ui/assets/index-7gl3mIuY.css +0 -1
- package/dist/ui/assets/index-CX4FCWGT.js +0 -10
- package/dist/ui/assets/rolldown-runtime-S-ySWqyJ.js +0 -1
- package/dist/ui/assets/vis-network-NIJHUFI3.js +0 -908
- package/dist/ui/fonts/jetbrains-mono-latin-400-normal.woff +0 -0
- package/dist/ui/icon-wordmark.png +0 -0
- package/dist/ui/icon-wordmark.svg +0 -30
- package/dist/ui/icon.png +0 -0
- package/dist/ui/icon.svg +0 -25
- package/dist/ui/index.html +0 -15
- package/dist/ui/prototype-sandbox/index.html +0 -257
- package/dist/ui/screenshots/activity.png +0 -0
- package/dist/ui/screenshots/code-base-intelligence.png +0 -0
- package/dist/ui/screenshots/dashboard.png +0 -0
- package/dist/ui/screenshots/project-memory.png +0 -0
- package/dist/ui/screenshots/reasoning-quality.png +0 -0
- package/dist/ui/screenshots/reasoning-session.png +0 -0
- package/dist/ui/screenshots/token-session.png +0 -0
- package/dist/ui/screenshots/token-trace-main.png +0 -0
- package/dist/ui/screenshots/token-turn.png +0 -0
- package/dist/ui/unerr-wordmark.png +0 -0
- package/dist/ui/unerr-wordmark.svg +0 -9
- package/dist/ui/unerr.png +0 -0
- package/dist/ui/unerr.svg +0 -25
- package/dist/ui/web-app-manifest-192x192.png +0 -0
- package/dist/ui/web-app-manifest-512x512.png +0 -0
|
@@ -1,644 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { createCompressionQualityMonitor } from "../proxy/compression-quality-monitor.js";
|
|
3
|
-
import { compressShellOutput } from "../proxy/shell-compressor.js";
|
|
4
|
-
import { compressDiff } from "../proxy/shell-strategies/diff.js";
|
|
5
|
-
import { compressErrorDiagnostic } from "../proxy/shell-strategies/error-diagnostic.js";
|
|
6
|
-
import { compressKeyValue } from "../proxy/shell-strategies/key-value.js";
|
|
7
|
-
import { compressLogText } from "../proxy/shell-strategies/log-text.js";
|
|
8
|
-
import { compressOmni } from "../proxy/shell-strategies/omni.js";
|
|
9
|
-
import { compressProgress } from "../proxy/shell-strategies/progress.js";
|
|
10
|
-
import { compressStructured } from "../proxy/shell-strategies/structured.js";
|
|
11
|
-
import { compressTabular } from "../proxy/shell-strategies/tabular.js";
|
|
12
|
-
import { compressTestResults } from "../proxy/shell-strategies/test-results.js";
|
|
13
|
-
import { compressTreePaths } from "../proxy/shell-strategies/tree-paths.js";
|
|
14
|
-
describe("compressTabular", () => {
|
|
15
|
-
it("converts ps-style columns to pipe grid", () => {
|
|
16
|
-
const raw = "USER PID CMD\nroot 1 init\nroot 42 bash";
|
|
17
|
-
const out = compressTabular(raw);
|
|
18
|
-
expect(out).toContain("_shell_fmt:tabular");
|
|
19
|
-
expect(out).toContain("root|1|init");
|
|
20
|
-
});
|
|
21
|
-
it("detects header row and uses real column names", () => {
|
|
22
|
-
const raw = "USER PID CMD\nroot 1 init";
|
|
23
|
-
const out = compressTabular(raw);
|
|
24
|
-
expect(out).toContain("USER|PID|CMD");
|
|
25
|
-
expect(out).not.toContain("c0|c1");
|
|
26
|
-
});
|
|
27
|
-
it("prunes columns for docker ps with profile", () => {
|
|
28
|
-
const raw = [
|
|
29
|
-
"CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES",
|
|
30
|
-
"abc123 nginx nginx 2d ago Up 2d 80/tcp web",
|
|
31
|
-
].join("\n");
|
|
32
|
-
const out = compressTabular(raw, "docker ps");
|
|
33
|
-
expect(out).toContain("NAMES");
|
|
34
|
-
expect(out).toContain("STATUS");
|
|
35
|
-
// CREATED and COMMAND should be pruned
|
|
36
|
-
expect(out).not.toContain("CREATED");
|
|
37
|
-
});
|
|
38
|
-
it("falls back to c0/c1 for headerless output", () => {
|
|
39
|
-
const raw = "foo bar\nbaz qux";
|
|
40
|
-
const out = compressTabular(raw);
|
|
41
|
-
expect(out).toContain("c0|c1");
|
|
42
|
-
});
|
|
43
|
-
it("safety valve triggers when compression is too aggressive", () => {
|
|
44
|
-
// Create output where tabular parsing would strip most content
|
|
45
|
-
const lines = [
|
|
46
|
-
"HEADER1 HEADER2 HEADER3 HEADER4",
|
|
47
|
-
...Array.from({ length: 20 }, (_, i) => `value${i} data${i} info${i} extra-long-description-with-lots-of-detail-${i}-that-should-not-be-lost`),
|
|
48
|
-
].join("\n");
|
|
49
|
-
const out = compressTabular(lines);
|
|
50
|
-
// Should still contain _shell_fmt:tabular
|
|
51
|
-
expect(out).toContain("_shell_fmt:tabular");
|
|
52
|
-
// If safety valve triggered, all original content should be present
|
|
53
|
-
// (gentle fallback just strips blank lines)
|
|
54
|
-
expect(out.length).toBeGreaterThan(lines.length * 0.3);
|
|
55
|
-
});
|
|
56
|
-
it("ps aux preserves COMMAND column with spaces via splitWithLastRest", () => {
|
|
57
|
-
const raw = [
|
|
58
|
-
"USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND",
|
|
59
|
-
"root 1 0.0 0.1 169236 13128 ? Ss Jan01 0:05 /sbin/init splash",
|
|
60
|
-
"www-data 100 0.5 1.2 500000 25000 ? S 09:00 1:30 /usr/sbin/apache2 -k start",
|
|
61
|
-
].join("\n");
|
|
62
|
-
const out = compressTabular(raw, "ps aux");
|
|
63
|
-
expect(out).toContain("_shell_fmt:tabular");
|
|
64
|
-
// COMMAND column should be preserved with spaces
|
|
65
|
-
expect(out).toContain("/sbin/init splash");
|
|
66
|
-
expect(out).toContain("/usr/sbin/apache2 -k start");
|
|
67
|
-
});
|
|
68
|
-
it("docker ps preserves NAMES column", () => {
|
|
69
|
-
const raw = [
|
|
70
|
-
"CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES",
|
|
71
|
-
'abc123def456 nginx "nginx -g …" 2 days ago Up 2 days 80/tcp my-web-server',
|
|
72
|
-
].join("\n");
|
|
73
|
-
const out = compressTabular(raw, "docker ps");
|
|
74
|
-
expect(out).toContain("NAMES");
|
|
75
|
-
expect(out).toContain("my-web-server");
|
|
76
|
-
});
|
|
77
|
-
it("lsof preserves NAME column with file paths", () => {
|
|
78
|
-
const raw = [
|
|
79
|
-
"COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME",
|
|
80
|
-
"node 123 dev cwd DIR 1,5 4096 2 /home/dev/project",
|
|
81
|
-
"node 123 dev txt REG 1,5 50000 100 /usr/bin/node",
|
|
82
|
-
].join("\n");
|
|
83
|
-
const out = compressTabular(raw, "lsof");
|
|
84
|
-
expect(out).toContain("NAME");
|
|
85
|
-
expect(out).toContain("/home/dev/project");
|
|
86
|
-
});
|
|
87
|
-
it("systemctl list-units preserves DESCRIPTION column", () => {
|
|
88
|
-
const raw = [
|
|
89
|
-
"UNIT LOAD ACTIVE SUB DESCRIPTION",
|
|
90
|
-
"docker.service loaded active running Docker Application Container Engine",
|
|
91
|
-
"ssh.service loaded active running OpenBSD Secure Shell server",
|
|
92
|
-
].join("\n");
|
|
93
|
-
const out = compressTabular(raw, "systemctl list-units");
|
|
94
|
-
expect(out).toContain("DESCRIPTION");
|
|
95
|
-
expect(out).toContain("Docker Application Container Engine");
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
describe("compressLogText", () => {
|
|
99
|
-
it("passes through short output without format header", () => {
|
|
100
|
-
const out = compressLogText("line1\nline2\nline3");
|
|
101
|
-
expect(out).not.toContain("_shell_fmt:");
|
|
102
|
-
expect(out).toContain("line1");
|
|
103
|
-
});
|
|
104
|
-
it("deduplicates repeated log lines with ×N", () => {
|
|
105
|
-
const lines = [
|
|
106
|
-
...Array.from({ length: 50 }, () => "Compiling module v1.0"),
|
|
107
|
-
...Array.from({ length: 40 }, (_, i) => `step ${i}`),
|
|
108
|
-
];
|
|
109
|
-
const out = compressLogText(lines.join("\n"));
|
|
110
|
-
expect(out).toContain("[×50]");
|
|
111
|
-
expect(out).toContain("unique patterns");
|
|
112
|
-
});
|
|
113
|
-
it("preserves error lines verbatim", () => {
|
|
114
|
-
const lines = [
|
|
115
|
-
...Array.from({ length: 90 }, () => "INFO: processing"),
|
|
116
|
-
"ERROR: build failed",
|
|
117
|
-
"FATAL: out of memory",
|
|
118
|
-
];
|
|
119
|
-
const out = compressLogText(lines.join("\n"));
|
|
120
|
-
expect(out).toContain("ERROR: build failed");
|
|
121
|
-
expect(out).toContain("FATAL: out of memory");
|
|
122
|
-
});
|
|
123
|
-
it("normalizes timestamps for pattern matching", () => {
|
|
124
|
-
const lines = Array.from({ length: 100 }, (_, i) => `2024-01-01T00:00:${String(i).padStart(2, "0")}Z request handled`);
|
|
125
|
-
const out = compressLogText(lines.join("\n"));
|
|
126
|
-
expect(out).toContain("[×100]");
|
|
127
|
-
expect(out).toContain("unique patterns");
|
|
128
|
-
});
|
|
129
|
-
});
|
|
130
|
-
describe("compressTestResults", () => {
|
|
131
|
-
it("collapses many passing lines (generic fallback)", () => {
|
|
132
|
-
const raw = `${Array.from({ length: 20 }, () => "PASS ok").join("\n")}\nFAIL boom`;
|
|
133
|
-
const out = compressTestResults(raw);
|
|
134
|
-
expect(out).toContain("collapsed");
|
|
135
|
-
expect(out).toContain("FAIL boom");
|
|
136
|
-
});
|
|
137
|
-
it("vitest all-pass: single summary line", () => {
|
|
138
|
-
const input = [
|
|
139
|
-
" RUN v3.2.4 /project",
|
|
140
|
-
" ✓ src/a.test.ts > test1 1ms",
|
|
141
|
-
" ✓ src/a.test.ts > test2 2ms",
|
|
142
|
-
" ✓ src/b.test.ts > test3 1ms",
|
|
143
|
-
"",
|
|
144
|
-
" Test Files 2 passed (2)",
|
|
145
|
-
" Tests 3 passed (3)",
|
|
146
|
-
" Duration 1.5s",
|
|
147
|
-
].join("\n");
|
|
148
|
-
const out = compressTestResults(input, "vitest run");
|
|
149
|
-
expect(out).toContain("3 passed");
|
|
150
|
-
expect(out).not.toContain("✓");
|
|
151
|
-
expect(out.split("\n").length).toBeLessThan(6);
|
|
152
|
-
});
|
|
153
|
-
it("vitest with failures: summary + failure blocks", () => {
|
|
154
|
-
const input = [
|
|
155
|
-
" ✓ src/a.test.ts > pass1 1ms",
|
|
156
|
-
" ✓ src/a.test.ts > pass2 2ms",
|
|
157
|
-
"⎯⎯⎯ Failed Tests 1 ⎯⎯⎯",
|
|
158
|
-
" FAIL src/b.test.ts > broken test",
|
|
159
|
-
"AssertionError: expected 1 to be 2",
|
|
160
|
-
" ❯ src/b.test.ts:10:5",
|
|
161
|
-
"⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯",
|
|
162
|
-
" Test Files 1 failed | 1 passed (2)",
|
|
163
|
-
" Tests 1 failed | 2 passed (3)",
|
|
164
|
-
" Duration 2.5s",
|
|
165
|
-
].join("\n");
|
|
166
|
-
const out = compressTestResults(input, "vitest run");
|
|
167
|
-
expect(out).toContain("1 failed");
|
|
168
|
-
expect(out).toContain("2 passed");
|
|
169
|
-
expect(out).toContain("FAIL");
|
|
170
|
-
expect(out).toContain("AssertionError");
|
|
171
|
-
expect(out).not.toContain("✓");
|
|
172
|
-
});
|
|
173
|
-
it("vitest strips [unerr] noise lines", () => {
|
|
174
|
-
const input = [
|
|
175
|
-
" ✓ test 1ms",
|
|
176
|
-
"[unerr] ⚠ Prevented: Loop",
|
|
177
|
-
" ✓ test2 2ms",
|
|
178
|
-
" Test Files 1 passed (1)",
|
|
179
|
-
" Tests 2 passed (2)",
|
|
180
|
-
].join("\n");
|
|
181
|
-
const out = compressTestResults(input, "vitest run");
|
|
182
|
-
expect(out).not.toContain("[unerr]");
|
|
183
|
-
});
|
|
184
|
-
it("vitest exitCode=0 emits minimal summary", () => {
|
|
185
|
-
const input = [
|
|
186
|
-
" ✓ src/a.test.ts > test1 1ms",
|
|
187
|
-
" Test Files 1 passed (1)",
|
|
188
|
-
" Tests 5 passed (5)",
|
|
189
|
-
" Duration 1.0s",
|
|
190
|
-
].join("\n");
|
|
191
|
-
const out = compressTestResults(input, "vitest run", 0);
|
|
192
|
-
expect(out).toContain("5 passed");
|
|
193
|
-
expect(out.split("\n").length).toBeLessThanOrEqual(2);
|
|
194
|
-
});
|
|
195
|
-
it("pytest: extracts summary and failures", () => {
|
|
196
|
-
const input = [
|
|
197
|
-
"============================= test session starts ==============================",
|
|
198
|
-
"collected 10 items",
|
|
199
|
-
"test_foo.py::test_ok PASSED",
|
|
200
|
-
"test_foo.py::test_fail FAILED",
|
|
201
|
-
"=================================== FAILURES ===================================",
|
|
202
|
-
"_________________________________ test_fail __________________________________",
|
|
203
|
-
" assert 1 == 2",
|
|
204
|
-
"AssertionError",
|
|
205
|
-
"========================= 9 passed, 1 failed in 2.5s ==========================",
|
|
206
|
-
].join("\n");
|
|
207
|
-
const out = compressTestResults(input, "pytest");
|
|
208
|
-
expect(out).toContain("9 passed");
|
|
209
|
-
expect(out).toContain("1 failed");
|
|
210
|
-
expect(out).toContain("test_fail");
|
|
211
|
-
});
|
|
212
|
-
it("cargo test: extracts summary", () => {
|
|
213
|
-
const input = [
|
|
214
|
-
"test parser::test_basic ... ok",
|
|
215
|
-
"test parser::test_edge ... ok",
|
|
216
|
-
"test parser::test_fail ... FAILED",
|
|
217
|
-
"failures:",
|
|
218
|
-
"---- parser::test_fail stdout ----",
|
|
219
|
-
"thread panicked at 'assertion failed'",
|
|
220
|
-
"test result: ok. 2 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.2s",
|
|
221
|
-
].join("\n");
|
|
222
|
-
const out = compressTestResults(input, "cargo test");
|
|
223
|
-
expect(out).toContain("2 passed");
|
|
224
|
-
expect(out).toContain("1 failed");
|
|
225
|
-
expect(out).toContain("test_fail");
|
|
226
|
-
});
|
|
227
|
-
it("go test: extracts summary", () => {
|
|
228
|
-
const input = [
|
|
229
|
-
"--- PASS: TestFoo (0.00s)",
|
|
230
|
-
"--- PASS: TestBar (0.01s)",
|
|
231
|
-
"--- FAIL: TestBaz (0.02s)",
|
|
232
|
-
" expected X got Y",
|
|
233
|
-
"FAIL\tgithub.com/foo/bar\t0.045s",
|
|
234
|
-
].join("\n");
|
|
235
|
-
const out = compressTestResults(input, "go test ./...");
|
|
236
|
-
expect(out).toContain("2 passed");
|
|
237
|
-
expect(out).toContain("1 failed");
|
|
238
|
-
expect(out).toContain("TestBaz");
|
|
239
|
-
});
|
|
240
|
-
});
|
|
241
|
-
describe("compressProgress", () => {
|
|
242
|
-
it("truncates long npm-style output", () => {
|
|
243
|
-
const raw = Array.from({ length: 80 }, (_, i) => `step ${i}`).join("\n");
|
|
244
|
-
const out = compressProgress(raw);
|
|
245
|
-
expect(out).toContain("omitted");
|
|
246
|
-
expect(out.split("\n").length).toBeLessThan(40);
|
|
247
|
-
});
|
|
248
|
-
it("extracts summary line when present", () => {
|
|
249
|
-
const raw = [
|
|
250
|
-
...Array.from({ length: 50 }, (_, i) => `Downloading dep ${i}`),
|
|
251
|
-
"added 42 packages in 3s",
|
|
252
|
-
].join("\n");
|
|
253
|
-
const out = compressProgress(raw);
|
|
254
|
-
expect(out).toContain("added 42 packages");
|
|
255
|
-
// Should be very compact — just header + summary
|
|
256
|
-
expect(out.split("\n").length).toBeLessThanOrEqual(3);
|
|
257
|
-
});
|
|
258
|
-
});
|
|
259
|
-
describe("compressStructured (FE-F T2)", () => {
|
|
260
|
-
it("minifies JSON objects", () => {
|
|
261
|
-
const raw = '{\n "a": 1,\n "b": [2, 3]\n}';
|
|
262
|
-
const out = compressStructured(raw);
|
|
263
|
-
expect(out).toContain("_shell_fmt:structured_json");
|
|
264
|
-
expect(out).toContain('"a":1');
|
|
265
|
-
});
|
|
266
|
-
it("tags unstructured blobs", () => {
|
|
267
|
-
const out = compressStructured("hello world\n\n\nextra");
|
|
268
|
-
expect(out).toContain("_shell_fmt:structured_text");
|
|
269
|
-
});
|
|
270
|
-
it("depth-limits large JSON", () => {
|
|
271
|
-
// Create JSON large enough to exceed MEDIUM_JSON (8000 chars)
|
|
272
|
-
const items = Array.from({ length: 200 }, (_, i) => ({
|
|
273
|
-
id: i,
|
|
274
|
-
name: `item-${i}`,
|
|
275
|
-
data: { nested: true },
|
|
276
|
-
}));
|
|
277
|
-
const obj = { results: items, meta: { total: 200 } };
|
|
278
|
-
const raw = JSON.stringify(obj, null, 2);
|
|
279
|
-
expect(raw.length).toBeGreaterThan(8000);
|
|
280
|
-
const out = compressStructured(raw);
|
|
281
|
-
expect(out).toContain("_shell_fmt:structured_json");
|
|
282
|
-
expect(out).toContain("more items");
|
|
283
|
-
});
|
|
284
|
-
it("prunes large string values with noise keys", () => {
|
|
285
|
-
// Create JSON large enough to exceed SMALL_JSON (2000 chars) so pruning kicks in
|
|
286
|
-
const obj = {
|
|
287
|
-
name: "test",
|
|
288
|
-
certificate: "x".repeat(2000),
|
|
289
|
-
value: 42,
|
|
290
|
-
extra: Array.from({ length: 10 }, (_, i) => `field-${i}`),
|
|
291
|
-
};
|
|
292
|
-
const raw = JSON.stringify(obj, null, 2);
|
|
293
|
-
const out = compressStructured(raw);
|
|
294
|
-
expect(out).toContain("pruned");
|
|
295
|
-
expect(out).not.toContain("x".repeat(200));
|
|
296
|
-
});
|
|
297
|
-
});
|
|
298
|
-
describe("compressDiff (FE-F T4)", () => {
|
|
299
|
-
it("emits stats header and preserves body shape", () => {
|
|
300
|
-
const raw = `diff --git a/a.ts b/a.ts
|
|
301
|
-
@@ -1,2 +1,3 @@
|
|
302
|
-
export function foo() {
|
|
303
|
-
+ line
|
|
304
|
-
}
|
|
305
|
-
`;
|
|
306
|
-
const out = compressDiff(raw);
|
|
307
|
-
expect(out).toContain("_shell_diff:");
|
|
308
|
-
expect(out).toContain("files=");
|
|
309
|
-
});
|
|
310
|
-
it("annotates high-risk symbols when risk map provided", () => {
|
|
311
|
-
const risks = new Map([
|
|
312
|
-
["login", { name: "login", risk_level: "high", fan_in: 12 }],
|
|
313
|
-
]);
|
|
314
|
-
const raw = "@@ function login()";
|
|
315
|
-
const out = compressDiff(raw, risks);
|
|
316
|
-
expect(out).toContain("[HIGH-RISK:login");
|
|
317
|
-
});
|
|
318
|
-
});
|
|
319
|
-
describe("compressTreePaths (FE-F T5)", () => {
|
|
320
|
-
it("emits tree_paths header with rollup summary on large inputs", () => {
|
|
321
|
-
// R7 rewrite: <40 lines passes through unchanged; ≥40 lines hits the rollup path
|
|
322
|
-
const lines = [];
|
|
323
|
-
for (let i = 0; i < 50; i++)
|
|
324
|
-
lines.push(`src/foo/file_${i}.ts`);
|
|
325
|
-
const out = compressTreePaths(lines.join("\n"));
|
|
326
|
-
expect(out).toContain("_shell_fmt:tree_paths");
|
|
327
|
-
expect(out).toContain("rolled up");
|
|
328
|
-
expect(out).toContain("src/foo/");
|
|
329
|
-
});
|
|
330
|
-
it("collapses node_modules directory in large inputs", () => {
|
|
331
|
-
const lines = ["src/index.ts", "src/utils.ts"];
|
|
332
|
-
for (let i = 0; i < 50; i++)
|
|
333
|
-
lines.push(`node_modules/lodash/${i}.js`);
|
|
334
|
-
const out = compressTreePaths(lines.join("\n"));
|
|
335
|
-
expect(out).toContain("node_modules/");
|
|
336
|
-
expect(out).toContain("[collapsed]");
|
|
337
|
-
});
|
|
338
|
-
it("passes small inputs through unchanged", () => {
|
|
339
|
-
const raw = "src/foo/a.ts\nsrc/foo/b.ts\nsrc/foo/c.ts";
|
|
340
|
-
expect(compressTreePaths(raw)).toBe(raw);
|
|
341
|
-
});
|
|
342
|
-
});
|
|
343
|
-
describe("compressKeyValue (FE-F T6)", () => {
|
|
344
|
-
it("drops noisy keys and shortens PATH", () => {
|
|
345
|
-
const raw = "PATH=/a:/b:/c:/d:/e\nHOME=/Users/x\nOLDPWD=/tmp\nFOO=bar";
|
|
346
|
-
const out = compressKeyValue(raw);
|
|
347
|
-
expect(out).toContain("_shell_fmt:key_value");
|
|
348
|
-
expect(out).not.toContain("OLDPWD");
|
|
349
|
-
expect(out).toContain("HOME=");
|
|
350
|
-
});
|
|
351
|
-
it("drops terminal and locale noise keys", () => {
|
|
352
|
-
const raw = "TERM=xterm-256color\nLC_ALL=en_US.UTF-8\nGOPATH=/go\nFOO=bar";
|
|
353
|
-
const out = compressKeyValue(raw);
|
|
354
|
-
expect(out).not.toContain("TERM=");
|
|
355
|
-
expect(out).not.toContain("LC_ALL=");
|
|
356
|
-
expect(out).toContain("GOPATH=");
|
|
357
|
-
expect(out).toContain("FOO=bar");
|
|
358
|
-
});
|
|
359
|
-
it("truncates long values", () => {
|
|
360
|
-
const raw = `LONG_VAR=${"x".repeat(500)}`;
|
|
361
|
-
const out = compressKeyValue(raw);
|
|
362
|
-
expect(out).toContain("500 chars");
|
|
363
|
-
expect(out).not.toContain("x".repeat(200));
|
|
364
|
-
});
|
|
365
|
-
// B21 regression: `git branch --show-current` (mapped to key_value by the
|
|
366
|
-
// classifier on the `git branch` prefix) produces a single bare word.
|
|
367
|
-
// No key=value or key:value rows means no compression possible — emitting
|
|
368
|
-
// the 20-byte header on a 5-byte payload is strictly net-negative.
|
|
369
|
-
it("passthrough on output with no parseable key:value rows (B21)", () => {
|
|
370
|
-
const raw = "main\n";
|
|
371
|
-
const out = compressKeyValue(raw);
|
|
372
|
-
expect(out).toBe(raw);
|
|
373
|
-
expect(out).not.toContain("_shell_fmt:");
|
|
374
|
-
});
|
|
375
|
-
it("passthrough on multi-line plain text with no key:value rows", () => {
|
|
376
|
-
const raw = "one\ntwo\nthree\n";
|
|
377
|
-
const out = compressKeyValue(raw);
|
|
378
|
-
expect(out).toBe(raw);
|
|
379
|
-
expect(out).not.toContain("_shell_fmt:");
|
|
380
|
-
});
|
|
381
|
-
it("still emits header when at least one key=value row exists", () => {
|
|
382
|
-
const raw = "FOO=bar\nplain line";
|
|
383
|
-
const out = compressKeyValue(raw);
|
|
384
|
-
expect(out).toContain("_shell_fmt:key_value");
|
|
385
|
-
expect(out).toContain("FOO=bar");
|
|
386
|
-
});
|
|
387
|
-
it("still emits header for colon-format with parseable rows", () => {
|
|
388
|
-
const raw = "Name: my-pod\nNamespace: default\nStatus: Running";
|
|
389
|
-
const out = compressKeyValue(raw);
|
|
390
|
-
expect(out).toContain("_shell_fmt:key_value");
|
|
391
|
-
expect(out).toContain("Name: my-pod");
|
|
392
|
-
});
|
|
393
|
-
});
|
|
394
|
-
describe("compressErrorDiagnostic (FE-F T7)", () => {
|
|
395
|
-
it("node_stack: deduplicates identical stack frames", () => {
|
|
396
|
-
const raw = `Error: boom
|
|
397
|
-
at x (f.ts:1)
|
|
398
|
-
at x (f.ts:1)
|
|
399
|
-
at x (f.ts:1)
|
|
400
|
-
at y (g.ts:2)`;
|
|
401
|
-
const out = compressErrorDiagnostic(raw);
|
|
402
|
-
expect(out).toContain("_shell_fmt:error_diagnostic");
|
|
403
|
-
expect(out).toContain("Error: boom");
|
|
404
|
-
expect(out).toContain("at x (f.ts:1)");
|
|
405
|
-
expect(out).toContain("at y (g.ts:2)");
|
|
406
|
-
});
|
|
407
|
-
it("tsc: groups errors by TS code", () => {
|
|
408
|
-
const raw = [
|
|
409
|
-
"src/a.ts(10,5): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.",
|
|
410
|
-
"src/b.ts(20,3): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.",
|
|
411
|
-
"src/c.ts(5,1): error TS2322: Type 'undefined' is not assignable to type 'string'.",
|
|
412
|
-
"Found 3 errors in 3 files.",
|
|
413
|
-
].join("\n");
|
|
414
|
-
const out = compressErrorDiagnostic(raw, "tsc --noEmit");
|
|
415
|
-
expect(out).toContain("TS2345");
|
|
416
|
-
expect(out).toContain("1 more in 2 file(s)");
|
|
417
|
-
expect(out).toContain("Found 3 errors");
|
|
418
|
-
});
|
|
419
|
-
it("eslint: groups by rule", () => {
|
|
420
|
-
const raw = [
|
|
421
|
-
"/path/to/file.ts",
|
|
422
|
-
" 10:5 error Unexpected console statement no-console",
|
|
423
|
-
" 23:1 error Unexpected console statement no-console",
|
|
424
|
-
"/path/to/other.ts",
|
|
425
|
-
" 5:3 warning Missing return type @typescript-eslint/explicit-function-return-type",
|
|
426
|
-
"",
|
|
427
|
-
"✖ 3 problems (2 errors, 1 warning)",
|
|
428
|
-
].join("\n");
|
|
429
|
-
const out = compressErrorDiagnostic(raw, "eslint .");
|
|
430
|
-
expect(out).toContain("no-console");
|
|
431
|
-
expect(out).toContain("2 occurrences");
|
|
432
|
-
expect(out).toContain("✖ 3 problems");
|
|
433
|
-
});
|
|
434
|
-
it("node stack: filters node:internal frames", () => {
|
|
435
|
-
const raw = [
|
|
436
|
-
"Error: connection refused",
|
|
437
|
-
" at connect (src/db.ts:10:5)",
|
|
438
|
-
" at node:internal/process/task_queues:1:2",
|
|
439
|
-
" at node:internal/process/task_queues:3:4",
|
|
440
|
-
" at main (src/index.ts:5:3)",
|
|
441
|
-
].join("\n");
|
|
442
|
-
const out = compressErrorDiagnostic(raw);
|
|
443
|
-
expect(out).toContain("connection refused");
|
|
444
|
-
expect(out).toContain("src/db.ts");
|
|
445
|
-
expect(out).not.toContain("node:internal");
|
|
446
|
-
});
|
|
447
|
-
});
|
|
448
|
-
describe("compressShellOutput integration", () => {
|
|
449
|
-
it("compresses docker ps output", async () => {
|
|
450
|
-
const txt = "CONTAINER ID IMAGE COMMAND\nabc123 nginx nginx -g";
|
|
451
|
-
const r = await compressShellOutput("docker ps", txt, {
|
|
452
|
-
persistStats: false,
|
|
453
|
-
});
|
|
454
|
-
expect(r.text).toContain("_shell_fmt");
|
|
455
|
-
expect(r.classification.category).toBe("tabular");
|
|
456
|
-
});
|
|
457
|
-
it("applies omni compression for low-confidence output", async () => {
|
|
458
|
-
// Small input passes through bare (no header) — verify only that the omni
|
|
459
|
-
// path was chosen via classification metadata, not via the absent format tag.
|
|
460
|
-
const r = await compressShellOutput("unknown", "x", {
|
|
461
|
-
persistStats: false,
|
|
462
|
-
});
|
|
463
|
-
expect(r.classification.confidence).toBeLessThan(0.7);
|
|
464
|
-
expect(r.text).toContain("x");
|
|
465
|
-
expect(r.text).not.toContain("_shell_fmt:");
|
|
466
|
-
});
|
|
467
|
-
it("records shell categories on quality monitor when provided", async () => {
|
|
468
|
-
const mon = createCompressionQualityMonitor();
|
|
469
|
-
await compressShellOutput("docker ps", "CONTAINER ID IMAGE\nabc123 nginx", { persistStats: false, qualityMonitor: mon });
|
|
470
|
-
expect(mon.getRetention("shell_tabular")).toBeGreaterThanOrEqual(0.4);
|
|
471
|
-
});
|
|
472
|
-
});
|
|
473
|
-
describe("compressOmni", () => {
|
|
474
|
-
it("passes small output through without format header", () => {
|
|
475
|
-
const out = compressOmni("hello\nworld");
|
|
476
|
-
expect(out).not.toContain("_shell_fmt:");
|
|
477
|
-
expect(out).toContain("hello");
|
|
478
|
-
expect(out).toContain("world");
|
|
479
|
-
});
|
|
480
|
-
it("Tier 1: collapses 3+ blank lines to 1", () => {
|
|
481
|
-
// Need enough lines to exceed SMALL_THRESHOLD (40)
|
|
482
|
-
const filler = Array.from({ length: 45 }, (_, i) => `line_${i}_unique`);
|
|
483
|
-
filler.splice(20, 0, "", "", "", "", "");
|
|
484
|
-
const out = compressOmni(filler.join("\n"));
|
|
485
|
-
expect(out).not.toContain("\n\n\n");
|
|
486
|
-
expect(out).toContain("line_0_unique");
|
|
487
|
-
});
|
|
488
|
-
it("Tier 2: deduplicates 3+ consecutive identical lines", () => {
|
|
489
|
-
const lines = Array.from({ length: 100 }, () => "Compiling crate v1.0");
|
|
490
|
-
const out = compressOmni(lines.join("\n"));
|
|
491
|
-
expect(out).toContain("[×100]");
|
|
492
|
-
expect(out.split("\n").length).toBeLessThan(10);
|
|
493
|
-
});
|
|
494
|
-
it("Tier 2: keeps runs shorter than 3 intact", () => {
|
|
495
|
-
const lines = ["alpha", "alpha", "bravo", "bravo", "charlie"];
|
|
496
|
-
// Filler lines must be unique even after pattern normalization
|
|
497
|
-
const filler = Array.from({ length: 80 }, (_, i) => `unique_${String.fromCharCode(65 + (i % 26))}${String.fromCharCode(97 + Math.floor(i / 26))}_line`);
|
|
498
|
-
const out = compressOmni([...filler, ...lines].join("\n"));
|
|
499
|
-
// Consecutive dedup threshold is 3, so runs of 2 should stay
|
|
500
|
-
expect(out).toContain("alpha");
|
|
501
|
-
expect(out).toContain("bravo");
|
|
502
|
-
});
|
|
503
|
-
it("Tier 2: skips output under 80 lines", () => {
|
|
504
|
-
const lines = Array.from({ length: 30 }, () => "same");
|
|
505
|
-
expect(compressOmni(lines.join("\n"))).not.toContain("[×");
|
|
506
|
-
});
|
|
507
|
-
it("Tier 3: truncates >200 lines with head + tail", () => {
|
|
508
|
-
// Each line must be unique even after normalization to avoid pattern-dedup
|
|
509
|
-
const lines = Array.from({ length: 500 }, (_, i) => `processing file_${String.fromCharCode(65 + (i % 26))}_module_${String.fromCharCode(97 + (i % 26))}`);
|
|
510
|
-
const out = compressOmni(lines.join("\n"));
|
|
511
|
-
// Pattern-dedup or truncation should reduce the output
|
|
512
|
-
expect(out.split("\n").length).toBeLessThan(400);
|
|
513
|
-
});
|
|
514
|
-
it("Tier 3: preserves diagnostic lines from middle", () => {
|
|
515
|
-
// Use truly unique lines to prevent pattern-dedup from collapsing
|
|
516
|
-
const lines = Array.from({ length: 500 }, (_, i) => {
|
|
517
|
-
if (i === 200)
|
|
518
|
-
return "ERROR: build failed";
|
|
519
|
-
if (i === 300)
|
|
520
|
-
return "FATAL: out of memory";
|
|
521
|
-
return `processing_unique_module_${String.fromCharCode(65 + (i % 26))}${String.fromCharCode(97 + ((i * 7) % 26))}_v${i}`;
|
|
522
|
-
});
|
|
523
|
-
const out = compressOmni(lines.join("\n"));
|
|
524
|
-
expect(out).toContain("ERROR: build failed");
|
|
525
|
-
expect(out).toContain("FATAL: out of memory");
|
|
526
|
-
});
|
|
527
|
-
it("character cap: limits very wide output", () => {
|
|
528
|
-
const lines = Array.from({ length: 100 }, () => "x".repeat(10_000));
|
|
529
|
-
const out = compressOmni(lines.join("\n"));
|
|
530
|
-
expect(out.length).toBeLessThan(60_000);
|
|
531
|
-
});
|
|
532
|
-
it("shows line savings in header for large compression", () => {
|
|
533
|
-
const lines = Array.from({ length: 200 }, () => "Downloading...");
|
|
534
|
-
const out = compressOmni(lines.join("\n"));
|
|
535
|
-
expect(out).toMatch(/_shell_fmt:omni \(\d+→\d+ lines\)/);
|
|
536
|
-
});
|
|
537
|
-
// ── Hardening: edge cases that must never crash ──
|
|
538
|
-
it("handles empty string", () => {
|
|
539
|
-
const out = compressOmni("");
|
|
540
|
-
expect(out).toBeDefined();
|
|
541
|
-
expect(out).not.toContain("_shell_fmt:");
|
|
542
|
-
});
|
|
543
|
-
it("handles single line", () => {
|
|
544
|
-
const out = compressOmni("just one line");
|
|
545
|
-
expect(out).toContain("just one line");
|
|
546
|
-
expect(out).not.toContain("_shell_fmt:");
|
|
547
|
-
});
|
|
548
|
-
it("handles only blank lines", () => {
|
|
549
|
-
const out = compressOmni("\n\n\n\n\n\n\n\n\n\n");
|
|
550
|
-
expect(out).not.toContain("_shell_fmt:");
|
|
551
|
-
});
|
|
552
|
-
it("handles only whitespace lines", () => {
|
|
553
|
-
const lines = Array.from({ length: 50 }, () => " \t ");
|
|
554
|
-
const out = compressOmni(lines.join("\n"));
|
|
555
|
-
expect(out).toContain("_shell_fmt:omni");
|
|
556
|
-
});
|
|
557
|
-
it("handles Windows CRLF line endings", () => {
|
|
558
|
-
const lines = Array.from({ length: 50 }, (_, i) => `line ${i}`);
|
|
559
|
-
const out = compressOmni(lines.join("\r\n"));
|
|
560
|
-
expect(out).toContain("_shell_fmt:omni");
|
|
561
|
-
expect(out).not.toContain("\r");
|
|
562
|
-
});
|
|
563
|
-
it("handles mixed CRLF and LF endings", () => {
|
|
564
|
-
const lines = ["line1\r\n", "line2\n", "line3\r\n", "line4\n"];
|
|
565
|
-
const out = compressOmni(lines.join("").repeat(15));
|
|
566
|
-
expect(out).toContain("_shell_fmt:omni");
|
|
567
|
-
});
|
|
568
|
-
it("handles binary-like content with null bytes gracefully", () => {
|
|
569
|
-
const lines = Array.from({ length: 50 }, (_, i) => `data\x00\x01\x02 row ${i}`);
|
|
570
|
-
const out = compressOmni(lines.join("\n"));
|
|
571
|
-
expect(out).toContain("_shell_fmt:omni");
|
|
572
|
-
});
|
|
573
|
-
it("handles very long single line (minified JS)", () => {
|
|
574
|
-
const line = `var a=${"x".repeat(100_000)};`;
|
|
575
|
-
const out = compressOmni(line);
|
|
576
|
-
// Single line is under SMALL_THRESHOLD so no header is added, but capChars
|
|
577
|
-
// still trims wide input; the omission marker confirms truncation happened.
|
|
578
|
-
expect(out).toContain("chars omitted");
|
|
579
|
-
expect(out.length).toBeLessThan(60_000);
|
|
580
|
-
});
|
|
581
|
-
it("handles lines with only ANSI escape codes", () => {
|
|
582
|
-
const lines = Array.from({ length: 50 }, () => "\x1b[31m\x1b[0m");
|
|
583
|
-
const out = compressOmni(lines.join("\n"));
|
|
584
|
-
expect(out).toContain("_shell_fmt:omni");
|
|
585
|
-
});
|
|
586
|
-
it("handles mixed diagnostic and normal lines at boundaries", () => {
|
|
587
|
-
// First and last lines are diagnostic
|
|
588
|
-
const lines = [
|
|
589
|
-
"ERROR: start",
|
|
590
|
-
...Array.from({ length: 248 }, (_, i) => `normal line ${i}`),
|
|
591
|
-
"FATAL: end",
|
|
592
|
-
];
|
|
593
|
-
const out = compressOmni(lines.join("\n"));
|
|
594
|
-
expect(out).toContain("ERROR: start");
|
|
595
|
-
expect(out).toContain("FATAL: end");
|
|
596
|
-
});
|
|
597
|
-
it("handles all-diagnostic lines", () => {
|
|
598
|
-
const lines = Array.from({ length: 60 }, (_, i) => `ERROR: failure ${i}`);
|
|
599
|
-
const out = compressOmni(lines.join("\n"));
|
|
600
|
-
expect(out).toContain("_shell_fmt:omni");
|
|
601
|
-
expect(out).toContain("ERROR: failure");
|
|
602
|
-
});
|
|
603
|
-
it("pattern dedup handles UUIDs, timestamps, IPs", () => {
|
|
604
|
-
const lines = Array.from({ length: 60 }, (_, i) => `2024-01-15T10:00:${String(i).padStart(2, "0")}Z [192.168.1.${i}] Processing batch abc${String(i).padStart(4, "0")}def`);
|
|
605
|
-
const out = compressOmni(lines.join("\n"));
|
|
606
|
-
expect(out).toContain("_shell_fmt:omni");
|
|
607
|
-
// Pattern dedup should collapse these
|
|
608
|
-
expect(out.split("\n").length).toBeLessThan(40);
|
|
609
|
-
});
|
|
610
|
-
it("handles Unicode content (CJK, emoji)", () => {
|
|
611
|
-
const lines = Array.from({ length: 50 }, (_, i) => `处理文件 ${i}: 成功 ✓`);
|
|
612
|
-
const out = compressOmni(lines.join("\n"));
|
|
613
|
-
expect(out).toContain("_shell_fmt:omni");
|
|
614
|
-
expect(out).toContain("处理文件");
|
|
615
|
-
});
|
|
616
|
-
it("handles tab-separated output (TSV-like)", () => {
|
|
617
|
-
const lines = Array.from({ length: 50 }, (_, i) => `col1_${i}\tcol2_${i}\tcol3_${i}`);
|
|
618
|
-
const out = compressOmni(lines.join("\n"));
|
|
619
|
-
expect(out).toContain("_shell_fmt:omni");
|
|
620
|
-
});
|
|
621
|
-
it("handles cargo build output with repeating compile lines", () => {
|
|
622
|
-
const lines = [
|
|
623
|
-
...Array.from({ length: 80 }, (_, i) => ` Compiling dep-${i} v0.${i}.0`),
|
|
624
|
-
" Compiling my-project v1.0.0",
|
|
625
|
-
" Finished release [optimized] target(s) in 45.2s",
|
|
626
|
-
];
|
|
627
|
-
const out = compressOmni(lines.join("\n"));
|
|
628
|
-
expect(out).toContain("_shell_fmt:omni");
|
|
629
|
-
expect(out.split("\n").length).toBeLessThan(50);
|
|
630
|
-
});
|
|
631
|
-
it("handles webpack/vite build output", () => {
|
|
632
|
-
const lines = [
|
|
633
|
-
"vite v5.0.0 building for production...",
|
|
634
|
-
...Array.from({ length: 100 }, (_, i) => `transforming (${i + 1}) src/components/Component${i}.tsx`),
|
|
635
|
-
"✓ 100 modules transformed.",
|
|
636
|
-
"dist/assets/index-abc123.js 125.4 kB │ gzip: 42.1 kB",
|
|
637
|
-
"✓ built in 3.21s",
|
|
638
|
-
];
|
|
639
|
-
const out = compressOmni(lines.join("\n"));
|
|
640
|
-
expect(out).toContain("_shell_fmt:omni");
|
|
641
|
-
// Pattern dedup should compress the repeating transform lines
|
|
642
|
-
expect(out.split("\n").length).toBeLessThan(lines.length);
|
|
643
|
-
});
|
|
644
|
-
});
|