@ttt-productions/repo-guards 0.1.28 → 0.1.29
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.
|
@@ -4,10 +4,15 @@ export interface TypedMockHelper {
|
|
|
4
4
|
owner: string;
|
|
5
5
|
names: readonly string[];
|
|
6
6
|
}
|
|
7
|
+
export interface FakeCall {
|
|
8
|
+
name: string;
|
|
9
|
+
overridesArgument: number;
|
|
10
|
+
}
|
|
7
11
|
export interface ForbiddenOverride {
|
|
8
12
|
module: string;
|
|
9
13
|
names: readonly string[];
|
|
10
14
|
why: string;
|
|
15
|
+
fakeCalls?: readonly FakeCall[];
|
|
11
16
|
}
|
|
12
17
|
export interface TypedMockFacts {
|
|
13
18
|
importAliases: Readonly<Record<string, string>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typed-mocks.d.ts","sourceRoot":"","sources":["../../../src/modules/repo/typed-mocks.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAwBzE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAWpD,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,GAAG,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"typed-mocks.d.ts","sourceRoot":"","sources":["../../../src/modules/repo/typed-mocks.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAwBzE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAWpD,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,SAAS,QAAQ,EAAE,CAAC;CACjC;AAED,MAAM,WAAW,cAAc;IAC7B,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAChD,KAAK,EAAE,SAAS,UAAU,EAAE,CAAC;IAC7B,YAAY,EAAE,SAAS,eAAe,EAAE,CAAC;IACzC,kBAAkB,EAAE,SAAS,iBAAiB,EAAE,CAAC;CAClD;AAuTD,eAAO,MAAM,cAAc,EAAE,sBAAsB,CAAC,cAAc,CAqCjE,CAAC"}
|
|
@@ -153,12 +153,32 @@ const isOriginalSpread = (expression, original) => {
|
|
|
153
153
|
callee.expression.text === 'vi' &&
|
|
154
154
|
callee.name.text === 'importActual'));
|
|
155
155
|
};
|
|
156
|
-
function
|
|
156
|
+
function calleeName(call) {
|
|
157
|
+
const callee = unwrap(call.expression);
|
|
158
|
+
if (ts.isIdentifier(callee))
|
|
159
|
+
return callee.text;
|
|
160
|
+
if (ts.isPropertyAccessExpression(callee))
|
|
161
|
+
return callee.name.text;
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
function overridesOf(returned, original, fakeCalls) {
|
|
157
165
|
const value = awaited(returned);
|
|
158
166
|
const objects = [];
|
|
159
167
|
const out = [];
|
|
168
|
+
const name = ts.isCallExpression(value) ? calleeName(value) : null;
|
|
169
|
+
const overridesAt = name === null ? undefined : fakeCalls.get(name);
|
|
160
170
|
if (ts.isObjectLiteralExpression(value))
|
|
161
171
|
objects.push(value);
|
|
172
|
+
else if (ts.isCallExpression(value) && overridesAt !== undefined) {
|
|
173
|
+
const argument = value.arguments[overridesAt];
|
|
174
|
+
if (argument) {
|
|
175
|
+
const arg = unwrap(argument);
|
|
176
|
+
if (ts.isObjectLiteralExpression(arg))
|
|
177
|
+
objects.push(arg);
|
|
178
|
+
else
|
|
179
|
+
out.push({ unread: arg });
|
|
180
|
+
}
|
|
181
|
+
}
|
|
162
182
|
else if (ts.isCallExpression(value))
|
|
163
183
|
for (const argument of value.arguments) {
|
|
164
184
|
const arg = unwrap(argument);
|
|
@@ -195,10 +215,11 @@ function checkForbidden(reader, facts, files) {
|
|
|
195
215
|
if (!fn)
|
|
196
216
|
continue;
|
|
197
217
|
const forbidden = new Set(entry.names);
|
|
218
|
+
const fakeCalls = new Map((entry.fakeCalls ?? []).map((call) => [call.name, call.overridesArgument]));
|
|
198
219
|
for (const returned of returnedExpressions(fn)) {
|
|
199
220
|
if (!returned)
|
|
200
221
|
continue;
|
|
201
|
-
for (const read of overridesOf(returned, parameterName(fn))) {
|
|
222
|
+
for (const read of overridesOf(returned, parameterName(fn), fakeCalls)) {
|
|
202
223
|
if ('unread' in read) {
|
|
203
224
|
findings.push({
|
|
204
225
|
file: file.path,
|
|
@@ -256,6 +277,21 @@ function factProblems(facts) {
|
|
|
256
277
|
seen.add(entry.module);
|
|
257
278
|
namesProblem(entry.names, `forbiddenOverrides entry ${entry.module}'s names`, problems);
|
|
258
279
|
textProblem(entry.why, `forbiddenOverrides entry ${entry.module}'s why`, problems);
|
|
280
|
+
if (entry.fakeCalls === undefined)
|
|
281
|
+
continue;
|
|
282
|
+
if (!Array.isArray(entry.fakeCalls)) {
|
|
283
|
+
problems.push(`forbiddenOverrides entry ${entry.module}'s fakeCalls is not a list`);
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
for (const call of entry.fakeCalls) {
|
|
287
|
+
const fake = (call && typeof call === 'object' ? call : {});
|
|
288
|
+
if (!isText(fake.name))
|
|
289
|
+
problems.push(`a fakeCalls entry of ${entry.module} names no fake`);
|
|
290
|
+
if (typeof fake.overridesArgument !== 'number' ||
|
|
291
|
+
!Number.isInteger(fake.overridesArgument) ||
|
|
292
|
+
fake.overridesArgument < 1)
|
|
293
|
+
problems.push(`fakeCalls entry ${String(fake.name)} of ${entry.module} needs overridesArgument, the position of its override object after importOriginal`);
|
|
294
|
+
}
|
|
259
295
|
}
|
|
260
296
|
}
|
|
261
297
|
return problems;
|
|
@@ -285,7 +321,9 @@ export const typedMockGuard = {
|
|
|
285
321
|
'a stand-in that behaves differently on the path a case proves lets the case pass on behavior the app ' +
|
|
286
322
|
'never has. The guard reads the names each override object sets at its top level; a spread of anything ' +
|
|
287
323
|
'but the real module, a computed name, or an override object it cannot see fails, since it could hide a ' +
|
|
288
|
-
'listed one. Each entry names its module, the exports, and why the real one runs.'
|
|
324
|
+
'listed one. Each entry names its module, the exports, and why the real one runs. An entry may name ' +
|
|
325
|
+
'its shared fake’s calls with the position of their override object: for a call to that fake only that ' +
|
|
326
|
+
'argument is read, and the arguments before it are the fake’s state.',
|
|
289
327
|
timeoutMs: REPOSITORY_GUARD_TIMEOUT_MS,
|
|
290
328
|
run(context) {
|
|
291
329
|
return checkForbidden(context.reader, facts, context.files(SCOPE));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typed-mocks.js","sourceRoot":"","sources":["../../../src/modules/repo/typed-mocks.ts"],"names":[],"mappings":"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","sourcesContent":["import ts from 'typescript';\nimport { join } from 'node:path';\nimport type { Finding } from '../../guard.js';\nimport type { RepoReader, Scope } from '../../node/reader.js';\nimport type { GenericGuardDefinition } from '../../node/suite/config.js';\nimport type { FileView } from '../../reader/view.js';\nimport { lineOfNode, unwrap } from '../../support/ast.js';\nimport {\n awaited,\n bindingsOf,\n calledExport,\n factoryFunction,\n isRunnerOptions,\n parameterName,\n returnedExpressions,\n type Binding,\n type Factory,\n} from '../../support/mock-factories.js';\nimport {\n importAliasesProblem,\n isText,\n namesProblem,\n objectProblem,\n pathProblem,\n textProblem,\n} from '../../support/facts.js';\nimport { aliasList, isLocalModule, resolveSpecifier } from '../../support/resolve-specifier.js';\nimport { REPOSITORY_GUARD_TIMEOUT_MS } from '../../support/timeouts.js';\nimport type { SharedFake } from './shared-fakes.js';\n\nconst MOCK_CALLS: ReadonlySet<string> = new Set(['vi.mock', 'vi.doMock']);\n\nconst SCOPE: Scope = {\n roots: ['app', 'functions', 'scripts', 'workers'],\n kinds: ['code'],\n tests: 'only',\n optionalRoots: ['functions', 'scripts', 'workers'],\n};\n\nexport interface TypedMockHelper {\n owner: string;\n names: readonly string[];\n}\n\nexport interface ForbiddenOverride {\n module: string;\n names: readonly string[];\n why: string;\n}\n\nexport interface TypedMockFacts {\n importAliases: Readonly<Record<string, string>>;\n fakes: readonly SharedFake[];\n typedHelpers: readonly TypedMockHelper[];\n forbiddenOverrides: readonly ForbiddenOverride[];\n}\n\ninterface Site {\n node: ts.CallExpression;\n module: string;\n byImport: boolean;\n factory: ts.Expression | undefined;\n}\n\nfunction sitesOf(reader: RepoReader, file: FileView, aliases: Array<[string, string]>): Site[] {\n const out: Site[] = [];\n for (const site of file.imports) {\n if (site.kind !== 'mock' || !MOCK_CALLS.has(site.callee ?? '')) continue;\n if (!ts.isCallExpression(site.node)) continue;\n out.push({\n node: site.node,\n module: resolveSpecifier(reader, file.path, site.spec, aliases),\n byImport: site.byImport,\n factory: site.node.arguments[1],\n });\n }\n return out;\n}\n\ntype OwnerKind = 'fake' | 'helper';\ntype Owners = ReadonlyMap<\n string,\n { kind: 'fake' } | { kind: 'helper'; names: ReadonlySet<string> }\n>;\n\nfunction ownerKind(owners: Owners, path: string, name: string): OwnerKind | null {\n const owner = owners.get(path);\n if (!owner) return null;\n if (owner.kind === 'fake') return 'fake';\n return owner.names.has(name) ? 'helper' : null;\n}\n\nfunction calleeKind(\n call: ts.CallExpression,\n owners: Owners,\n bindings: Map<string, Binding>,\n resolve: (spec: string) => string,\n): OwnerKind | null {\n const target = calledExport(call, bindings, resolve);\n return target ? ownerKind(owners, target.path, target.name) : null;\n}\n\nfunction untypedReason(\n expression: ts.Expression | null,\n fn: Factory,\n owners: Owners,\n bindings: Map<string, Binding>,\n resolve: (spec: string) => string,\n): string | null {\n const call = expression ? awaited(expression) : null;\n if (!call || !ts.isCallExpression(call))\n return 'returns something other than a shared fake or a typed mock helper';\n const kind = calleeKind(call, owners, bindings, resolve);\n if (!kind) return 'returns through a function that is no listed shared fake or typed mock helper';\n if (kind === 'fake') return null;\n if (call.typeArguments?.length)\n return 'writes the type of its typed mock helper instead of taking it from importOriginal';\n const original = parameterName(fn);\n const [first] = call.arguments;\n const passed = first ? unwrap(first) : null;\n if (!original || !passed || !ts.isIdentifier(passed) || passed.text !== original)\n return \"hands its typed mock helper something other than the factory's own importOriginal\";\n return null;\n}\n\nfunction ownersOf(facts: TypedMockFacts): Owners {\n const owners = new Map<\n string,\n { kind: 'fake' } | { kind: 'helper'; names: ReadonlySet<string> }\n >();\n for (const fake of facts.fakes) owners.set(fake.owner, { kind: 'fake' });\n for (const helper of facts.typedHelpers) {\n const already = owners.get(helper.owner);\n if (already?.kind === 'fake') continue;\n owners.set(helper.owner, {\n kind: 'helper',\n names: new Set([...(already?.names ?? []), ...helper.names]),\n });\n }\n return owners;\n}\n\nfunction ownerFiles(facts: TypedMockFacts): Set<string> {\n return new Set([\n ...facts.fakes.map((fake) => fake.owner),\n ...facts.typedHelpers.map((h) => h.owner),\n ]);\n}\n\n// A package-owned helper is named by its package entry (`@scope/pkg/testing`), not a repo file.\nfunction missingOwner(reader: RepoReader, owner: string): string | null {\n if (isLocalModule(owner) || owner.startsWith('.'))\n return reader.exists(owner) ? null : 'this typed fake or helper file no longer exists';\n const segments = owner.split('/');\n const name = segments.slice(0, owner.startsWith('@') ? 2 : 1).join('/');\n const subpath = segments.slice(name.split('/').length).join('/');\n const manifest = reader.readOutside(\n join(reader.layout.repoRoot, 'node_modules', ...name.split('/'), 'package.json'),\n );\n if (manifest === null) return `no installed package ${name} holds this typed helper entry`;\n const exports: unknown = (JSON.parse(manifest) as { exports?: unknown }).exports;\n const key = subpath ? `./${subpath}` : '.';\n return exports && typeof exports === 'object' && key in exports\n ? null\n : `${name} exports no ${key} entry for this typed helper`;\n}\n\nfunction checkTyped(reader: RepoReader, facts: TypedMockFacts, files: FileView[]): Finding[] {\n const findings: Finding[] = [];\n const aliases = aliasList(facts.importAliases);\n const owners = ownersOf(facts);\n const skip = ownerFiles(facts);\n for (const owner of skip) {\n const missing = missingOwner(reader, owner);\n if (missing) findings.push({ file: owner, detail: missing });\n }\n for (const file of files) {\n if (skip.has(file.path)) continue;\n const resolve = (spec: string) => resolveSpecifier(reader, file.path, spec, aliases);\n for (const site of sitesOf(reader, file, aliases)) {\n const report = (node: ts.Node, why: string) =>\n findings.push({\n file: file.path,\n line: lineOfNode(file, node),\n detail: `mocks ${site.module} with a factory that ${why}`,\n });\n if (!site.byImport)\n findings.push({\n file: file.path,\n line: lineOfNode(file, site.node),\n detail:\n `mocks ${site.module} by a string path — name it through import('…'), so the factory's ` +\n \"importOriginal carries the module's own type\",\n });\n if (!site.factory || isRunnerOptions(site.factory)) continue;\n const fn = factoryFunction(site.factory);\n if (!fn) {\n report(site.node, 'the guard cannot read, since it is not written at the mock');\n continue;\n }\n const returns = returnedExpressions(fn);\n if (!returns.length) report(site.node, 'returns nothing');\n const bindings = bindingsOf(fn, resolve);\n for (const returned of returns) {\n const why = untypedReason(returned, fn, owners, bindings, resolve);\n if (why) report(returned ?? site.node, why);\n }\n }\n }\n return findings;\n}\n\nconst isOriginalSpread = (expression: ts.Expression, original: string | null): boolean => {\n const value = awaited(expression);\n if (!ts.isCallExpression(value)) return false;\n const callee = unwrap(value.expression);\n return (\n (ts.isIdentifier(callee) && callee.text === original) ||\n (ts.isPropertyAccessExpression(callee) &&\n ts.isIdentifier(callee.expression) &&\n callee.expression.text === 'vi' &&\n callee.name.text === 'importActual')\n );\n};\n\ntype OverrideRead = { name: string; node: ts.Node } | { unread: ts.Node };\n\nfunction overridesOf(returned: ts.Expression, original: string | null): OverrideRead[] {\n const value = awaited(returned);\n const objects: ts.ObjectLiteralExpression[] = [];\n const out: OverrideRead[] = [];\n if (ts.isObjectLiteralExpression(value)) objects.push(value);\n else if (ts.isCallExpression(value))\n for (const argument of value.arguments) {\n const arg = unwrap(argument);\n if (ts.isObjectLiteralExpression(arg)) objects.push(arg);\n else if (!(ts.isIdentifier(arg) && arg.text === original)) out.push({ unread: arg });\n }\n for (const object of objects)\n for (const property of object.properties) {\n if (ts.isSpreadAssignment(property)) {\n if (!isOriginalSpread(property.expression, original)) out.push({ unread: property });\n continue;\n }\n const name = property.name;\n if (name && (ts.isIdentifier(name) || ts.isStringLiteralLike(name)))\n out.push({ name: name.text, node: property });\n else out.push({ unread: property });\n }\n return out;\n}\n\nfunction checkForbidden(reader: RepoReader, facts: TypedMockFacts, files: FileView[]): Finding[] {\n const findings: Finding[] = [];\n const aliases = aliasList(facts.importAliases);\n const byModule = new Map(facts.forbiddenOverrides.map((entry) => [entry.module, entry]));\n for (const file of files) {\n for (const site of sitesOf(reader, file, aliases)) {\n const entry = byModule.get(site.module);\n if (!entry || !site.factory || isRunnerOptions(site.factory)) continue;\n const fn = factoryFunction(site.factory);\n if (!fn) continue;\n const forbidden = new Set(entry.names);\n for (const returned of returnedExpressions(fn)) {\n if (!returned) continue;\n for (const read of overridesOf(returned, parameterName(fn))) {\n if ('unread' in read) {\n findings.push({\n file: file.path,\n line: lineOfNode(file, read.unread),\n detail: `overrides ${site.module} through names the guard cannot read — write each override by name, since ${entry.names.join(', ')} must stay real: ${entry.why}`,\n });\n continue;\n }\n if (!forbidden.has(read.name)) continue;\n findings.push({\n file: file.path,\n line: lineOfNode(file, read.node),\n detail: `overrides ${site.module}'s ${read.name} — the real one runs in every test: ${entry.why}`,\n });\n }\n }\n }\n }\n return findings;\n}\n\nfunction factProblems(facts: TypedMockFacts): string[] {\n const problems: string[] = [];\n if (!objectProblem(facts, 'the facts', problems)) return problems;\n importAliasesProblem(facts.importAliases, 'importAliases', problems);\n if (!Array.isArray(facts.fakes))\n problems.push('fakes is missing (declare [] when there is none)');\n else\n for (const fake of facts.fakes as unknown[]) {\n const entry = (fake && typeof fake === 'object' ? fake : {}) as Record<string, unknown>;\n textProblem(entry.module, 'an entry of fakes’ module', problems);\n pathProblem(entry.owner, `fakes entry ${String(entry.module)}'s owner`, problems);\n }\n if (!Array.isArray(facts.typedHelpers))\n problems.push('typedHelpers is missing (declare [] when there is none)');\n else\n for (const helper of facts.typedHelpers as unknown[]) {\n const entry = (helper && typeof helper === 'object' ? helper : {}) as Record<string, unknown>;\n pathProblem(entry.owner, \"a typedHelpers entry's owner\", problems);\n namesProblem(entry.names, `typedHelpers entry ${String(entry.owner)}'s names`, problems);\n }\n if (!Array.isArray(facts.forbiddenOverrides))\n problems.push('forbiddenOverrides is missing (declare [] when there is none)');\n else {\n const seen = new Set<string>();\n for (const raw of facts.forbiddenOverrides as unknown[]) {\n const entry = (raw && typeof raw === 'object' ? raw : {}) as Record<string, unknown>;\n if (!isText(entry.module)) {\n problems.push('an entry of forbiddenOverrides names no module');\n continue;\n }\n if (seen.has(entry.module)) problems.push(`forbiddenOverrides lists ${entry.module} twice`);\n seen.add(entry.module);\n namesProblem(entry.names, `forbiddenOverrides entry ${entry.module}'s names`, problems);\n textProblem(entry.why, `forbiddenOverrides entry ${entry.module}'s why`, problems);\n }\n }\n return problems;\n}\n\nexport const typedMockGuard: GenericGuardDefinition<TypedMockFacts> = {\n id: 'typedMocks',\n absentWhen: false,\n factProblems,\n build(facts) {\n return [\n {\n title: 'every module mock is typed against the module it replaces',\n description:\n \"A test's module mock names its module through `import('…')` and returns, on every path, through the \" +\n \"module's shared fake or a typed mock helper. A helper takes its module type from the factory's own \" +\n '`importOriginal`, never from a type argument the test writes, so a wrong module, a misspelled export, ' +\n 'and a wrong signature fail to compile. A factory that returns a hand-written object is never compared to ' +\n 'the module, so a double that no longer matches it still passes. A mock with no factory, or with the ' +\n 'runner’s options object (`{ spy: true }`) in its place, is the runner’s own and passes.',\n timeoutMs: REPOSITORY_GUARD_TIMEOUT_MS,\n run(context) {\n return checkTyped(context.reader, facts, context.files(SCOPE));\n },\n },\n {\n title: 'no test overrides an export whose real behavior every test needs',\n description:\n 'A listed export is never replaced in a test, by its shared fake’s overrides or by a factory of its own: ' +\n 'a stand-in that behaves differently on the path a case proves lets the case pass on behavior the app ' +\n 'never has. The guard reads the names each override object sets at its top level; a spread of anything ' +\n 'but the real module, a computed name, or an override object it cannot see fails, since it could hide a ' +\n 'listed one. Each entry names its module, the exports, and why the real one runs.',\n timeoutMs: REPOSITORY_GUARD_TIMEOUT_MS,\n run(context) {\n return checkForbidden(context.reader, facts, context.files(SCOPE));\n },\n },\n ];\n },\n};\n"]}
|
|
1
|
+
{"version":3,"file":"typed-mocks.js","sourceRoot":"","sources":["../../../src/modules/repo/typed-mocks.ts"],"names":[],"mappings":"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","sourcesContent":["import ts from 'typescript';\nimport { join } from 'node:path';\nimport type { Finding } from '../../guard.js';\nimport type { RepoReader, Scope } from '../../node/reader.js';\nimport type { GenericGuardDefinition } from '../../node/suite/config.js';\nimport type { FileView } from '../../reader/view.js';\nimport { lineOfNode, unwrap } from '../../support/ast.js';\nimport {\n awaited,\n bindingsOf,\n calledExport,\n factoryFunction,\n isRunnerOptions,\n parameterName,\n returnedExpressions,\n type Binding,\n type Factory,\n} from '../../support/mock-factories.js';\nimport {\n importAliasesProblem,\n isText,\n namesProblem,\n objectProblem,\n pathProblem,\n textProblem,\n} from '../../support/facts.js';\nimport { aliasList, isLocalModule, resolveSpecifier } from '../../support/resolve-specifier.js';\nimport { REPOSITORY_GUARD_TIMEOUT_MS } from '../../support/timeouts.js';\nimport type { SharedFake } from './shared-fakes.js';\n\nconst MOCK_CALLS: ReadonlySet<string> = new Set(['vi.mock', 'vi.doMock']);\n\nconst SCOPE: Scope = {\n roots: ['app', 'functions', 'scripts', 'workers'],\n kinds: ['code'],\n tests: 'only',\n optionalRoots: ['functions', 'scripts', 'workers'],\n};\n\nexport interface TypedMockHelper {\n owner: string;\n names: readonly string[];\n}\n\nexport interface FakeCall {\n name: string;\n overridesArgument: number;\n}\n\nexport interface ForbiddenOverride {\n module: string;\n names: readonly string[];\n why: string;\n fakeCalls?: readonly FakeCall[];\n}\n\nexport interface TypedMockFacts {\n importAliases: Readonly<Record<string, string>>;\n fakes: readonly SharedFake[];\n typedHelpers: readonly TypedMockHelper[];\n forbiddenOverrides: readonly ForbiddenOverride[];\n}\n\ninterface Site {\n node: ts.CallExpression;\n module: string;\n byImport: boolean;\n factory: ts.Expression | undefined;\n}\n\nfunction sitesOf(reader: RepoReader, file: FileView, aliases: Array<[string, string]>): Site[] {\n const out: Site[] = [];\n for (const site of file.imports) {\n if (site.kind !== 'mock' || !MOCK_CALLS.has(site.callee ?? '')) continue;\n if (!ts.isCallExpression(site.node)) continue;\n out.push({\n node: site.node,\n module: resolveSpecifier(reader, file.path, site.spec, aliases),\n byImport: site.byImport,\n factory: site.node.arguments[1],\n });\n }\n return out;\n}\n\ntype OwnerKind = 'fake' | 'helper';\ntype Owners = ReadonlyMap<\n string,\n { kind: 'fake' } | { kind: 'helper'; names: ReadonlySet<string> }\n>;\n\nfunction ownerKind(owners: Owners, path: string, name: string): OwnerKind | null {\n const owner = owners.get(path);\n if (!owner) return null;\n if (owner.kind === 'fake') return 'fake';\n return owner.names.has(name) ? 'helper' : null;\n}\n\nfunction calleeKind(\n call: ts.CallExpression,\n owners: Owners,\n bindings: Map<string, Binding>,\n resolve: (spec: string) => string,\n): OwnerKind | null {\n const target = calledExport(call, bindings, resolve);\n return target ? ownerKind(owners, target.path, target.name) : null;\n}\n\nfunction untypedReason(\n expression: ts.Expression | null,\n fn: Factory,\n owners: Owners,\n bindings: Map<string, Binding>,\n resolve: (spec: string) => string,\n): string | null {\n const call = expression ? awaited(expression) : null;\n if (!call || !ts.isCallExpression(call))\n return 'returns something other than a shared fake or a typed mock helper';\n const kind = calleeKind(call, owners, bindings, resolve);\n if (!kind) return 'returns through a function that is no listed shared fake or typed mock helper';\n if (kind === 'fake') return null;\n if (call.typeArguments?.length)\n return 'writes the type of its typed mock helper instead of taking it from importOriginal';\n const original = parameterName(fn);\n const [first] = call.arguments;\n const passed = first ? unwrap(first) : null;\n if (!original || !passed || !ts.isIdentifier(passed) || passed.text !== original)\n return \"hands its typed mock helper something other than the factory's own importOriginal\";\n return null;\n}\n\nfunction ownersOf(facts: TypedMockFacts): Owners {\n const owners = new Map<\n string,\n { kind: 'fake' } | { kind: 'helper'; names: ReadonlySet<string> }\n >();\n for (const fake of facts.fakes) owners.set(fake.owner, { kind: 'fake' });\n for (const helper of facts.typedHelpers) {\n const already = owners.get(helper.owner);\n if (already?.kind === 'fake') continue;\n owners.set(helper.owner, {\n kind: 'helper',\n names: new Set([...(already?.names ?? []), ...helper.names]),\n });\n }\n return owners;\n}\n\nfunction ownerFiles(facts: TypedMockFacts): Set<string> {\n return new Set([\n ...facts.fakes.map((fake) => fake.owner),\n ...facts.typedHelpers.map((h) => h.owner),\n ]);\n}\n\n// A package-owned helper is named by its package entry (`@scope/pkg/testing`), not a repo file.\nfunction missingOwner(reader: RepoReader, owner: string): string | null {\n if (isLocalModule(owner) || owner.startsWith('.'))\n return reader.exists(owner) ? null : 'this typed fake or helper file no longer exists';\n const segments = owner.split('/');\n const name = segments.slice(0, owner.startsWith('@') ? 2 : 1).join('/');\n const subpath = segments.slice(name.split('/').length).join('/');\n const manifest = reader.readOutside(\n join(reader.layout.repoRoot, 'node_modules', ...name.split('/'), 'package.json'),\n );\n if (manifest === null) return `no installed package ${name} holds this typed helper entry`;\n const exports: unknown = (JSON.parse(manifest) as { exports?: unknown }).exports;\n const key = subpath ? `./${subpath}` : '.';\n return exports && typeof exports === 'object' && key in exports\n ? null\n : `${name} exports no ${key} entry for this typed helper`;\n}\n\nfunction checkTyped(reader: RepoReader, facts: TypedMockFacts, files: FileView[]): Finding[] {\n const findings: Finding[] = [];\n const aliases = aliasList(facts.importAliases);\n const owners = ownersOf(facts);\n const skip = ownerFiles(facts);\n for (const owner of skip) {\n const missing = missingOwner(reader, owner);\n if (missing) findings.push({ file: owner, detail: missing });\n }\n for (const file of files) {\n if (skip.has(file.path)) continue;\n const resolve = (spec: string) => resolveSpecifier(reader, file.path, spec, aliases);\n for (const site of sitesOf(reader, file, aliases)) {\n const report = (node: ts.Node, why: string) =>\n findings.push({\n file: file.path,\n line: lineOfNode(file, node),\n detail: `mocks ${site.module} with a factory that ${why}`,\n });\n if (!site.byImport)\n findings.push({\n file: file.path,\n line: lineOfNode(file, site.node),\n detail:\n `mocks ${site.module} by a string path — name it through import('…'), so the factory's ` +\n \"importOriginal carries the module's own type\",\n });\n if (!site.factory || isRunnerOptions(site.factory)) continue;\n const fn = factoryFunction(site.factory);\n if (!fn) {\n report(site.node, 'the guard cannot read, since it is not written at the mock');\n continue;\n }\n const returns = returnedExpressions(fn);\n if (!returns.length) report(site.node, 'returns nothing');\n const bindings = bindingsOf(fn, resolve);\n for (const returned of returns) {\n const why = untypedReason(returned, fn, owners, bindings, resolve);\n if (why) report(returned ?? site.node, why);\n }\n }\n }\n return findings;\n}\n\nconst isOriginalSpread = (expression: ts.Expression, original: string | null): boolean => {\n const value = awaited(expression);\n if (!ts.isCallExpression(value)) return false;\n const callee = unwrap(value.expression);\n return (\n (ts.isIdentifier(callee) && callee.text === original) ||\n (ts.isPropertyAccessExpression(callee) &&\n ts.isIdentifier(callee.expression) &&\n callee.expression.text === 'vi' &&\n callee.name.text === 'importActual')\n );\n};\n\ntype OverrideRead = { name: string; node: ts.Node } | { unread: ts.Node };\n\nfunction calleeName(call: ts.CallExpression): string | null {\n const callee = unwrap(call.expression);\n if (ts.isIdentifier(callee)) return callee.text;\n if (ts.isPropertyAccessExpression(callee)) return callee.name.text;\n return null;\n}\n\nfunction overridesOf(\n returned: ts.Expression,\n original: string | null,\n fakeCalls: ReadonlyMap<string, number>,\n): OverrideRead[] {\n const value = awaited(returned);\n const objects: ts.ObjectLiteralExpression[] = [];\n const out: OverrideRead[] = [];\n const name = ts.isCallExpression(value) ? calleeName(value) : null;\n const overridesAt = name === null ? undefined : fakeCalls.get(name);\n if (ts.isObjectLiteralExpression(value)) objects.push(value);\n else if (ts.isCallExpression(value) && overridesAt !== undefined) {\n const argument = value.arguments[overridesAt];\n if (argument) {\n const arg = unwrap(argument);\n if (ts.isObjectLiteralExpression(arg)) objects.push(arg);\n else out.push({ unread: arg });\n }\n } else if (ts.isCallExpression(value))\n for (const argument of value.arguments) {\n const arg = unwrap(argument);\n if (ts.isObjectLiteralExpression(arg)) objects.push(arg);\n else if (!(ts.isIdentifier(arg) && arg.text === original)) out.push({ unread: arg });\n }\n for (const object of objects)\n for (const property of object.properties) {\n if (ts.isSpreadAssignment(property)) {\n if (!isOriginalSpread(property.expression, original)) out.push({ unread: property });\n continue;\n }\n const name = property.name;\n if (name && (ts.isIdentifier(name) || ts.isStringLiteralLike(name)))\n out.push({ name: name.text, node: property });\n else out.push({ unread: property });\n }\n return out;\n}\n\nfunction checkForbidden(reader: RepoReader, facts: TypedMockFacts, files: FileView[]): Finding[] {\n const findings: Finding[] = [];\n const aliases = aliasList(facts.importAliases);\n const byModule = new Map(facts.forbiddenOverrides.map((entry) => [entry.module, entry]));\n for (const file of files) {\n for (const site of sitesOf(reader, file, aliases)) {\n const entry = byModule.get(site.module);\n if (!entry || !site.factory || isRunnerOptions(site.factory)) continue;\n const fn = factoryFunction(site.factory);\n if (!fn) continue;\n const forbidden = new Set(entry.names);\n const fakeCalls = new Map(\n (entry.fakeCalls ?? []).map((call) => [call.name, call.overridesArgument]),\n );\n for (const returned of returnedExpressions(fn)) {\n if (!returned) continue;\n for (const read of overridesOf(returned, parameterName(fn), fakeCalls)) {\n if ('unread' in read) {\n findings.push({\n file: file.path,\n line: lineOfNode(file, read.unread),\n detail: `overrides ${site.module} through names the guard cannot read — write each override by name, since ${entry.names.join(', ')} must stay real: ${entry.why}`,\n });\n continue;\n }\n if (!forbidden.has(read.name)) continue;\n findings.push({\n file: file.path,\n line: lineOfNode(file, read.node),\n detail: `overrides ${site.module}'s ${read.name} — the real one runs in every test: ${entry.why}`,\n });\n }\n }\n }\n }\n return findings;\n}\n\nfunction factProblems(facts: TypedMockFacts): string[] {\n const problems: string[] = [];\n if (!objectProblem(facts, 'the facts', problems)) return problems;\n importAliasesProblem(facts.importAliases, 'importAliases', problems);\n if (!Array.isArray(facts.fakes))\n problems.push('fakes is missing (declare [] when there is none)');\n else\n for (const fake of facts.fakes as unknown[]) {\n const entry = (fake && typeof fake === 'object' ? fake : {}) as Record<string, unknown>;\n textProblem(entry.module, 'an entry of fakes’ module', problems);\n pathProblem(entry.owner, `fakes entry ${String(entry.module)}'s owner`, problems);\n }\n if (!Array.isArray(facts.typedHelpers))\n problems.push('typedHelpers is missing (declare [] when there is none)');\n else\n for (const helper of facts.typedHelpers as unknown[]) {\n const entry = (helper && typeof helper === 'object' ? helper : {}) as Record<string, unknown>;\n pathProblem(entry.owner, \"a typedHelpers entry's owner\", problems);\n namesProblem(entry.names, `typedHelpers entry ${String(entry.owner)}'s names`, problems);\n }\n if (!Array.isArray(facts.forbiddenOverrides))\n problems.push('forbiddenOverrides is missing (declare [] when there is none)');\n else {\n const seen = new Set<string>();\n for (const raw of facts.forbiddenOverrides as unknown[]) {\n const entry = (raw && typeof raw === 'object' ? raw : {}) as Record<string, unknown>;\n if (!isText(entry.module)) {\n problems.push('an entry of forbiddenOverrides names no module');\n continue;\n }\n if (seen.has(entry.module)) problems.push(`forbiddenOverrides lists ${entry.module} twice`);\n seen.add(entry.module);\n namesProblem(entry.names, `forbiddenOverrides entry ${entry.module}'s names`, problems);\n textProblem(entry.why, `forbiddenOverrides entry ${entry.module}'s why`, problems);\n if (entry.fakeCalls === undefined) continue;\n if (!Array.isArray(entry.fakeCalls)) {\n problems.push(`forbiddenOverrides entry ${entry.module}'s fakeCalls is not a list`);\n continue;\n }\n for (const call of entry.fakeCalls as unknown[]) {\n const fake = (call && typeof call === 'object' ? call : {}) as Record<string, unknown>;\n if (!isText(fake.name)) problems.push(`a fakeCalls entry of ${entry.module} names no fake`);\n if (\n typeof fake.overridesArgument !== 'number' ||\n !Number.isInteger(fake.overridesArgument) ||\n fake.overridesArgument < 1\n )\n problems.push(\n `fakeCalls entry ${String(fake.name)} of ${entry.module} needs overridesArgument, the position of its override object after importOriginal`,\n );\n }\n }\n }\n return problems;\n}\n\nexport const typedMockGuard: GenericGuardDefinition<TypedMockFacts> = {\n id: 'typedMocks',\n absentWhen: false,\n factProblems,\n build(facts) {\n return [\n {\n title: 'every module mock is typed against the module it replaces',\n description:\n \"A test's module mock names its module through `import('…')` and returns, on every path, through the \" +\n \"module's shared fake or a typed mock helper. A helper takes its module type from the factory's own \" +\n '`importOriginal`, never from a type argument the test writes, so a wrong module, a misspelled export, ' +\n 'and a wrong signature fail to compile. A factory that returns a hand-written object is never compared to ' +\n 'the module, so a double that no longer matches it still passes. A mock with no factory, or with the ' +\n 'runner’s options object (`{ spy: true }`) in its place, is the runner’s own and passes.',\n timeoutMs: REPOSITORY_GUARD_TIMEOUT_MS,\n run(context) {\n return checkTyped(context.reader, facts, context.files(SCOPE));\n },\n },\n {\n title: 'no test overrides an export whose real behavior every test needs',\n description:\n 'A listed export is never replaced in a test, by its shared fake’s overrides or by a factory of its own: ' +\n 'a stand-in that behaves differently on the path a case proves lets the case pass on behavior the app ' +\n 'never has. The guard reads the names each override object sets at its top level; a spread of anything ' +\n 'but the real module, a computed name, or an override object it cannot see fails, since it could hide a ' +\n 'listed one. Each entry names its module, the exports, and why the real one runs. An entry may name ' +\n 'its shared fake’s calls with the position of their override object: for a call to that fake only that ' +\n 'argument is read, and the arguments before it are the fake’s state.',\n timeoutMs: REPOSITORY_GUARD_TIMEOUT_MS,\n run(context) {\n return checkForbidden(context.reader, facts, context.files(SCOPE));\n },\n },\n ];\n },\n};\n"]}
|