pi-lens 3.6.2 → 3.6.4
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/CHANGELOG.md +10 -2
- package/package.json +4 -4
- package/tsconfig.json +1 -1
- package/clients/__tests__/file-time.test.js +0 -216
- package/clients/__tests__/file-time.test.ts +0 -276
- package/clients/__tests__/format-service.test.js +0 -245
- package/clients/__tests__/format-service.test.ts +0 -339
- package/clients/__tests__/formatters.test.js +0 -271
- package/clients/__tests__/formatters.test.ts +0 -401
- package/clients/agent-behavior-client.js +0 -110
- package/clients/agent-behavior-client.test.js +0 -94
- package/clients/agent-behavior-client.test.ts +0 -116
- package/clients/amain-types.js +0 -164
- package/clients/architect-client.js +0 -291
- package/clients/ast-grep-client.js +0 -253
- package/clients/ast-grep-parser.js +0 -84
- package/clients/ast-grep-rule-manager.js +0 -89
- package/clients/ast-grep-types.js +0 -9
- package/clients/auto-loop.js +0 -131
- package/clients/biome-client.js +0 -420
- package/clients/biome-client.test.js +0 -144
- package/clients/biome-client.test.ts +0 -163
- package/clients/cache/rule-cache.js +0 -72
- package/clients/cache-manager.js +0 -245
- package/clients/cache-manager.test.js +0 -197
- package/clients/cache-manager.test.ts +0 -299
- package/clients/complexity-client.js +0 -675
- package/clients/complexity-client.test.js +0 -234
- package/clients/complexity-client.test.ts +0 -255
- package/clients/config-validator.js +0 -465
- package/clients/dependency-checker.js +0 -325
- package/clients/dependency-checker.test.js +0 -60
- package/clients/dependency-checker.test.ts +0 -71
- package/clients/dispatch/__tests__/autofix-integration.test.js +0 -245
- package/clients/dispatch/__tests__/autofix-integration.test.ts +0 -300
- package/clients/dispatch/__tests__/runner-registration.test.js +0 -234
- package/clients/dispatch/__tests__/runner-registration.test.ts +0 -286
- package/clients/dispatch/debug.log +0 -1
- package/clients/dispatch/dispatcher.edge.test.js +0 -82
- package/clients/dispatch/dispatcher.edge.test.ts +0 -100
- package/clients/dispatch/dispatcher.format.test.js +0 -46
- package/clients/dispatch/dispatcher.format.test.ts +0 -58
- package/clients/dispatch/dispatcher.inline.test.js +0 -74
- package/clients/dispatch/dispatcher.inline.test.ts +0 -93
- package/clients/dispatch/dispatcher.js +0 -381
- package/clients/dispatch/dispatcher.test.js +0 -116
- package/clients/dispatch/dispatcher.test.ts +0 -149
- package/clients/dispatch/integration.js +0 -108
- package/clients/dispatch/plan.js +0 -183
- package/clients/dispatch/runners/architect.js +0 -83
- package/clients/dispatch/runners/architect.test.js +0 -138
- package/clients/dispatch/runners/architect.test.ts +0 -162
- package/clients/dispatch/runners/ast-grep-napi.js +0 -405
- package/clients/dispatch/runners/ast-grep-napi.test.js +0 -107
- package/clients/dispatch/runners/ast-grep-napi.test.ts +0 -129
- package/clients/dispatch/runners/ast-grep.js +0 -157
- package/clients/dispatch/runners/biome.js +0 -55
- package/clients/dispatch/runners/config-validation.js +0 -67
- package/clients/dispatch/runners/go-vet.js +0 -48
- package/clients/dispatch/runners/index.js +0 -47
- package/clients/dispatch/runners/lsp.js +0 -102
- package/clients/dispatch/runners/oxlint.js +0 -67
- package/clients/dispatch/runners/oxlint.test.js +0 -230
- package/clients/dispatch/runners/oxlint.test.ts +0 -303
- package/clients/dispatch/runners/pyright.js +0 -100
- package/clients/dispatch/runners/pyright.test.js +0 -98
- package/clients/dispatch/runners/pyright.test.ts +0 -121
- package/clients/dispatch/runners/python-slop.js +0 -97
- package/clients/dispatch/runners/python-slop.test.js +0 -203
- package/clients/dispatch/runners/python-slop.test.ts +0 -298
- package/clients/dispatch/runners/ruff.js +0 -48
- package/clients/dispatch/runners/rust-clippy.js +0 -102
- package/clients/dispatch/runners/scan_codebase.test.js +0 -89
- package/clients/dispatch/runners/scan_codebase.test.ts +0 -105
- package/clients/dispatch/runners/shellcheck.js +0 -147
- package/clients/dispatch/runners/shellcheck.test.js +0 -98
- package/clients/dispatch/runners/shellcheck.test.ts +0 -129
- package/clients/dispatch/runners/similarity.js +0 -230
- package/clients/dispatch/runners/spellcheck.js +0 -106
- package/clients/dispatch/runners/spellcheck.test.js +0 -158
- package/clients/dispatch/runners/spellcheck.test.ts +0 -214
- package/clients/dispatch/runners/tree-sitter.js +0 -246
- package/clients/dispatch/runners/ts-lsp.js +0 -125
- package/clients/dispatch/runners/ts-slop.js +0 -113
- package/clients/dispatch/runners/type-safety.js +0 -142
- package/clients/dispatch/runners/utils/diagnostic-parsers.js +0 -134
- package/clients/dispatch/runners/utils/runner-helpers.js +0 -115
- package/clients/dispatch/runners/utils.js +0 -51
- package/clients/dispatch/runners/yaml-rule-parser.js +0 -360
- package/clients/dispatch/types.js +0 -16
- package/clients/dispatch/utils/format-utils.js +0 -44
- package/clients/dogfood.test.js +0 -201
- package/clients/dogfood.test.ts +0 -269
- package/clients/file-kinds.js +0 -177
- package/clients/file-kinds.test.js +0 -169
- package/clients/file-kinds.test.ts +0 -210
- package/clients/file-time.js +0 -152
- package/clients/file-utils.js +0 -40
- package/clients/fix-scanners.js +0 -204
- package/clients/format-service.js +0 -184
- package/clients/formatters.js +0 -488
- package/clients/go-client.js +0 -203
- package/clients/go-client.test.js +0 -127
- package/clients/go-client.test.ts +0 -143
- package/clients/installer/index.js +0 -403
- package/clients/interviewer-templates.js +0 -75
- package/clients/interviewer.js +0 -173
- package/clients/jscpd-client.js +0 -196
- package/clients/jscpd-client.test.js +0 -127
- package/clients/jscpd-client.test.ts +0 -145
- package/clients/knip-client.js +0 -239
- package/clients/knip-client.test.js +0 -112
- package/clients/knip-client.test.ts +0 -128
- package/clients/latency-logger.js +0 -40
- package/clients/lsp/__tests__/client.test.js +0 -310
- package/clients/lsp/__tests__/client.test.ts +0 -412
- package/clients/lsp/__tests__/config.test.js +0 -167
- package/clients/lsp/__tests__/config.test.ts +0 -217
- package/clients/lsp/__tests__/error-recovery.test.js +0 -213
- package/clients/lsp/__tests__/error-recovery.test.ts +0 -279
- package/clients/lsp/__tests__/integration.test.js +0 -127
- package/clients/lsp/__tests__/integration.test.ts +0 -160
- package/clients/lsp/__tests__/launch.test.js +0 -313
- package/clients/lsp/__tests__/launch.test.ts +0 -394
- package/clients/lsp/__tests__/server.test.js +0 -259
- package/clients/lsp/__tests__/server.test.ts +0 -332
- package/clients/lsp/__tests__/service.test.js +0 -438
- package/clients/lsp/__tests__/service.test.ts +0 -530
- package/clients/lsp/client.js +0 -350
- package/clients/lsp/config.js +0 -112
- package/clients/lsp/index.js +0 -318
- package/clients/lsp/installer/index.js +0 -391
- package/clients/lsp/interactive-install.js +0 -221
- package/clients/lsp/language.js +0 -170
- package/clients/lsp/launch.js +0 -329
- package/clients/lsp/lsp/launch.js +0 -116
- package/clients/lsp/lsp/server.js +0 -532
- package/clients/lsp/lsp-index.js +0 -10
- package/clients/lsp/path-utils.js +0 -5
- package/clients/lsp/server.js +0 -725
- package/clients/lsp/test-py-spawn/requirements.txt +0 -1
- package/clients/lsp/test-py-spawn/test.py +0 -3
- package/clients/lsp/test-py-svc/requirements.txt +0 -1
- package/clients/lsp/test-py-svc/test.py +0 -3
- package/clients/lsp/test-python-project/requirements.txt +0 -1
- package/clients/lsp/test-python-project/test.py +0 -5
- package/clients/metrics-client.js +0 -107
- package/clients/metrics-client.test.js +0 -128
- package/clients/metrics-client.test.ts +0 -163
- package/clients/metrics-history.js +0 -367
- package/clients/path-utils.js +0 -142
- package/clients/pipeline.js +0 -272
- package/clients/production-readiness.js +0 -522
- package/clients/project-index.js +0 -255
- package/clients/project-metadata.js +0 -531
- package/clients/ruff-client.js +0 -325
- package/clients/ruff-client.test.js +0 -132
- package/clients/ruff-client.test.ts +0 -153
- package/clients/rules-scanner.js +0 -97
- package/clients/runner-tracker.js +0 -152
- package/clients/rust-client.js +0 -205
- package/clients/rust-client.test.js +0 -108
- package/clients/rust-client.test.ts +0 -130
- package/clients/safe-spawn-async.js +0 -163
- package/clients/safe-spawn.js +0 -241
- package/clients/sanitize.js +0 -291
- package/clients/sanitize.test.js +0 -177
- package/clients/sanitize.test.ts +0 -223
- package/clients/scan-architectural-debt.js +0 -167
- package/clients/scan-utils.js +0 -83
- package/clients/secrets-scanner.js +0 -119
- package/clients/secrets-scanner.test.js +0 -100
- package/clients/secrets-scanner.test.ts +0 -113
- package/clients/sg-runner.js +0 -292
- package/clients/state-matrix.js +0 -160
- package/clients/subprocess-client.js +0 -65
- package/clients/symbol-types.js +0 -5
- package/clients/test-runner-client.js +0 -523
- package/clients/test-runner-client.test.js +0 -192
- package/clients/test-runner-client.test.ts +0 -253
- package/clients/test-utils.js +0 -27
- package/clients/test-utils.ts +0 -36
- package/clients/todo-scanner.js +0 -200
- package/clients/todo-scanner.test.js +0 -301
- package/clients/todo-scanner.test.ts +0 -352
- package/clients/tool-availability.js +0 -207
- package/clients/tree-sitter-client.js +0 -601
- package/clients/tree-sitter-query-loader.js +0 -355
- package/clients/tree-sitter-symbol-extractor.js +0 -289
- package/clients/ts-service.js +0 -129
- package/clients/type-coverage-client.js +0 -127
- package/clients/type-coverage-client.test.js +0 -105
- package/clients/type-coverage-client.test.ts +0 -125
- package/clients/type-safety-client.js +0 -138
- package/clients/types.js +0 -11
- package/clients/typescript-client.codefix.test.js +0 -157
- package/clients/typescript-client.codefix.test.ts +0 -186
- package/clients/typescript-client.js +0 -509
- package/clients/typescript-client.test.js +0 -105
- package/clients/typescript-client.test.ts +0 -126
- package/commands/booboo.js +0 -1007
- package/commands/fix-from-booboo.js +0 -398
- package/commands/fix-simplified.js +0 -618
- package/commands/rate.js +0 -281
- package/commands/rate.test.js +0 -119
- package/commands/rate.test.ts +0 -131
- package/commands/refactor.js +0 -130
|
@@ -1,197 +0,0 @@
|
|
|
1
|
-
import * as fs from "node:fs";
|
|
2
|
-
import * as os from "node:os";
|
|
3
|
-
import * as path from "node:path";
|
|
4
|
-
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
5
|
-
import { CacheManager, } from "./cache-manager.js";
|
|
6
|
-
describe("CacheManager", () => {
|
|
7
|
-
let manager;
|
|
8
|
-
let testDir;
|
|
9
|
-
beforeEach(() => {
|
|
10
|
-
manager = new CacheManager();
|
|
11
|
-
testDir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-lens-cache-test-"));
|
|
12
|
-
});
|
|
13
|
-
afterEach(() => {
|
|
14
|
-
fs.rmSync(testDir, { recursive: true, force: true });
|
|
15
|
-
});
|
|
16
|
-
describe("scanner cache", () => {
|
|
17
|
-
it("should return null for missing cache", () => {
|
|
18
|
-
const result = manager.readCache("knip", testDir);
|
|
19
|
-
expect(result).toBeNull();
|
|
20
|
-
});
|
|
21
|
-
it("should write and read cache", () => {
|
|
22
|
-
const data = { files: ["a.ts", "b.ts"], unused: ["x"] };
|
|
23
|
-
manager.writeCache("knip", data, testDir, { scanDurationMs: 1500 });
|
|
24
|
-
const result = manager.readCache("knip", testDir);
|
|
25
|
-
expect(result).not.toBeNull();
|
|
26
|
-
expect(result?.data).toEqual(data);
|
|
27
|
-
expect(result?.meta.scanDurationMs).toBe(1500);
|
|
28
|
-
expect(result?.meta.timestamp).toBeDefined();
|
|
29
|
-
});
|
|
30
|
-
it("should return null for stale cache", () => {
|
|
31
|
-
const data = { files: [] };
|
|
32
|
-
manager.writeCache("jscpd", data, testDir);
|
|
33
|
-
// Manually set old timestamp
|
|
34
|
-
const metaPath = path.join(testDir, ".pi-lens", "cache", "jscpd.meta.json");
|
|
35
|
-
const meta = JSON.parse(fs.readFileSync(metaPath, "utf-8"));
|
|
36
|
-
meta.timestamp = new Date(Date.now() - 60 * 60 * 1000).toISOString(); // 1 hour ago
|
|
37
|
-
fs.writeFileSync(metaPath, JSON.stringify(meta));
|
|
38
|
-
const result = manager.readCache("jscpd", testDir, 30 * 60 * 1000);
|
|
39
|
-
expect(result).toBeNull();
|
|
40
|
-
});
|
|
41
|
-
it("should respect custom maxAge", () => {
|
|
42
|
-
const data = { files: [] };
|
|
43
|
-
manager.writeCache("madge", data, testDir);
|
|
44
|
-
// Cache is 45 min old
|
|
45
|
-
const metaPath = path.join(testDir, ".pi-lens", "cache", "madge.meta.json");
|
|
46
|
-
const meta = JSON.parse(fs.readFileSync(metaPath, "utf-8"));
|
|
47
|
-
meta.timestamp = new Date(Date.now() - 45 * 60 * 1000).toISOString();
|
|
48
|
-
fs.writeFileSync(metaPath, JSON.stringify(meta));
|
|
49
|
-
// Default 30 min → stale
|
|
50
|
-
expect(manager.readCache("madge", testDir)).toBeNull();
|
|
51
|
-
// Custom 60 min → fresh
|
|
52
|
-
const result = manager.readCache("madge", testDir, 60 * 60 * 1000);
|
|
53
|
-
expect(result).not.toBeNull();
|
|
54
|
-
});
|
|
55
|
-
it("should check cache freshness", () => {
|
|
56
|
-
expect(manager.isCacheFresh("knip", testDir)).toBe(false);
|
|
57
|
-
manager.writeCache("knip", {}, testDir);
|
|
58
|
-
expect(manager.isCacheFresh("knip", testDir)).toBe(true);
|
|
59
|
-
});
|
|
60
|
-
it("should clear cache", () => {
|
|
61
|
-
manager.writeCache("jscpd", { clones: [] }, testDir);
|
|
62
|
-
expect(manager.isCacheFresh("jscpd", testDir)).toBe(true);
|
|
63
|
-
manager.clearCache("jscpd", testDir);
|
|
64
|
-
expect(manager.isCacheFresh("jscpd", testDir)).toBe(false);
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
// Helper to get test file path (absolute)
|
|
68
|
-
const testFile = (name) => path.join(testDir, name);
|
|
69
|
-
describe("turn state", () => {
|
|
70
|
-
it("should return default state when no file exists", () => {
|
|
71
|
-
const state = manager.readTurnState(testDir);
|
|
72
|
-
expect(state.files).toEqual({});
|
|
73
|
-
expect(state.turnCycles).toBe(0);
|
|
74
|
-
expect(state.maxCycles).toBe(3);
|
|
75
|
-
});
|
|
76
|
-
it("should write and read turn state", () => {
|
|
77
|
-
const state = {
|
|
78
|
-
files: {
|
|
79
|
-
"src/a.ts": {
|
|
80
|
-
modifiedRanges: [{ start: 1, end: 10 }],
|
|
81
|
-
importsChanged: true,
|
|
82
|
-
lastEdit: new Date().toISOString(),
|
|
83
|
-
},
|
|
84
|
-
},
|
|
85
|
-
turnCycles: 1,
|
|
86
|
-
maxCycles: 3,
|
|
87
|
-
lastUpdated: "",
|
|
88
|
-
};
|
|
89
|
-
manager.writeTurnState(state, testDir);
|
|
90
|
-
const read = manager.readTurnState(testDir);
|
|
91
|
-
expect(read.turnCycles).toBe(1);
|
|
92
|
-
expect(read.files["src/a.ts"].modifiedRanges).toHaveLength(1);
|
|
93
|
-
});
|
|
94
|
-
it("should add modified ranges and merge overlapping", () => {
|
|
95
|
-
manager.addModifiedRange(testFile("src/a.ts"), { start: 1, end: 10 }, false, testDir);
|
|
96
|
-
manager.addModifiedRange(testFile("src/a.ts"), { start: 8, end: 20 }, true, testDir);
|
|
97
|
-
const state = manager.readTurnState(testDir);
|
|
98
|
-
const key = "src/a.ts";
|
|
99
|
-
const ranges = state.files[key]?.modifiedRanges;
|
|
100
|
-
expect(ranges).toHaveLength(1); // Merged into one
|
|
101
|
-
expect(ranges?.[0]).toEqual({ start: 1, end: 20 });
|
|
102
|
-
expect(state.files[key].importsChanged).toBe(true);
|
|
103
|
-
});
|
|
104
|
-
it("should track imports_changed flag", () => {
|
|
105
|
-
// First edit without import change
|
|
106
|
-
manager.addModifiedRange(testFile("src/a.ts"), { start: 1, end: 5 }, false, testDir);
|
|
107
|
-
// Second edit with import change
|
|
108
|
-
manager.addModifiedRange(testFile("src/a.ts"), { start: 10, end: 15 }, true, testDir);
|
|
109
|
-
const state = manager.readTurnState(testDir);
|
|
110
|
-
expect(state.files["src/a.ts"].importsChanged).toBe(true);
|
|
111
|
-
});
|
|
112
|
-
it("should increment turn cycle", () => {
|
|
113
|
-
manager.incrementTurnCycle(testDir);
|
|
114
|
-
manager.incrementTurnCycle(testDir);
|
|
115
|
-
const state = manager.readTurnState(testDir);
|
|
116
|
-
expect(state.turnCycles).toBe(2);
|
|
117
|
-
});
|
|
118
|
-
it("should detect max cycles exceeded", () => {
|
|
119
|
-
expect(manager.isMaxCyclesExceeded(testDir)).toBe(false);
|
|
120
|
-
manager.incrementTurnCycle(testDir);
|
|
121
|
-
manager.incrementTurnCycle(testDir);
|
|
122
|
-
manager.incrementTurnCycle(testDir);
|
|
123
|
-
expect(manager.isMaxCyclesExceeded(testDir)).toBe(true);
|
|
124
|
-
});
|
|
125
|
-
it("should clear turn state", () => {
|
|
126
|
-
manager.addModifiedRange(testFile("src/a.ts"), { start: 1, end: 10 }, true, testDir);
|
|
127
|
-
manager.incrementTurnCycle(testDir);
|
|
128
|
-
manager.clearTurnState(testDir);
|
|
129
|
-
const state = manager.readTurnState(testDir);
|
|
130
|
-
expect(Object.keys(state.files)).toHaveLength(0);
|
|
131
|
-
expect(state.turnCycles).toBe(0);
|
|
132
|
-
});
|
|
133
|
-
});
|
|
134
|
-
describe("file queries", () => {
|
|
135
|
-
beforeEach(() => {
|
|
136
|
-
// Clear any previous state from other tests
|
|
137
|
-
manager.clearTurnState(testDir);
|
|
138
|
-
// Now add our test files
|
|
139
|
-
manager.addModifiedRange(testFile("a.ts"), { start: 1, end: 10 }, false, testDir);
|
|
140
|
-
manager.addModifiedRange(testFile("b.ts"), { start: 5, end: 20 }, true, testDir);
|
|
141
|
-
manager.addModifiedRange(testFile("c.ts"), { start: 1, end: 5 }, true, testDir);
|
|
142
|
-
});
|
|
143
|
-
it("should get all files for jscpd", () => {
|
|
144
|
-
const files = manager.getFilesForJscpd(testDir);
|
|
145
|
-
expect(files).toHaveLength(3);
|
|
146
|
-
});
|
|
147
|
-
it("should get only files with import changes for madge", () => {
|
|
148
|
-
// Verify state was recorded correctly
|
|
149
|
-
const state = manager.readTurnState(testDir);
|
|
150
|
-
const fileKeys = Object.keys(state.files);
|
|
151
|
-
// Only b.ts and c.ts have importsChanged: true
|
|
152
|
-
const madgeFiles = manager.getFilesForMadge(testDir);
|
|
153
|
-
const filesWithImportsTrue = fileKeys.filter((k) => state.files[k].importsChanged);
|
|
154
|
-
expect(madgeFiles).toHaveLength(filesWithImportsTrue.length);
|
|
155
|
-
});
|
|
156
|
-
});
|
|
157
|
-
describe("range utilities", () => {
|
|
158
|
-
it("should merge non-overlapping ranges", () => {
|
|
159
|
-
const ranges = [
|
|
160
|
-
{ start: 1, end: 5 },
|
|
161
|
-
{ start: 10, end: 15 },
|
|
162
|
-
{ start: 20, end: 25 },
|
|
163
|
-
];
|
|
164
|
-
expect(manager.mergeRanges(ranges)).toHaveLength(3);
|
|
165
|
-
});
|
|
166
|
-
it("should merge overlapping ranges", () => {
|
|
167
|
-
const ranges = [
|
|
168
|
-
{ start: 1, end: 10 },
|
|
169
|
-
{ start: 5, end: 15 },
|
|
170
|
-
];
|
|
171
|
-
const merged = manager.mergeRanges(ranges);
|
|
172
|
-
expect(merged).toHaveLength(1);
|
|
173
|
-
expect(merged[0]).toEqual({ start: 1, end: 15 });
|
|
174
|
-
});
|
|
175
|
-
it("should merge adjacent ranges", () => {
|
|
176
|
-
const ranges = [
|
|
177
|
-
{ start: 1, end: 10 },
|
|
178
|
-
{ start: 11, end: 20 },
|
|
179
|
-
];
|
|
180
|
-
const merged = manager.mergeRanges(ranges);
|
|
181
|
-
expect(merged).toHaveLength(1);
|
|
182
|
-
expect(merged[0]).toEqual({ start: 1, end: 20 });
|
|
183
|
-
});
|
|
184
|
-
it("should detect line in modified range", () => {
|
|
185
|
-
const ranges = [
|
|
186
|
-
{ start: 10, end: 20 },
|
|
187
|
-
{ start: 30, end: 40 },
|
|
188
|
-
];
|
|
189
|
-
expect(manager.isLineInModifiedRange(5, ranges)).toBe(false);
|
|
190
|
-
expect(manager.isLineInModifiedRange(10, ranges)).toBe(true);
|
|
191
|
-
expect(manager.isLineInModifiedRange(15, ranges)).toBe(true);
|
|
192
|
-
expect(manager.isLineInModifiedRange(20, ranges)).toBe(true);
|
|
193
|
-
expect(manager.isLineInModifiedRange(25, ranges)).toBe(false);
|
|
194
|
-
expect(manager.isLineInModifiedRange(35, ranges)).toBe(true);
|
|
195
|
-
});
|
|
196
|
-
});
|
|
197
|
-
});
|
|
@@ -1,299 +0,0 @@
|
|
|
1
|
-
import * as fs from "node:fs";
|
|
2
|
-
import * as os from "node:os";
|
|
3
|
-
import * as path from "node:path";
|
|
4
|
-
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
5
|
-
import {
|
|
6
|
-
CacheManager,
|
|
7
|
-
type ModifiedRange,
|
|
8
|
-
type TurnState,
|
|
9
|
-
} from "./cache-manager.js";
|
|
10
|
-
|
|
11
|
-
describe("CacheManager", () => {
|
|
12
|
-
let manager: CacheManager;
|
|
13
|
-
let testDir: string;
|
|
14
|
-
|
|
15
|
-
beforeEach(() => {
|
|
16
|
-
manager = new CacheManager();
|
|
17
|
-
testDir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-lens-cache-test-"));
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
afterEach(() => {
|
|
21
|
-
fs.rmSync(testDir, { recursive: true, force: true });
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
describe("scanner cache", () => {
|
|
25
|
-
it("should return null for missing cache", () => {
|
|
26
|
-
const result = manager.readCache("knip", testDir);
|
|
27
|
-
expect(result).toBeNull();
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it("should write and read cache", () => {
|
|
31
|
-
const data = { files: ["a.ts", "b.ts"], unused: ["x"] };
|
|
32
|
-
manager.writeCache("knip", data, testDir, { scanDurationMs: 1500 });
|
|
33
|
-
|
|
34
|
-
const result = manager.readCache<typeof data>("knip", testDir);
|
|
35
|
-
expect(result).not.toBeNull();
|
|
36
|
-
expect(result?.data).toEqual(data);
|
|
37
|
-
expect(result?.meta.scanDurationMs).toBe(1500);
|
|
38
|
-
expect(result?.meta.timestamp).toBeDefined();
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it("should return null for stale cache", () => {
|
|
42
|
-
const data = { files: [] };
|
|
43
|
-
manager.writeCache("jscpd", data, testDir);
|
|
44
|
-
|
|
45
|
-
// Manually set old timestamp
|
|
46
|
-
const metaPath = path.join(
|
|
47
|
-
testDir,
|
|
48
|
-
".pi-lens",
|
|
49
|
-
"cache",
|
|
50
|
-
"jscpd.meta.json",
|
|
51
|
-
);
|
|
52
|
-
const meta = JSON.parse(fs.readFileSync(metaPath, "utf-8"));
|
|
53
|
-
meta.timestamp = new Date(Date.now() - 60 * 60 * 1000).toISOString(); // 1 hour ago
|
|
54
|
-
fs.writeFileSync(metaPath, JSON.stringify(meta));
|
|
55
|
-
|
|
56
|
-
const result = manager.readCache("jscpd", testDir, 30 * 60 * 1000);
|
|
57
|
-
expect(result).toBeNull();
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it("should respect custom maxAge", () => {
|
|
61
|
-
const data = { files: [] };
|
|
62
|
-
manager.writeCache("madge", data, testDir);
|
|
63
|
-
|
|
64
|
-
// Cache is 45 min old
|
|
65
|
-
const metaPath = path.join(
|
|
66
|
-
testDir,
|
|
67
|
-
".pi-lens",
|
|
68
|
-
"cache",
|
|
69
|
-
"madge.meta.json",
|
|
70
|
-
);
|
|
71
|
-
const meta = JSON.parse(fs.readFileSync(metaPath, "utf-8"));
|
|
72
|
-
meta.timestamp = new Date(Date.now() - 45 * 60 * 1000).toISOString();
|
|
73
|
-
fs.writeFileSync(metaPath, JSON.stringify(meta));
|
|
74
|
-
|
|
75
|
-
// Default 30 min → stale
|
|
76
|
-
expect(manager.readCache("madge", testDir)).toBeNull();
|
|
77
|
-
|
|
78
|
-
// Custom 60 min → fresh
|
|
79
|
-
const result = manager.readCache("madge", testDir, 60 * 60 * 1000);
|
|
80
|
-
expect(result).not.toBeNull();
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it("should check cache freshness", () => {
|
|
84
|
-
expect(manager.isCacheFresh("knip", testDir)).toBe(false);
|
|
85
|
-
|
|
86
|
-
manager.writeCache("knip", {}, testDir);
|
|
87
|
-
expect(manager.isCacheFresh("knip", testDir)).toBe(true);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it("should clear cache", () => {
|
|
91
|
-
manager.writeCache("jscpd", { clones: [] }, testDir);
|
|
92
|
-
expect(manager.isCacheFresh("jscpd", testDir)).toBe(true);
|
|
93
|
-
|
|
94
|
-
manager.clearCache("jscpd", testDir);
|
|
95
|
-
expect(manager.isCacheFresh("jscpd", testDir)).toBe(false);
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
// Helper to get test file path (absolute)
|
|
100
|
-
const testFile = (name: string) => path.join(testDir, name);
|
|
101
|
-
|
|
102
|
-
describe("turn state", () => {
|
|
103
|
-
it("should return default state when no file exists", () => {
|
|
104
|
-
const state = manager.readTurnState(testDir);
|
|
105
|
-
expect(state.files).toEqual({});
|
|
106
|
-
expect(state.turnCycles).toBe(0);
|
|
107
|
-
expect(state.maxCycles).toBe(3);
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
it("should write and read turn state", () => {
|
|
111
|
-
const state: TurnState = {
|
|
112
|
-
files: {
|
|
113
|
-
"src/a.ts": {
|
|
114
|
-
modifiedRanges: [{ start: 1, end: 10 }],
|
|
115
|
-
importsChanged: true,
|
|
116
|
-
lastEdit: new Date().toISOString(),
|
|
117
|
-
},
|
|
118
|
-
},
|
|
119
|
-
turnCycles: 1,
|
|
120
|
-
maxCycles: 3,
|
|
121
|
-
lastUpdated: "",
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
manager.writeTurnState(state, testDir);
|
|
125
|
-
const read = manager.readTurnState(testDir);
|
|
126
|
-
|
|
127
|
-
expect(read.turnCycles).toBe(1);
|
|
128
|
-
expect(read.files["src/a.ts"].modifiedRanges).toHaveLength(1);
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
it("should add modified ranges and merge overlapping", () => {
|
|
132
|
-
manager.addModifiedRange(
|
|
133
|
-
testFile("src/a.ts"),
|
|
134
|
-
{ start: 1, end: 10 },
|
|
135
|
-
false,
|
|
136
|
-
testDir,
|
|
137
|
-
);
|
|
138
|
-
manager.addModifiedRange(
|
|
139
|
-
testFile("src/a.ts"),
|
|
140
|
-
{ start: 8, end: 20 },
|
|
141
|
-
true,
|
|
142
|
-
testDir,
|
|
143
|
-
);
|
|
144
|
-
|
|
145
|
-
const state = manager.readTurnState(testDir);
|
|
146
|
-
const key = "src/a.ts";
|
|
147
|
-
const ranges = state.files[key]?.modifiedRanges;
|
|
148
|
-
|
|
149
|
-
expect(ranges).toHaveLength(1); // Merged into one
|
|
150
|
-
expect(ranges?.[0]).toEqual({ start: 1, end: 20 });
|
|
151
|
-
expect(state.files[key].importsChanged).toBe(true);
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
it("should track imports_changed flag", () => {
|
|
155
|
-
// First edit without import change
|
|
156
|
-
manager.addModifiedRange(
|
|
157
|
-
testFile("src/a.ts"),
|
|
158
|
-
{ start: 1, end: 5 },
|
|
159
|
-
false,
|
|
160
|
-
testDir,
|
|
161
|
-
);
|
|
162
|
-
|
|
163
|
-
// Second edit with import change
|
|
164
|
-
manager.addModifiedRange(
|
|
165
|
-
testFile("src/a.ts"),
|
|
166
|
-
{ start: 10, end: 15 },
|
|
167
|
-
true,
|
|
168
|
-
testDir,
|
|
169
|
-
);
|
|
170
|
-
|
|
171
|
-
const state = manager.readTurnState(testDir);
|
|
172
|
-
expect(state.files["src/a.ts"].importsChanged).toBe(true);
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
it("should increment turn cycle", () => {
|
|
176
|
-
manager.incrementTurnCycle(testDir);
|
|
177
|
-
manager.incrementTurnCycle(testDir);
|
|
178
|
-
|
|
179
|
-
const state = manager.readTurnState(testDir);
|
|
180
|
-
expect(state.turnCycles).toBe(2);
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
it("should detect max cycles exceeded", () => {
|
|
184
|
-
expect(manager.isMaxCyclesExceeded(testDir)).toBe(false);
|
|
185
|
-
|
|
186
|
-
manager.incrementTurnCycle(testDir);
|
|
187
|
-
manager.incrementTurnCycle(testDir);
|
|
188
|
-
manager.incrementTurnCycle(testDir);
|
|
189
|
-
|
|
190
|
-
expect(manager.isMaxCyclesExceeded(testDir)).toBe(true);
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
it("should clear turn state", () => {
|
|
194
|
-
manager.addModifiedRange(
|
|
195
|
-
testFile("src/a.ts"),
|
|
196
|
-
{ start: 1, end: 10 },
|
|
197
|
-
true,
|
|
198
|
-
testDir,
|
|
199
|
-
);
|
|
200
|
-
manager.incrementTurnCycle(testDir);
|
|
201
|
-
|
|
202
|
-
manager.clearTurnState(testDir);
|
|
203
|
-
|
|
204
|
-
const state = manager.readTurnState(testDir);
|
|
205
|
-
expect(Object.keys(state.files)).toHaveLength(0);
|
|
206
|
-
expect(state.turnCycles).toBe(0);
|
|
207
|
-
});
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
describe("file queries", () => {
|
|
211
|
-
beforeEach(() => {
|
|
212
|
-
// Clear any previous state from other tests
|
|
213
|
-
manager.clearTurnState(testDir);
|
|
214
|
-
// Now add our test files
|
|
215
|
-
manager.addModifiedRange(
|
|
216
|
-
testFile("a.ts"),
|
|
217
|
-
{ start: 1, end: 10 },
|
|
218
|
-
false,
|
|
219
|
-
testDir,
|
|
220
|
-
);
|
|
221
|
-
manager.addModifiedRange(
|
|
222
|
-
testFile("b.ts"),
|
|
223
|
-
{ start: 5, end: 20 },
|
|
224
|
-
true,
|
|
225
|
-
testDir,
|
|
226
|
-
);
|
|
227
|
-
manager.addModifiedRange(
|
|
228
|
-
testFile("c.ts"),
|
|
229
|
-
{ start: 1, end: 5 },
|
|
230
|
-
true,
|
|
231
|
-
testDir,
|
|
232
|
-
);
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
it("should get all files for jscpd", () => {
|
|
236
|
-
const files = manager.getFilesForJscpd(testDir);
|
|
237
|
-
expect(files).toHaveLength(3);
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
it("should get only files with import changes for madge", () => {
|
|
241
|
-
// Verify state was recorded correctly
|
|
242
|
-
const state = manager.readTurnState(testDir);
|
|
243
|
-
const fileKeys = Object.keys(state.files);
|
|
244
|
-
|
|
245
|
-
// Only b.ts and c.ts have importsChanged: true
|
|
246
|
-
const madgeFiles = manager.getFilesForMadge(testDir);
|
|
247
|
-
const filesWithImportsTrue = fileKeys.filter(
|
|
248
|
-
(k) => state.files[k].importsChanged,
|
|
249
|
-
);
|
|
250
|
-
|
|
251
|
-
expect(madgeFiles).toHaveLength(filesWithImportsTrue.length);
|
|
252
|
-
});
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
describe("range utilities", () => {
|
|
256
|
-
it("should merge non-overlapping ranges", () => {
|
|
257
|
-
const ranges: ModifiedRange[] = [
|
|
258
|
-
{ start: 1, end: 5 },
|
|
259
|
-
{ start: 10, end: 15 },
|
|
260
|
-
{ start: 20, end: 25 },
|
|
261
|
-
];
|
|
262
|
-
expect(manager.mergeRanges(ranges)).toHaveLength(3);
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
it("should merge overlapping ranges", () => {
|
|
266
|
-
const ranges: ModifiedRange[] = [
|
|
267
|
-
{ start: 1, end: 10 },
|
|
268
|
-
{ start: 5, end: 15 },
|
|
269
|
-
];
|
|
270
|
-
const merged = manager.mergeRanges(ranges);
|
|
271
|
-
expect(merged).toHaveLength(1);
|
|
272
|
-
expect(merged[0]).toEqual({ start: 1, end: 15 });
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
it("should merge adjacent ranges", () => {
|
|
276
|
-
const ranges: ModifiedRange[] = [
|
|
277
|
-
{ start: 1, end: 10 },
|
|
278
|
-
{ start: 11, end: 20 },
|
|
279
|
-
];
|
|
280
|
-
const merged = manager.mergeRanges(ranges);
|
|
281
|
-
expect(merged).toHaveLength(1);
|
|
282
|
-
expect(merged[0]).toEqual({ start: 1, end: 20 });
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
it("should detect line in modified range", () => {
|
|
286
|
-
const ranges: ModifiedRange[] = [
|
|
287
|
-
{ start: 10, end: 20 },
|
|
288
|
-
{ start: 30, end: 40 },
|
|
289
|
-
];
|
|
290
|
-
|
|
291
|
-
expect(manager.isLineInModifiedRange(5, ranges)).toBe(false);
|
|
292
|
-
expect(manager.isLineInModifiedRange(10, ranges)).toBe(true);
|
|
293
|
-
expect(manager.isLineInModifiedRange(15, ranges)).toBe(true);
|
|
294
|
-
expect(manager.isLineInModifiedRange(20, ranges)).toBe(true);
|
|
295
|
-
expect(manager.isLineInModifiedRange(25, ranges)).toBe(false);
|
|
296
|
-
expect(manager.isLineInModifiedRange(35, ranges)).toBe(true);
|
|
297
|
-
});
|
|
298
|
-
});
|
|
299
|
-
});
|