@mmnto/totem 1.18.2 → 1.19.0
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/dist/compile-lesson.d.ts +42 -0
- package/dist/compile-lesson.d.ts.map +1 -1
- package/dist/compile-lesson.js +189 -0
- package/dist/compile-lesson.js.map +1 -1
- package/dist/compile-lesson.test.js +539 -0
- package/dist/compile-lesson.test.js.map +1 -1
- package/dist/compiler-schema.d.ts +110 -30
- package/dist/compiler-schema.d.ts.map +1 -1
- package/dist/compiler-schema.js +48 -2
- package/dist/compiler-schema.js.map +1 -1
- package/dist/compiler-schema.test.js +80 -0
- package/dist/compiler-schema.test.js.map +1 -1
- package/dist/compiler.d.ts +13 -6
- package/dist/compiler.d.ts.map +1 -1
- package/dist/compiler.js +14 -7
- package/dist/compiler.js.map +1 -1
- package/dist/compiler.test.js +33 -0
- package/dist/compiler.test.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/recurrence-stats.d.ts +10 -10
- package/dist/retrospect.d.ts +42 -42
- package/dist/stage4-verifier.d.ts +133 -0
- package/dist/stage4-verifier.d.ts.map +1 -0
- package/dist/stage4-verifier.js +355 -0
- package/dist/stage4-verifier.js.map +1 -0
- package/dist/stage4-verifier.test.d.ts +2 -0
- package/dist/stage4-verifier.test.d.ts.map +1 -0
- package/dist/stage4-verifier.test.js +372 -0
- package/dist/stage4-verifier.test.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,372 @@
|
|
|
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 { DEFAULT_BASELINE_GLOBS, getDefaultBaseline, verifyAgainstCodebase, } from './stage4-verifier.js';
|
|
6
|
+
import { cleanTmpDir } from './test-utils.js';
|
|
7
|
+
// ─── Helpers ────────────────────────────────────────
|
|
8
|
+
function makeRule(overrides = {}) {
|
|
9
|
+
return {
|
|
10
|
+
lessonHash: 'h1',
|
|
11
|
+
lessonHeading: 'No console log',
|
|
12
|
+
pattern: 'console\\.log',
|
|
13
|
+
message: 'no console.log',
|
|
14
|
+
engine: 'regex',
|
|
15
|
+
compiledAt: new Date().toISOString(),
|
|
16
|
+
badExample: "console.log('debug')",
|
|
17
|
+
goodExample: '// noop',
|
|
18
|
+
fileGlobs: ['packages/cli/src/**/*.ts'],
|
|
19
|
+
...overrides,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function makeDeps(files) {
|
|
23
|
+
return {
|
|
24
|
+
listFiles: async () => [...files.keys()],
|
|
25
|
+
readFile: async (file) => {
|
|
26
|
+
const content = files.get(file);
|
|
27
|
+
if (content === undefined) {
|
|
28
|
+
throw new Error(`stub readFile: ${file} not in fixture map`);
|
|
29
|
+
}
|
|
30
|
+
return content;
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
// ─── getDefaultBaseline ────────────────────────────
|
|
35
|
+
describe('getDefaultBaseline', () => {
|
|
36
|
+
it('returns the canonical test/fixture exclusion globs', () => {
|
|
37
|
+
const baseline = getDefaultBaseline();
|
|
38
|
+
expect(baseline.excludeFileGlobs).toEqual(DEFAULT_BASELINE_GLOBS);
|
|
39
|
+
expect(baseline.excludeFileGlobs).toContain('**/*.test.*');
|
|
40
|
+
expect(baseline.excludeFileGlobs).toContain('**/*.spec.*');
|
|
41
|
+
expect(baseline.excludeFileGlobs).toContain('**/__tests__/**');
|
|
42
|
+
expect(baseline.excludeFileGlobs).toContain('**/tests/**');
|
|
43
|
+
expect(baseline.excludeFileGlobs).toContain('**/__fixtures__/**');
|
|
44
|
+
expect(baseline.excludeFileGlobs).toContain('**/fixtures/**');
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
// ─── verifyAgainstCodebase: four-outcome contract ─
|
|
48
|
+
describe('verifyAgainstCodebase outcome: no-matches', () => {
|
|
49
|
+
it('returns no-matches when codebase walk is empty', async () => {
|
|
50
|
+
const deps = makeDeps(new Map());
|
|
51
|
+
const result = await verifyAgainstCodebase(makeRule(), getDefaultBaseline(), deps);
|
|
52
|
+
expect(result.outcome).toBe('no-matches');
|
|
53
|
+
expect(result.baselineMatches).toEqual([]);
|
|
54
|
+
expect(result.inScopeMatches).toEqual([]);
|
|
55
|
+
});
|
|
56
|
+
it('returns no-matches when no file in the codebase matches the pattern', async () => {
|
|
57
|
+
const files = new Map([
|
|
58
|
+
['packages/cli/src/foo.ts', "const x = 1;\nconst y = 'hello';\n"],
|
|
59
|
+
['packages/cli/src/bar.ts', 'function noop() { return null; }\n'],
|
|
60
|
+
]);
|
|
61
|
+
const result = await verifyAgainstCodebase(makeRule(), getDefaultBaseline(), makeDeps(files));
|
|
62
|
+
expect(result.outcome).toBe('no-matches');
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
describe('verifyAgainstCodebase outcome: out-of-scope', () => {
|
|
66
|
+
it('archives the rule when the pattern fires on a baseline test file', async () => {
|
|
67
|
+
const files = new Map([
|
|
68
|
+
['packages/cli/src/foo.ts', "console.log('debug');\n"],
|
|
69
|
+
// Pattern fires on a test file — over-broad evidence even though
|
|
70
|
+
// the production-side hit looks legitimate.
|
|
71
|
+
['packages/cli/src/foo.test.ts', "console.log('test stub');\n"],
|
|
72
|
+
]);
|
|
73
|
+
const result = await verifyAgainstCodebase(makeRule(), getDefaultBaseline(), makeDeps(files));
|
|
74
|
+
expect(result.outcome).toBe('out-of-scope');
|
|
75
|
+
expect(result.baselineMatches).toEqual(['packages/cli/src/foo.test.ts']);
|
|
76
|
+
});
|
|
77
|
+
it('archives the rule when the pattern fires outside fileGlobs scope', async () => {
|
|
78
|
+
const files = new Map([
|
|
79
|
+
['packages/cli/src/foo.ts', "console.log('debug');\n"],
|
|
80
|
+
// packages/core is OUTSIDE the rule's fileGlobs of packages/cli/src/**.
|
|
81
|
+
// Any match here is out-of-scope evidence regardless of whether the
|
|
82
|
+
// baseline globs apply.
|
|
83
|
+
['packages/core/src/bar.ts', "console.log('out of scope');\n"],
|
|
84
|
+
]);
|
|
85
|
+
const result = await verifyAgainstCodebase(makeRule(), getDefaultBaseline(), makeDeps(files));
|
|
86
|
+
expect(result.outcome).toBe('out-of-scope');
|
|
87
|
+
expect(result.baselineMatches).toEqual(['packages/core/src/bar.ts']);
|
|
88
|
+
});
|
|
89
|
+
it('archives the rule when fired only on /__tests__/ directory shape', async () => {
|
|
90
|
+
const files = new Map([
|
|
91
|
+
['packages/cli/src/__tests__/spec.ts', "console.log('test setup');\n"],
|
|
92
|
+
]);
|
|
93
|
+
const result = await verifyAgainstCodebase(makeRule(), getDefaultBaseline(), makeDeps(files));
|
|
94
|
+
expect(result.outcome).toBe('out-of-scope');
|
|
95
|
+
expect(result.baselineMatches).toEqual(['packages/cli/src/__tests__/spec.ts']);
|
|
96
|
+
});
|
|
97
|
+
it('out-of-scope wins precedence over in-scope outcomes', async () => {
|
|
98
|
+
// Pattern fires on both an in-scope file AND a baseline file. Even
|
|
99
|
+
// though the in-scope match is structurally equivalent to the
|
|
100
|
+
// badExample, the baseline hit forces an archive — ADR-091 §Stage 4
|
|
101
|
+
// is explicit on the ordering.
|
|
102
|
+
const files = new Map([
|
|
103
|
+
['packages/cli/src/foo.ts', "console.log('debug');\n"],
|
|
104
|
+
['packages/cli/src/foo.test.ts', "console.log('test');\n"],
|
|
105
|
+
]);
|
|
106
|
+
const result = await verifyAgainstCodebase(makeRule(), getDefaultBaseline(), makeDeps(files));
|
|
107
|
+
expect(result.outcome).toBe('out-of-scope');
|
|
108
|
+
expect(result.baselineMatches).toEqual(['packages/cli/src/foo.test.ts']);
|
|
109
|
+
expect(result.inScopeMatches).toEqual(['packages/cli/src/foo.ts']);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
describe('verifyAgainstCodebase outcome: in-scope-bad-example', () => {
|
|
113
|
+
it('promotes the rule when only in-scope matches and they all match badExample', async () => {
|
|
114
|
+
const files = new Map([
|
|
115
|
+
['packages/cli/src/foo.ts', "console.log('debug')\n"],
|
|
116
|
+
['packages/cli/src/bar.ts', " console.log('debug')\n"], // leading whitespace ok
|
|
117
|
+
]);
|
|
118
|
+
const result = await verifyAgainstCodebase(makeRule(), getDefaultBaseline(), makeDeps(files));
|
|
119
|
+
expect(result.outcome).toBe('in-scope-bad-example');
|
|
120
|
+
expect(result.baselineMatches).toEqual([]);
|
|
121
|
+
expect(result.inScopeMatches).toEqual(['packages/cli/src/bar.ts', 'packages/cli/src/foo.ts']);
|
|
122
|
+
expect(result.candidateDebtLines).toEqual([]);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
describe('verifyAgainstCodebase outcome: candidate-debt', () => {
|
|
126
|
+
it('flags candidate debt when in-scope matches differ from badExample shape', async () => {
|
|
127
|
+
// Pattern fires on multiple in-scope files but only ONE matches the
|
|
128
|
+
// exact badExample shape. The other one is a candidate-debt site —
|
|
129
|
+
// the rule may be legit, may be a false positive; human review needed.
|
|
130
|
+
const files = new Map([
|
|
131
|
+
['packages/cli/src/foo.ts', "console.log('debug')\n"],
|
|
132
|
+
['packages/cli/src/bar.ts', 'console.log(`${process.env.DEBUG}`)\n'],
|
|
133
|
+
]);
|
|
134
|
+
const result = await verifyAgainstCodebase(makeRule(), getDefaultBaseline(), makeDeps(files));
|
|
135
|
+
expect(result.outcome).toBe('candidate-debt');
|
|
136
|
+
expect(result.baselineMatches).toEqual([]);
|
|
137
|
+
expect(result.inScopeMatches).toEqual(['packages/cli/src/bar.ts', 'packages/cli/src/foo.ts']);
|
|
138
|
+
expect(result.candidateDebtLines).toEqual(['console.log(`${process.env.DEBUG}`)']);
|
|
139
|
+
});
|
|
140
|
+
it('returns candidate-debt when no badExample is present (all hits are debt)', async () => {
|
|
141
|
+
const ruleNoExample = makeRule({ badExample: undefined });
|
|
142
|
+
const files = new Map([['packages/cli/src/foo.ts', "console.log('debug')\n"]]);
|
|
143
|
+
const result = await verifyAgainstCodebase(ruleNoExample, getDefaultBaseline(), makeDeps(files));
|
|
144
|
+
expect(result.outcome).toBe('candidate-debt');
|
|
145
|
+
expect(result.candidateDebtLines).toEqual(["console.log('debug')"]);
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
// ─── #1526 regression class ────────────────────────
|
|
149
|
+
describe('verifyAgainstCodebase regression: #1526 class (over-broad new net.Socket pattern)', () => {
|
|
150
|
+
it('structurally rejects an over-broad pattern that fires on every new net.Socket()', async () => {
|
|
151
|
+
// The class of failure ADR-091 §"Stage 4" cites: a compile worker
|
|
152
|
+
// emitted `new net\.Socket\(\)` on a lesson about "secure socket
|
|
153
|
+
// initialization". The pattern fires on every legitimate net.Socket
|
|
154
|
+
// construction in the consumer codebase. Stage 4 catches it before
|
|
155
|
+
// the rule reaches active.
|
|
156
|
+
const overBroadRule = makeRule({
|
|
157
|
+
lessonHash: 'overbroad1',
|
|
158
|
+
lessonHeading: 'Secure socket initialization',
|
|
159
|
+
pattern: 'new net\\.Socket\\(\\)',
|
|
160
|
+
message: 'Use the secure socket factory',
|
|
161
|
+
badExample: 'const s = new net.Socket();',
|
|
162
|
+
fileGlobs: ['packages/cli/src/**/*.ts'],
|
|
163
|
+
});
|
|
164
|
+
const files = new Map([
|
|
165
|
+
// In-scope hit — looks legitimate, would alone yield in-scope-bad-example
|
|
166
|
+
// outcome.
|
|
167
|
+
['packages/cli/src/server.ts', 'const s = new net.Socket();\n'],
|
|
168
|
+
// OUT-OF-SCOPE hit — proves the pattern is over-broad. This is the
|
|
169
|
+
// signal Stage 4 surfaces that Layer 3 does not catch.
|
|
170
|
+
['packages/core/src/transport.ts', 'const s = new net.Socket();\n'],
|
|
171
|
+
]);
|
|
172
|
+
const result = await verifyAgainstCodebase(overBroadRule, getDefaultBaseline(), makeDeps(files));
|
|
173
|
+
expect(result.outcome).toBe('out-of-scope');
|
|
174
|
+
expect(result.baselineMatches).toEqual(['packages/core/src/transport.ts']);
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
// ─── Suppression interaction ───────────────────────
|
|
178
|
+
describe('verifyAgainstCodebase suppression handling', () => {
|
|
179
|
+
it('treats suppressed lines as no-fire (consumer opt-out is not over-broad evidence)', async () => {
|
|
180
|
+
// A line with `// totem-ignore` is an explicit consumer allow-list. Stage 4
|
|
181
|
+
// should not flag it as out-of-scope evidence — the consumer has said
|
|
182
|
+
// "yes, the rule fires here, and yes, that's intended."
|
|
183
|
+
const files = new Map([
|
|
184
|
+
['packages/cli/src/foo.test.ts', "// totem-ignore-next-line\nconsole.log('test stub')\n"],
|
|
185
|
+
]);
|
|
186
|
+
const result = await verifyAgainstCodebase(makeRule(), getDefaultBaseline(), makeDeps(files));
|
|
187
|
+
// Suppressed lines don't yield violations, so the codebase walk produces
|
|
188
|
+
// zero hits → no-matches outcome. The fact that this matches the
|
|
189
|
+
// baseline test-file glob is moot because the rule never fired there.
|
|
190
|
+
expect(result.outcome).toBe('no-matches');
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
// ─── fileGlobs + baseline interaction ──────────────
|
|
194
|
+
describe('verifyAgainstCodebase rule without fileGlobs', () => {
|
|
195
|
+
it('treats every non-baseline file as in-scope when rule has no fileGlobs', async () => {
|
|
196
|
+
const ruleNoScope = makeRule({ fileGlobs: undefined });
|
|
197
|
+
const files = new Map([['anywhere/in/the/repo.ts', "console.log('debug')\n"]]);
|
|
198
|
+
const result = await verifyAgainstCodebase(ruleNoScope, getDefaultBaseline(), makeDeps(files));
|
|
199
|
+
expect(result.outcome).toBe('in-scope-bad-example');
|
|
200
|
+
expect(result.inScopeMatches).toEqual(['anywhere/in/the/repo.ts']);
|
|
201
|
+
});
|
|
202
|
+
it('still applies the default baseline when rule has no fileGlobs', async () => {
|
|
203
|
+
const ruleNoScope = makeRule({ fileGlobs: undefined });
|
|
204
|
+
const files = new Map([
|
|
205
|
+
['packages/__tests__/foo.ts', "console.log('debug')\n"],
|
|
206
|
+
]);
|
|
207
|
+
const result = await verifyAgainstCodebase(ruleNoScope, getDefaultBaseline(), makeDeps(files));
|
|
208
|
+
expect(result.outcome).toBe('out-of-scope');
|
|
209
|
+
expect(result.baselineMatches).toEqual(['packages/__tests__/foo.ts']);
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
// ─── Failure modes ────────────────────────────────
|
|
213
|
+
describe('verifyAgainstCodebase failure modes', () => {
|
|
214
|
+
it('throws when readFile rejects (Tenet 4: fail loud)', async () => {
|
|
215
|
+
const deps = {
|
|
216
|
+
listFiles: async () => ['packages/cli/src/missing.ts'],
|
|
217
|
+
readFile: async () => {
|
|
218
|
+
throw new Error('ENOENT: no such file');
|
|
219
|
+
},
|
|
220
|
+
};
|
|
221
|
+
await expect(verifyAgainstCodebase(makeRule(), getDefaultBaseline(), deps)).rejects.toThrow(/Stage 4 verifier could not read packages\/cli\/src\/missing\.ts/);
|
|
222
|
+
});
|
|
223
|
+
it('throws when an ast-grep rule is verified without a workingDirectory (CR mmnto-ai/totem#1757 R1)', async () => {
|
|
224
|
+
// Earlier code returned `[]` from `runRuleAgainstAllFiles` when
|
|
225
|
+
// `workingDirectory` was absent on AST/ast-grep rules, which silently
|
|
226
|
+
// misclassified the run as `'no-matches'`. Fail loud instead so the
|
|
227
|
+
// missing-input case cannot masquerade as a clean codebase result.
|
|
228
|
+
const files = new Map([['packages/cli/src/foo.ts', 'console.log("x")']]);
|
|
229
|
+
const deps = {
|
|
230
|
+
listFiles: async () => [...files.keys()],
|
|
231
|
+
readFile: async (file) => files.get(file) ?? '',
|
|
232
|
+
// workingDirectory intentionally omitted.
|
|
233
|
+
};
|
|
234
|
+
const astRule = makeRule({ engine: 'ast-grep', pattern: 'console.log($X)' });
|
|
235
|
+
await expect(verifyAgainstCodebase(astRule, getDefaultBaseline(), deps)).rejects.toThrow(/Stage 4 verifier requires deps\.workingDirectory for ast-grep rules/);
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
// ─── Custom baseline ──────────────────────────────
|
|
239
|
+
describe('verifyAgainstCodebase custom baseline overrides', () => {
|
|
240
|
+
it('honors a custom baseline that excludes additional patterns', async () => {
|
|
241
|
+
const customBaseline = {
|
|
242
|
+
excludeFileGlobs: [...DEFAULT_BASELINE_GLOBS, '**/migrations/**'],
|
|
243
|
+
};
|
|
244
|
+
const files = new Map([
|
|
245
|
+
// Pattern fires on a migration file. Default baseline would treat
|
|
246
|
+
// this as in-scope (since `migrations/` isn't in DEFAULT_BASELINE_GLOBS).
|
|
247
|
+
// Custom baseline extension flips it to out-of-scope.
|
|
248
|
+
['packages/cli/src/migrations/001-init.ts', "console.log('migrating')\n"],
|
|
249
|
+
]);
|
|
250
|
+
const result = await verifyAgainstCodebase(makeRule(), customBaseline, makeDeps(files));
|
|
251
|
+
expect(result.outcome).toBe('out-of-scope');
|
|
252
|
+
expect(result.baselineMatches).toEqual(['packages/cli/src/migrations/001-init.ts']);
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
// ─── fileToAdditions trailing-blank handling ──────
|
|
256
|
+
describe("verifyAgainstCodebase fileToAdditions doesn't synthesize trailing blank line (CR mmnto-ai/totem#1757 R3)", () => {
|
|
257
|
+
it('does not match a `^$` blank-line pattern against a newline-terminated single-line file', async () => {
|
|
258
|
+
// Pre-fix: `split(/\r?\n/)` on `'foo\n'` produced `['foo', '']`,
|
|
259
|
+
// and the empty trailing element would fire any rule with
|
|
260
|
+
// pattern `^$`. The fix drops the trailing empty when content
|
|
261
|
+
// was newline-terminated, so the only addition is line 1: `foo`.
|
|
262
|
+
const blankLineRule = makeRule({
|
|
263
|
+
pattern: '^$',
|
|
264
|
+
message: 'no blank lines',
|
|
265
|
+
badExample: '',
|
|
266
|
+
});
|
|
267
|
+
const files = new Map([
|
|
268
|
+
// Newline-terminated single-line file. Pre-fix: would fire on
|
|
269
|
+
// synthetic blank line 2; post-fix: no fire.
|
|
270
|
+
['packages/cli/src/foo.ts', 'foo\n'],
|
|
271
|
+
]);
|
|
272
|
+
const result = await verifyAgainstCodebase(blankLineRule, getDefaultBaseline(), makeDeps(files));
|
|
273
|
+
expect(result.outcome).toBe('no-matches');
|
|
274
|
+
});
|
|
275
|
+
it('handles empty file content without producing a synthetic addition', async () => {
|
|
276
|
+
const blankLineRule = makeRule({ pattern: '^$', message: 'no blank lines', badExample: '' });
|
|
277
|
+
const files = new Map([['packages/cli/src/empty.ts', '']]);
|
|
278
|
+
const result = await verifyAgainstCodebase(blankLineRule, getDefaultBaseline(), makeDeps(files));
|
|
279
|
+
expect(result.outcome).toBe('no-matches');
|
|
280
|
+
});
|
|
281
|
+
it('still detects real blank lines inside the file body', async () => {
|
|
282
|
+
// Real blank line on line 2 must still fire — the fix only strips
|
|
283
|
+
// the synthetic terminator, not legitimate interior blank lines.
|
|
284
|
+
const blankLineRule = makeRule({
|
|
285
|
+
pattern: '^$',
|
|
286
|
+
message: 'no blank lines',
|
|
287
|
+
badExample: '',
|
|
288
|
+
fileGlobs: undefined,
|
|
289
|
+
});
|
|
290
|
+
const files = new Map([['packages/cli/src/foo.ts', 'first\n\nthird\n']]);
|
|
291
|
+
const result = await verifyAgainstCodebase(blankLineRule, getDefaultBaseline(), makeDeps(files));
|
|
292
|
+
// The interior blank line fires the pattern. Whether the outcome
|
|
293
|
+
// is 'in-scope-bad-example' or 'candidate-debt' depends on the
|
|
294
|
+
// bad-example match logic, but the file MUST appear in
|
|
295
|
+
// `inScopeMatches` either way — proof the rule fired and the fix
|
|
296
|
+
// didn't suppress real blank-line detection.
|
|
297
|
+
expect(['in-scope-bad-example', 'candidate-debt']).toContain(result.outcome);
|
|
298
|
+
expect(result.inScopeMatches).toEqual(['packages/cli/src/foo.ts']);
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
// ─── ast-grep engine path ──────────────────────────
|
|
302
|
+
describe('verifyAgainstCodebase ast-grep engine (CR mmnto-ai/totem#1757 R2)', () => {
|
|
303
|
+
let tmpDir;
|
|
304
|
+
beforeEach(() => {
|
|
305
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'totem-stage4-astgrep-'));
|
|
306
|
+
fs.mkdirSync(path.join(tmpDir, 'packages', 'cli', 'src'), { recursive: true });
|
|
307
|
+
});
|
|
308
|
+
afterEach(() => {
|
|
309
|
+
cleanTmpDir(tmpDir);
|
|
310
|
+
});
|
|
311
|
+
it('routes ast-grep rules through the AST matcher and reports in-scope-bad-example', async () => {
|
|
312
|
+
// The four-outcome contract above only exercises the regex engine.
|
|
313
|
+
// ast-grep rules go through `applyAstRulesToAdditions`, which reads
|
|
314
|
+
// file content from disk via `workingDirectory`, so the test stages
|
|
315
|
+
// a real tmpDir alongside the synthetic readFile callback used to
|
|
316
|
+
// build additions. Confirms the engine dispatch in
|
|
317
|
+
// `runRuleAgainstAllFiles` does not silently fall back to regex.
|
|
318
|
+
const filePath = 'packages/cli/src/foo.ts';
|
|
319
|
+
const fileContent = "console.log('debug')\n";
|
|
320
|
+
fs.writeFileSync(path.join(tmpDir, filePath), fileContent);
|
|
321
|
+
const astRule = {
|
|
322
|
+
lessonHash: 'h-ast',
|
|
323
|
+
lessonHeading: 'No console.log',
|
|
324
|
+
pattern: 'console.log($X)',
|
|
325
|
+
astGrepPattern: 'console.log($X)',
|
|
326
|
+
message: 'no console.log',
|
|
327
|
+
engine: 'ast-grep',
|
|
328
|
+
compiledAt: new Date().toISOString(),
|
|
329
|
+
badExample: "console.log('debug')",
|
|
330
|
+
goodExample: '// noop',
|
|
331
|
+
fileGlobs: ['packages/cli/src/**/*.ts'],
|
|
332
|
+
};
|
|
333
|
+
const deps = {
|
|
334
|
+
listFiles: async () => [filePath],
|
|
335
|
+
readFile: async (file) => {
|
|
336
|
+
if (file === filePath)
|
|
337
|
+
return fileContent;
|
|
338
|
+
throw new Error(`stub readFile: ${file} not in fixture map`);
|
|
339
|
+
},
|
|
340
|
+
workingDirectory: tmpDir,
|
|
341
|
+
};
|
|
342
|
+
const result = await verifyAgainstCodebase(astRule, getDefaultBaseline(), deps);
|
|
343
|
+
expect(result.outcome).toBe('in-scope-bad-example');
|
|
344
|
+
expect(result.inScopeMatches).toEqual([filePath]);
|
|
345
|
+
expect(result.baselineMatches).toEqual([]);
|
|
346
|
+
});
|
|
347
|
+
});
|
|
348
|
+
// ─── badExample structural equivalence ────────────
|
|
349
|
+
describe('verifyAgainstCodebase badExample matching', () => {
|
|
350
|
+
it('matches multi-line badExample any-line-equal semantics', async () => {
|
|
351
|
+
// Pipeline 3 reuses the Bad snippet block as badExample. Multi-line
|
|
352
|
+
// bodies should match if ANY component line equals the violation line.
|
|
353
|
+
const ruleMultiLine = makeRule({
|
|
354
|
+
badExample: "// helper\nconsole.log('debug')\nreturn null;",
|
|
355
|
+
});
|
|
356
|
+
const files = new Map([
|
|
357
|
+
// The middle line of the multi-line badExample matches the violation.
|
|
358
|
+
['packages/cli/src/foo.ts', "console.log('debug')\n"],
|
|
359
|
+
]);
|
|
360
|
+
const result = await verifyAgainstCodebase(ruleMultiLine, getDefaultBaseline(), makeDeps(files));
|
|
361
|
+
expect(result.outcome).toBe('in-scope-bad-example');
|
|
362
|
+
});
|
|
363
|
+
it('treats trimmed-equal lines as bad-example matches (whitespace-insensitive)', async () => {
|
|
364
|
+
const files = new Map([
|
|
365
|
+
// Tab-indented match — should still equal the badExample after trim.
|
|
366
|
+
['packages/cli/src/foo.ts', "\t\tconsole.log('debug')\n"],
|
|
367
|
+
]);
|
|
368
|
+
const result = await verifyAgainstCodebase(makeRule(), getDefaultBaseline(), makeDeps(files));
|
|
369
|
+
expect(result.outcome).toBe('in-scope-bad-example');
|
|
370
|
+
});
|
|
371
|
+
});
|
|
372
|
+
//# sourceMappingURL=stage4-verifier.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stage4-verifier.test.js","sourceRoot":"","sources":["../src/stage4-verifier.test.ts"],"names":[],"mappings":"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"}
|