@1aboveio/skills 1.0.8 → 1.0.9
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/.codex-plugin/plugin.json +13 -0
- package/.codex-plugin/resources.json +1827 -0
- package/README.md +22 -0
- package/package.json +9 -2
- package/skills/app-dev/airflow/SKILL.md +1 -1
- package/skills/app-dev/airflow/release-check.mjs +3 -1
- package/skills/app-dev/android-foundation/SKILL.md +1 -1
- package/skills/app-dev/android-foundation/release-check.mjs +3 -1
- package/skills/app-dev/app-debug/SKILL.md +1 -1
- package/skills/app-dev/app-debug/release-check.mjs +3 -1
- package/skills/app-dev/better-auth/SKILL.md +1 -1
- package/skills/app-dev/better-auth/release-check.mjs +3 -1
- package/skills/app-dev/data-catalog/SKILL.md +1 -1
- package/skills/app-dev/data-catalog/release-check.mjs +3 -1
- package/skills/app-dev/data-quality/SKILL.md +1 -1
- package/skills/app-dev/data-quality/release-check.mjs +3 -1
- package/skills/app-dev/ios-foundation/SKILL.md +1 -1
- package/skills/app-dev/ios-foundation/release-check.mjs +3 -1
- package/skills/app-dev/monorepo/SKILL.md +1 -1
- package/skills/app-dev/monorepo/release-check.mjs +3 -1
- package/skills/app-dev/nextjs-fullstack/SKILL.md +1 -1
- package/skills/app-dev/nextjs-fullstack/release-check.mjs +3 -1
- package/skills/app-dev/prisma-setup/SKILL.md +1 -1
- package/skills/app-dev/prisma-setup/release-check.mjs +3 -1
- package/skills/app-dev/pyspark/SKILL.md +1 -1
- package/skills/app-dev/pyspark/release-check.mjs +3 -1
- package/skills/app-dev/python-backend/SKILL.md +1 -1
- package/skills/app-dev/python-backend/release-check.mjs +3 -1
- package/skills/app-dev/shadcn/SKILL.md +1 -1
- package/skills/app-dev/shadcn/release-check.mjs +3 -1
- package/skills/app-dev/zod-v4/SKILL.md +1 -1
- package/skills/app-dev/zod-v4/release-check.mjs +3 -1
- package/skills/cicd/cloud-build/SKILL.md +1 -1
- package/skills/cicd/cloud-build/release-check.mjs +3 -1
- package/skills/cicd/cloud-debug/SKILL.md +1 -1
- package/skills/cicd/cloud-debug/release-check.mjs +3 -1
- package/skills/cicd/cloud-deploy/SKILL.md +1 -1
- package/skills/cicd/cloud-deploy/release-check.mjs +3 -1
- package/skills/cicd/docker/SKILL.md +1 -1
- package/skills/cicd/docker/release-check.mjs +3 -1
- package/skills/cicd/github-actions/SKILL.md +1 -1
- package/skills/cicd/github-actions/release-check.mjs +3 -1
- package/skills/cicd/github-flow/SKILL.md +1 -1
- package/skills/cicd/github-flow/release-check.mjs +3 -1
- package/skills/cicd/google-cloud/SKILL.md +1 -1
- package/skills/cicd/google-cloud/release-check.mjs +3 -1
- package/skills/cicd/mergify/SKILL.md +1 -1
- package/skills/cicd/mergify/release-check.mjs +3 -1
- package/skills/cicd/podman/SKILL.md +1 -1
- package/skills/cicd/podman/release-check.mjs +3 -1
- package/skills/content/article-review/SKILL.md +1 -1
- package/skills/content/article-review/release-check.mjs +3 -1
- package/skills/content/article-to-image/SKILL.md +1 -1
- package/skills/content/article-to-image/release-check.mjs +3 -1
- package/skills/content/flowchart-svg/SKILL.md +1 -1
- package/skills/content/flowchart-svg/release-check.mjs +3 -1
- package/skills/content/image-gen/SKILL.md +1 -1
- package/skills/content/image-gen/release-check.mjs +3 -1
- package/skills/content/mermaid/SKILL.md +1 -1
- package/skills/content/mermaid/release-check.mjs +3 -1
- package/skills/content/wechat-article-writer/SKILL.md +1 -1
- package/skills/content/wechat-article-writer/release-check.mjs +3 -1
- package/skills/content/wechat-publisher/SKILL.md +1 -1
- package/skills/content/wechat-publisher/release-check.mjs +3 -1
- package/skills/content/wechat-search/SKILL.md +1 -1
- package/skills/content/wechat-search/release-check.mjs +3 -1
- package/skills/content/xai-search/SKILL.md +88 -13
- package/skills/content/xai-search/release-check.mjs +3 -1
- package/skills/content/xai-search/scripts/xai_search.sh +125 -149
- package/skills/core/distill-writing/SKILL.md +1 -1
- package/skills/core/distill-writing/release-check.mjs +3 -1
- package/skills/core/doc-to-md/SKILL.md +1 -1
- package/skills/core/doc-to-md/release-check.mjs +3 -1
- package/skills/core/editorial-review/SKILL.md +1 -1
- package/skills/core/editorial-review/release-check.mjs +3 -1
- package/skills/core/house-style/SKILL.md +1 -1
- package/skills/core/house-style/release-check.mjs +3 -1
- package/skills/core/plain-language/SKILL.md +1 -1
- package/skills/core/plain-language/release-check.mjs +3 -1
- package/skills/core/polish-cn/SKILL.md +1 -1
- package/skills/core/polish-cn/release-check.mjs +3 -1
- package/skills/core/reg-parser/SKILL.md +1 -1
- package/skills/core/reg-parser/release-check.mjs +3 -1
- package/skills/core/terminology/SKILL.md +1 -1
- package/skills/core/terminology/release-check.mjs +3 -1
- package/skills/core/write-doc/SKILL.md +1 -1
- package/skills/core/write-doc/release-check.mjs +3 -1
- package/skills/engineering/better-review/SKILL.md +1 -1
- package/skills/engineering/better-review/release-check.mjs +3 -1
- package/skills/engineering/contributing/SKILL.md +1 -1
- package/skills/engineering/contributing/release-check.mjs +3 -1
- package/skills/engineering/e2e-test/SKILL.md +1 -1
- package/skills/engineering/e2e-test/release-check.mjs +3 -1
- package/skills/engineering/engineering-runtime/SKILL.md +1 -1
- package/skills/engineering/engineering-runtime/coherence/workflow.json +13 -13
- package/skills/engineering/engineering-runtime/release-check.mjs +3 -1
- package/skills/engineering/engineering-runtime/scripts/installation-resolver.mjs +191 -0
- package/skills/engineering/engineering-runtime/scripts/workflow-coherence.mjs +51 -10
- package/skills/engineering/ensure-coverage/SKILL.md +1 -1
- package/skills/engineering/ensure-coverage/release-check.mjs +3 -1
- package/skills/engineering/golden-path/SKILL.md +1 -1
- package/skills/engineering/golden-path/release-check.mjs +3 -1
- package/skills/engineering/harness-runtime/SKILL.md +1 -1
- package/skills/engineering/harness-runtime/release-check.mjs +3 -1
- package/skills/engineering/resolve-issues/SKILL.md +1 -1
- package/skills/engineering/resolve-issues/references/preflight.md +7 -0
- package/skills/engineering/resolve-issues/references/shared-modules.md +27 -0
- package/skills/engineering/resolve-issues/references/task-plan.md +5 -1
- package/skills/engineering/resolve-issues/release-check.mjs +3 -1
- package/skills/engineering/resolve-issues/scripts/active-installation.mjs +32 -0
- package/skills/engineering/resolve-issues/scripts/install-hooks.mjs +3 -0
- package/skills/engineering/resolve-issues/scripts/rehydrate.mjs +17 -1
- package/skills/engineering/resolve-release/SKILL.md +1 -1
- package/skills/engineering/resolve-release/release-check.mjs +3 -1
- package/skills/engineering/rush-issues/SKILL.md +1 -1
- package/skills/engineering/rush-issues/release-check.mjs +3 -1
- package/skills/engineering/rush-release/SKILL.md +1 -1
- package/skills/engineering/rush-release/release-check.mjs +3 -1
- package/skills/engineering/test-quality/SKILL.md +1 -1
- package/skills/engineering/test-quality/release-check.mjs +3 -1
- package/skills/risk-compliance/compliance-audit/SKILL.md +1 -1
- package/skills/risk-compliance/compliance-audit/release-check.mjs +3 -1
- package/skills/risk-compliance/contract-compare/SKILL.md +1 -1
- package/skills/risk-compliance/contract-compare/release-check.mjs +3 -1
- package/skills/risk-compliance/contract-review/SKILL.md +1 -1
- package/skills/risk-compliance/contract-review/release-check.mjs +3 -1
- package/skills/risk-compliance/doc-authenticity/SKILL.md +1 -1
- package/skills/risk-compliance/doc-authenticity/release-check.mjs +3 -1
- package/skills/risk-compliance/fraud-analysis/SKILL.md +1 -1
- package/skills/risk-compliance/fraud-analysis/release-check.mjs +3 -1
- package/skills/risk-compliance/payment-analysis/SKILL.md +1 -1
- package/skills/risk-compliance/payment-analysis/release-check.mjs +3 -1
- package/skills/risk-compliance/similar-domain/SKILL.md +1 -1
- package/skills/risk-compliance/similar-domain/release-check.mjs +3 -1
- package/skills/risk-compliance/tld-detection/SKILL.md +1 -1
- package/skills/risk-compliance/tld-detection/release-check.mjs +3 -1
- package/skills/app-dev/android-foundation/scripts/device-run.test.mjs +0 -436
- package/skills/app-dev/data-catalog/scripts/data-catalog.test.mjs +0 -36
- package/skills/app-dev/data-quality/scripts/data-quality.test.mjs +0 -12
- package/skills/cicd/github-actions/scripts/ci-run-audit.test.mjs +0 -637
- package/skills/cicd/github-flow/scripts/flow.test.mjs +0 -114
- package/skills/cicd/github-flow/scripts/persist-conventions.test.mjs +0 -108
- package/skills/cicd/mergify/scripts/audit.test.mjs +0 -201
- package/skills/cicd/mergify/scripts/entrypoints.test.mjs +0 -106
- package/skills/cicd/mergify/scripts/pr-620-mergify-comments.fixture.json +0 -30
- package/skills/cicd/mergify/scripts/stack-push.test.mjs +0 -233
- package/skills/cicd/mergify/scripts/watch-pr-delivery.test.mjs +0 -1826
- package/skills/content/image-gen/scripts/credentials.test.mjs +0 -212
- package/skills/content/image-gen/scripts/options.test.mjs +0 -245
- package/skills/content/xai-search/tests/smoke_test.sh +0 -36
- package/skills/core/polish-cn/scripts/polish-cn.test.mjs +0 -244
- package/skills/core/reg-parser/scripts/reg-parser.test.mjs +0 -165
- package/skills/engineering/better-review/scripts/pr-context.test.mjs +0 -168
- package/skills/engineering/better-review/scripts/rereview-scope.test.mjs +0 -587
- package/skills/engineering/better-review/scripts/scan-diff.test.mjs +0 -414
- package/skills/engineering/better-review/scripts/vendor-sync.test.mjs +0 -96
- package/skills/engineering/contributing/scripts/persist-conventions.test.mjs +0 -118
- package/skills/engineering/e2e-test/scripts/presentation-checks.test.mjs +0 -106
- package/skills/engineering/e2e-test/scripts/presentation-perceivability.test.mjs +0 -117
- package/skills/engineering/e2e-test/scripts/presentation-reachability.test.mjs +0 -69
- package/skills/engineering/e2e-test/scripts/presentation-render-health.test.mjs +0 -98
- package/skills/engineering/e2e-test/scripts/presentation-sweep.test.mjs +0 -71
- package/skills/engineering/e2e-test/scripts/presentation-temporal.test.mjs +0 -59
- package/skills/engineering/e2e-test/scripts/project-route-manifest.test.mjs +0 -76
- package/skills/engineering/engineering-runtime/engineering-runtime.test.mjs +0 -68
- package/skills/engineering/engineering-runtime/scripts/invocation-policy.test.mjs +0 -95
- package/skills/engineering/engineering-runtime/scripts/main-module.test.mjs +0 -288
- package/skills/engineering/engineering-runtime/scripts/required-check-plan.test.mjs +0 -96
- package/skills/engineering/ensure-coverage/scripts/adapters/nextjs-inventory.test.mjs +0 -96
- package/skills/engineering/ensure-coverage/scripts/check-quarantine-expiry.test.mjs +0 -47
- package/skills/engineering/ensure-coverage/scripts/ci-audit.test.mjs +0 -86
- package/skills/engineering/ensure-coverage/scripts/coverage-ledger.test.mjs +0 -447
- package/skills/engineering/ensure-coverage/scripts/design-parity.test.mjs +0 -552
- package/skills/engineering/ensure-coverage/scripts/lint-tests.test.mjs +0 -289
- package/skills/engineering/ensure-coverage/scripts/mock-policy-config.test.mjs +0 -119
- package/skills/engineering/golden-path/scripts/golden-path.test.mjs +0 -1457
- package/skills/engineering/harness-runtime/discover-models.test.mjs +0 -363
- package/skills/engineering/harness-runtime/harness-runtime.test.mjs +0 -967
- package/skills/engineering/harness-runtime/model-catalog-seed.test.mjs +0 -829
- package/skills/engineering/harness-runtime/model-catalog.test.mjs +0 -366
- package/skills/engineering/resolve-issues/scripts/cleanup-runs.test.mjs +0 -280
- package/skills/engineering/resolve-issues/scripts/dropbox.test.mjs +0 -346
- package/skills/engineering/resolve-issues/scripts/findings-reuse.test.mjs +0 -88
- package/skills/engineering/resolve-issues/scripts/github-reviews.test.mjs +0 -264
- package/skills/engineering/resolve-issues/scripts/install-hooks.test.mjs +0 -79
- package/skills/engineering/resolve-issues/scripts/issue-tree.test.mjs +0 -107
- package/skills/engineering/resolve-issues/scripts/manual-invocation.test.mjs +0 -14
- package/skills/engineering/resolve-issues/scripts/next-actions-capacity.test.mjs +0 -149
- package/skills/engineering/resolve-issues/scripts/next-actions.test.mjs +0 -915
- package/skills/engineering/resolve-issues/scripts/plan-sync.test.mjs +0 -156
- package/skills/engineering/resolve-issues/scripts/preflight-models.test.mjs +0 -448
- package/skills/engineering/resolve-issues/scripts/ready-check.test.mjs +0 -217
- package/skills/engineering/resolve-issues/scripts/rehydrate.test.mjs +0 -91
- package/skills/engineering/resolve-issues/scripts/review-verdict.test.mjs +0 -1060
- package/skills/engineering/resolve-issues/scripts/size-check.test.mjs +0 -458
- package/skills/engineering/resolve-release/scripts/apply.test.mjs +0 -99
- package/skills/engineering/resolve-release/scripts/dark-flags.test.mjs +0 -100
- package/skills/engineering/resolve-release/scripts/flow.test.mjs +0 -340
- package/skills/engineering/resolve-release/scripts/green-head.test.mjs +0 -101
- package/skills/engineering/resolve-release/scripts/npm-version-absence.test.mjs +0 -426
- package/skills/engineering/resolve-release/scripts/plan-npm.test.mjs +0 -659
- package/skills/engineering/resolve-release/scripts/plan-toml-boundary.test.mjs +0 -317
- package/skills/engineering/resolve-release/scripts/plan-uv.test.mjs +0 -291
- package/skills/engineering/resolve-release/scripts/plan.test.mjs +0 -428
- package/skills/engineering/test-quality/scripts/lint-tests.test.mjs +0 -591
- package/skills/engineering/test-quality/scripts/mock-policy-config.test.mjs +0 -119
- package/skills/engineering/test-quality/scripts/persist-conventions.test.mjs +0 -104
- package/skills/risk-compliance/compliance-audit/scripts/compliance-audit.test.mjs +0 -14
- package/skills/risk-compliance/doc-authenticity/manual-invocation.test.mjs +0 -12
- package/skills/risk-compliance/doc-authenticity/scripts/validate_verdict.test.mjs +0 -212
- package/skills/risk-compliance/tld-detection/scripts/detect_ladders.test.mjs +0 -14
- package/skills/risk-compliance/tld-detection/scripts/detect_streams.test.mjs +0 -14
- package/skills/risk-compliance/tld-detection/tests/test_detect_ladders.py +0 -134
- package/skills/risk-compliance/tld-detection/tests/test_detect_streams.py +0 -520
|
@@ -1,414 +0,0 @@
|
|
|
1
|
-
import { test } from 'node:test'
|
|
2
|
-
import assert from 'node:assert/strict'
|
|
3
|
-
import { spawnSync } from 'node:child_process'
|
|
4
|
-
import fs from 'node:fs'
|
|
5
|
-
import os from 'node:os'
|
|
6
|
-
import path from 'node:path'
|
|
7
|
-
import { fileURLToPath } from 'node:url'
|
|
8
|
-
import { parseAddedLines, interceptionLeads, regexLeads, branchLeads, weakAssertionLeads, scanDiff, parseFileChanges, characterizationLeads, extractMockTarget, classifyMockTarget, overMockFindings, concurrencyFixtureLeads, parseCliArgs, ScanDiffCliError, NO_INPUT_EXIT, USAGE_EXIT } from './scan-diff.mjs'
|
|
9
|
-
|
|
10
|
-
const SCRIPT = fileURLToPath(new URL('./scan-diff.mjs', import.meta.url))
|
|
11
|
-
// Run the real CLI: returns { status, stdout, stderr }. `input` is piped to stdin.
|
|
12
|
-
function runCli(args = [], input = '') {
|
|
13
|
-
const r = spawnSync(process.execPath, [SCRIPT, ...args], { input, encoding: 'utf8' })
|
|
14
|
-
return { status: r.status, stdout: r.stdout, stderr: r.stderr }
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const DIFF = `diff --git a/src/match.ts b/src/match.ts
|
|
18
|
-
--- a/src/match.ts
|
|
19
|
-
+++ b/src/match.ts
|
|
20
|
-
@@ -10,3 +10,6 @@ export function f() {
|
|
21
|
-
const keep = 1
|
|
22
|
-
+ const re = /\\/api\\/foo/
|
|
23
|
-
+ if (path.startsWith(x)) return true
|
|
24
|
-
+ const anchored = /^\\/exact$/
|
|
25
|
-
@@ -40,2 +40,3 @@
|
|
26
|
-
ctx()
|
|
27
|
-
+ page.route('**/api/x', r => r.fulfill({}))
|
|
28
|
-
`
|
|
29
|
-
|
|
30
|
-
const TESTDIFF = `--- a/e2e/x.spec.ts
|
|
31
|
-
+++ b/e2e/x.spec.ts
|
|
32
|
-
@@ -1,2 +1,3 @@
|
|
33
|
-
import { test } from '@playwright/test'
|
|
34
|
-
+ await page.route('**/api/y', r => r.fulfill({ status: 200 }))
|
|
35
|
-
`
|
|
36
|
-
|
|
37
|
-
test('parseAddedLines tracks file + new-file line numbers, skips removed', () => {
|
|
38
|
-
const added = parseAddedLines(DIFF)
|
|
39
|
-
// first hunk starts at new line 10: keep(10), re(11), if(12), anchored(13)
|
|
40
|
-
const reLine = added.find((a) => a.text.includes('const re ='))
|
|
41
|
-
assert.equal(reLine.file, 'src/match.ts')
|
|
42
|
-
assert.equal(reLine.line, 11)
|
|
43
|
-
const pr = added.find((a) => a.text.includes('page.route'))
|
|
44
|
-
assert.equal(pr.line, 41) // second hunk new-start 40: ctx(40), page.route(41)
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
test('regexLeads flags unanchored regex, not anchored ones', () => {
|
|
48
|
-
const leads = regexLeads(parseAddedLines(DIFF))
|
|
49
|
-
assert.ok(leads.some((l) => l.type === 'unanchored-pattern' && l.snippet.includes('api')), 'unanchored regex flagged')
|
|
50
|
-
assert.ok(!leads.some((l) => l.snippet.includes('exact')), 'anchored /^...$/ not flagged')
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
test('branchLeads flags new if + ternary', () => {
|
|
54
|
-
const leads = branchLeads(parseAddedLines('--- a/x.ts\n+++ b/x.ts\n@@ -1 +1,2 @@\n+ if (a) b()\n+ const y = c ? d : e\n'))
|
|
55
|
-
assert.equal(leads.length, 2)
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
test('interceptionLeads only fires in test/spec files', () => {
|
|
59
|
-
assert.equal(interceptionLeads(parseAddedLines(DIFF)).length, 0, 'page.route in a non-test file is not an interception lead')
|
|
60
|
-
const t = interceptionLeads(parseAddedLines(TESTDIFF))
|
|
61
|
-
assert.equal(t.length, 1)
|
|
62
|
-
assert.equal(t[0].type, 'first-party-interception')
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
test('weakAssertionLeads flags catch-swallow + disjunctive-truthy in test files only', () => {
|
|
66
|
-
// Mirrors the real storefront-journeys.spec.ts auth tests.
|
|
67
|
-
const diff = `--- a/e2e/journeys.spec.ts
|
|
68
|
-
+++ b/e2e/journeys.spec.ts
|
|
69
|
-
@@ -1,2 +1,5 @@
|
|
70
|
-
import { test, expect } from '@playwright/test'
|
|
71
|
-
+ const hasEmail = await page.getByLabel(/email/i).isVisible().catch(() => false)
|
|
72
|
-
+ expect(hasEmail || hasPassword || hasLoginButton).toBeTruthy()
|
|
73
|
-
+ expect(ok).toBe(true)
|
|
74
|
-
`
|
|
75
|
-
const leads = weakAssertionLeads(parseAddedLines(diff))
|
|
76
|
-
assert.ok(leads.some((l) => l.type === 'swallowed-assertion'), 'catch(() => false) flagged')
|
|
77
|
-
assert.ok(leads.some((l) => l.type === 'weak-assertion'), 'disjunctive toBeTruthy flagged')
|
|
78
|
-
// A plain expect(ok).toBe(true) with no `||` is not a weak-assertion lead.
|
|
79
|
-
assert.ok(!leads.some((l) => l.type === 'weak-assertion' && l.snippet.includes('toBe(true)')), 'non-disjunctive toBe(true) not flagged')
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
test('weakAssertionLeads does not fire outside test files', () => {
|
|
83
|
-
const diff = `--- a/src/app.ts\n+++ b/src/app.ts\n@@ -1 +1,2 @@\n+ const v = await thing().catch(() => false)\n`
|
|
84
|
-
assert.equal(weakAssertionLeads(parseAddedLines(diff)).length, 0)
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
test('scanDiff aggregates leads + the decidable findings channel', () => {
|
|
88
|
-
const r = scanDiff(DIFF)
|
|
89
|
-
assert.equal(r.kind, 'leads+findings')
|
|
90
|
-
assert.ok(r.counts['unanchored-pattern'] >= 1)
|
|
91
|
-
assert.ok(r.counts['new-branch'] >= 1)
|
|
92
|
-
assert.ok(Array.isArray(r.leads))
|
|
93
|
-
assert.ok(Array.isArray(r.findings))
|
|
94
|
-
assert.deepEqual(r.findings, [], 'DIFF fixture has no first-party mock → no over-mock findings')
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
// --- over-mock: the vendored, decidable internal-seam detector (FINDINGS) ---
|
|
98
|
-
|
|
99
|
-
test('extractMockTarget pulls vi/jest/vitest mock + doMock specifiers', () => {
|
|
100
|
-
assert.equal(extractMockTarget("vi." + "mock('@/lib/x')"), '@/lib/x')
|
|
101
|
-
assert.equal(extractMockTarget('jest.' + 'mock("pkg")'), 'pkg')
|
|
102
|
-
assert.equal(extractMockTarget('vitest.' + 'doMock(`./rel`)'), './rel')
|
|
103
|
-
assert.equal(extractMockTarget('expect(x).toBe(1)'), null)
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
test('classifyMockTarget: first-party seam internal, external/stdlib/test-util exempt', () => {
|
|
107
|
-
assert.equal(classifyMockTarget('@/lib/auth').internal, true) // alias
|
|
108
|
-
assert.equal(classifyMockTarget('~/db').internal, true) // alias
|
|
109
|
-
assert.equal(classifyMockTarget('../services/billing').internal, true) // relative into source
|
|
110
|
-
assert.equal(classifyMockTarget('node:fs').internal, false) // stdlib
|
|
111
|
-
assert.equal(classifyMockTarget('react').internal, false) // bare pkg
|
|
112
|
-
assert.equal(classifyMockTarget('./__mocks__/x').internal, false) // test util
|
|
113
|
-
// declared topology: a monorepo scope is first-party; a generated dir is exempt
|
|
114
|
-
const topo = { firstPartyAliasPrefixes: ['@/'], firstPartyPackages: ['@acme/'], externals: ['^@/generated/'], testUtilHints: [] }
|
|
115
|
-
assert.equal(classifyMockTarget('@acme/shared', topo).internal, true)
|
|
116
|
-
assert.equal(classifyMockTarget('@/generated/openapi', topo).internal, false)
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
test('overMockFindings flags an ADDED first-party mock in a test file (the incident vector)', () => {
|
|
120
|
-
const diff = [
|
|
121
|
-
'--- a/src/__tests__/route.test.ts',
|
|
122
|
-
'+++ b/src/__tests__/route.test.ts',
|
|
123
|
-
'@@ -1,1 +1,2 @@',
|
|
124
|
-
' import x',
|
|
125
|
-
"+vi." + "mock('@/lib/org-allowlist-client')",
|
|
126
|
-
'',
|
|
127
|
-
].join('\n')
|
|
128
|
-
const f = overMockFindings(parseAddedLines(diff))
|
|
129
|
-
assert.equal(f.length, 1)
|
|
130
|
-
assert.equal(f[0].type, 'over-mock-internal-seam')
|
|
131
|
-
assert.equal(f[0].target, '@/lib/org-allowlist-client')
|
|
132
|
-
})
|
|
133
|
-
|
|
134
|
-
test('overMockFindings does not flag an external mock, or a mock outside a test file', () => {
|
|
135
|
-
const ext = `--- a/src/x.test.ts\n+++ b/src/x.test.ts\n@@ -1 +1,2 @@\n+vi.mock('stripe')\n`
|
|
136
|
-
assert.equal(overMockFindings(parseAddedLines(ext)).length, 0)
|
|
137
|
-
const nonTest = ['--- a/src/app.ts', '+++ b/src/app.ts', '@@ -1 +1,2 @@', "+vi." + "mock('@/lib/x')", ''].join('\n')
|
|
138
|
-
assert.equal(overMockFindings(parseAddedLines(nonTest)).length, 0)
|
|
139
|
-
})
|
|
140
|
-
|
|
141
|
-
// --- characterization: modifies-existing-behavior detection ---
|
|
142
|
-
|
|
143
|
-
// A source file whose existing lines are CHANGED (has `-` content) → owes a
|
|
144
|
-
// characterization test. (The shared DIFF fixture is all additions, so it must
|
|
145
|
-
// NOT fire — see the pure-addition test below; this uses a real modification.)
|
|
146
|
-
const MODDIFF = `--- a/src/price.ts
|
|
147
|
-
+++ b/src/price.ts
|
|
148
|
-
@@ -10,3 +10,3 @@ export function total(items) {
|
|
149
|
-
const base = sum(items)
|
|
150
|
-
- return base * 1.2
|
|
151
|
-
+ return base * 1.15
|
|
152
|
-
`
|
|
153
|
-
test('characterizationLeads flags a source file with changed existing lines', () => {
|
|
154
|
-
const leads = characterizationLeads(MODDIFF)
|
|
155
|
-
assert.equal(leads.length, 1)
|
|
156
|
-
assert.equal(leads[0].type, 'modifies-existing-behavior')
|
|
157
|
-
assert.equal(leads[0].file, 'src/price.ts')
|
|
158
|
-
assert.ok(leads[0].line > 0)
|
|
159
|
-
})
|
|
160
|
-
|
|
161
|
-
test('characterizationLeads does NOT fire on the all-additions DIFF fixture', () => {
|
|
162
|
-
assert.equal(characterizationLeads(DIFF).length, 0, 'no removed lines => no existing behavior modified')
|
|
163
|
-
})
|
|
164
|
-
|
|
165
|
-
test('characterizationLeads ignores a brand-new file (new behavior, not modification)', () => {
|
|
166
|
-
const diff = `diff --git a/src/new.ts b/src/new.ts
|
|
167
|
-
new file mode 100644
|
|
168
|
-
--- /dev/null
|
|
169
|
-
+++ b/src/new.ts
|
|
170
|
-
@@ -0,0 +1,2 @@
|
|
171
|
-
+export function n() { return 1 }
|
|
172
|
-
+export const k = 2
|
|
173
|
-
`
|
|
174
|
-
assert.equal(characterizationLeads(diff).length, 0)
|
|
175
|
-
})
|
|
176
|
-
|
|
177
|
-
test('characterizationLeads ignores a pure addition into an existing file', () => {
|
|
178
|
-
const diff = `--- a/src/app.ts
|
|
179
|
-
+++ b/src/app.ts
|
|
180
|
-
@@ -5,0 +6,2 @@
|
|
181
|
-
+export function added() { return 1 }
|
|
182
|
-
+export const k = 2
|
|
183
|
-
`
|
|
184
|
-
assert.equal(characterizationLeads(diff).length, 0, 'no removed lines => added behavior, not a modification')
|
|
185
|
-
})
|
|
186
|
-
|
|
187
|
-
test('characterizationLeads ignores test files and non-source files', () => {
|
|
188
|
-
const testDiff = `--- a/e2e/x.spec.ts\n+++ b/e2e/x.spec.ts\n@@ -1,2 +1,2 @@\n- old()\n+ neo()\n`
|
|
189
|
-
const docDiff = `--- a/README.md\n+++ b/README.md\n@@ -1,2 +1,2 @@\n-old\n+new\n`
|
|
190
|
-
assert.equal(characterizationLeads(testDiff).length, 0, 'editing a test is not modifying production behavior')
|
|
191
|
-
assert.equal(characterizationLeads(docDiff).length, 0, 'docs carry no behavior to pin')
|
|
192
|
-
})
|
|
193
|
-
|
|
194
|
-
test('parseFileChanges records isNew, removed/added counts, and firstLine', () => {
|
|
195
|
-
const files = parseFileChanges(MODDIFF)
|
|
196
|
-
const m = files.get('src/price.ts')
|
|
197
|
-
assert.equal(m.isNew, false)
|
|
198
|
-
assert.ok(m.removed >= 1 && m.added >= 1)
|
|
199
|
-
assert.ok(m.firstLine > 0)
|
|
200
|
-
})
|
|
201
|
-
|
|
202
|
-
// Regression fixture: the real shape from skyee-ai-risk PR #957 / issue #938, where a
|
|
203
|
-
// test titled "replays and races" read an already-sealed plan, so both concurrent
|
|
204
|
-
// callers took the early return and the contended first seal was never exercised.
|
|
205
|
-
// The adversarial reviewer needed 5 real-PostgreSQL trials to find what the fixture
|
|
206
|
-
// states outright.
|
|
207
|
-
const RACE_FALSE_GREEN = `--- a/src/lib/x.integration.test.ts
|
|
208
|
-
+++ b/src/lib/x.integration.test.ts
|
|
209
|
-
@@ -270,0 +271,10 @@
|
|
210
|
-
+ it("replays and races deterministically, retains immutable authority", async () => {
|
|
211
|
-
+ const source = await db.blacklistRepairRun.findFirstOrThrow({
|
|
212
|
-
+ where: { ownerExecutionId: PREFIX },
|
|
213
|
-
+ include: { caseAuthorizationPlan: true },
|
|
214
|
-
+ })
|
|
215
|
-
+ const firstId = source.caseAuthorizationPlan!.id
|
|
216
|
-
+ const [left, right] = await Promise.all([
|
|
217
|
-
+ authorize(source.id, binding, { db }),
|
|
218
|
-
+ authorize(source.id, binding, { db }),
|
|
219
|
-
+ ])
|
|
220
|
-
`
|
|
221
|
-
|
|
222
|
-
test('concurrencyFixtureLeads flags a race test whose fixture reads pre-existing state', () => {
|
|
223
|
-
const leads = concurrencyFixtureLeads(parseAddedLines(RACE_FALSE_GREEN))
|
|
224
|
-
assert.equal(leads.length, 1)
|
|
225
|
-
assert.equal(leads[0].type, 'concurrency-test-preexisting-fixture')
|
|
226
|
-
assert.match(leads[0].snippet, /findFirstOrThrow|caseAuthorizationPlan/)
|
|
227
|
-
assert.ok(scanDiff(RACE_FALSE_GREEN).leads.some((l) => l.type === 'concurrency-test-preexisting-fixture'),
|
|
228
|
-
'must surface through scanDiff, not just the exported helper')
|
|
229
|
-
})
|
|
230
|
-
|
|
231
|
-
test('concurrencyFixtureLeads stays quiet on a race test that builds its own state', () => {
|
|
232
|
-
const clean = `--- a/src/lib/y.integration.test.ts
|
|
233
|
-
+++ b/src/lib/y.integration.test.ts
|
|
234
|
-
@@ -1,0 +1,8 @@
|
|
235
|
-
+ it("races two first-seal callers", async () => {
|
|
236
|
-
+ await db.plan.deleteMany({ where: { runId } })
|
|
237
|
-
+ const [a, b] = await Promise.allSettled([
|
|
238
|
-
+ authorize(runId, binding, { db: clientA }),
|
|
239
|
-
+ authorize(runId, binding, { db: clientB }),
|
|
240
|
-
+ ])
|
|
241
|
-
`
|
|
242
|
-
assert.equal(concurrencyFixtureLeads(parseAddedLines(clean)).length, 0,
|
|
243
|
-
'a race test that starts from absent state is the correct shape, not a lead')
|
|
244
|
-
})
|
|
245
|
-
|
|
246
|
-
test('concurrencyFixtureLeads ignores non-concurrency tests and non-test files', () => {
|
|
247
|
-
const ordinary = `--- a/src/lib/z.test.ts
|
|
248
|
-
+++ b/src/lib/z.test.ts
|
|
249
|
-
@@ -1,0 +1,4 @@
|
|
250
|
-
+ it("returns the sealed plan", async () => {
|
|
251
|
-
+ const row = await db.plan.findFirstOrThrow({ where: { runId } })
|
|
252
|
-
+ const [x, y] = await Promise.all([load(row.id), load(row.id)])
|
|
253
|
-
`
|
|
254
|
-
assert.equal(concurrencyFixtureLeads(parseAddedLines(ordinary)).length, 0,
|
|
255
|
-
'Promise.all in a test that never claims to race is ordinary parallelism')
|
|
256
|
-
const src = RACE_FALSE_GREEN.replace(/x\.integration\.test\.ts/g, 'x.ts')
|
|
257
|
-
assert.equal(concurrencyFixtureLeads(parseAddedLines(src)).length, 0, 'production files carry no test fixture')
|
|
258
|
-
})
|
|
259
|
-
|
|
260
|
-
// Review catch (vera932 on #364): tracking only concurrency titles left the test
|
|
261
|
-
// scope open for the rest of the file, so the NEXT ordinary test's fixture was
|
|
262
|
-
// attributed to a race test that had already ended. The negative case above only
|
|
263
|
-
// passed because nothing preceded it — order is the whole bug.
|
|
264
|
-
const RACE_THEN_ORDINARY = `--- a/src/lib/x.integration.test.ts
|
|
265
|
-
+++ b/src/lib/x.integration.test.ts
|
|
266
|
-
@@ -1,0 +1,12 @@
|
|
267
|
-
+ it("races two first-seal callers", async () => {
|
|
268
|
-
+ await db.plan.deleteMany({ where: { runId } })
|
|
269
|
-
+ const [a, b] = await Promise.allSettled([
|
|
270
|
-
+ authorize(runId, binding, { db: clientA }),
|
|
271
|
-
+ authorize(runId, binding, { db: clientB }),
|
|
272
|
-
+ ])
|
|
273
|
-
+ })
|
|
274
|
-
+ it("loads the sealed plan", async () => {
|
|
275
|
-
+ const row = await db.plan.findFirstOrThrow({ where: { runId } })
|
|
276
|
-
+ const [x, y] = await Promise.all([load(row.id), load(row.id)])
|
|
277
|
-
+ })
|
|
278
|
-
`
|
|
279
|
-
|
|
280
|
-
test('concurrencyFixtureLeads closes scope at the next test title, whatever it is named', () => {
|
|
281
|
-
assert.equal(concurrencyFixtureLeads(parseAddedLines(RACE_THEN_ORDINARY)).length, 0,
|
|
282
|
-
'an ordinary test following a correct race test must not inherit its scope')
|
|
283
|
-
})
|
|
284
|
-
|
|
285
|
-
test('concurrencyFixtureLeads still fires when the offending test follows another test', () => {
|
|
286
|
-
const after = `--- a/src/lib/x.integration.test.ts
|
|
287
|
-
+++ b/src/lib/x.integration.test.ts
|
|
288
|
-
@@ -1,0 +1,10 @@
|
|
289
|
-
+ it("seeds the fixture", async () => {
|
|
290
|
-
+ await db.plan.create({ data: { runId } })
|
|
291
|
-
+ })
|
|
292
|
-
+ it("races deterministically", async () => {
|
|
293
|
-
+ const source = await db.run.findFirstOrThrow({ where: { runId } })
|
|
294
|
-
+ const firstId = source.plan!.id
|
|
295
|
-
+ const [l, r] = await Promise.all([authorize(runId), authorize(runId)])
|
|
296
|
-
+ })
|
|
297
|
-
`
|
|
298
|
-
const leads = concurrencyFixtureLeads(parseAddedLines(after))
|
|
299
|
-
assert.equal(leads.length, 1, 'scope reset must not suppress a real hit later in the file')
|
|
300
|
-
assert.match(leads[0].snippet, /findFirstOrThrow/)
|
|
301
|
-
})
|
|
302
|
-
|
|
303
|
-
// --- CLI arg parsing: reject the exact --base/--head producer mistake (#416) ---
|
|
304
|
-
|
|
305
|
-
test('parseCliArgs accepts a bare invocation (piped stdin) and --diff <file>', () => {
|
|
306
|
-
assert.deepEqual(parseCliArgs([]), { diffPath: null })
|
|
307
|
-
assert.deepEqual(parseCliArgs(['--diff', 'changes.patch']), { diffPath: 'changes.patch' })
|
|
308
|
-
})
|
|
309
|
-
|
|
310
|
-
test('parseCliArgs rejects the --base/--head invocation that scanned empty stdin', () => {
|
|
311
|
-
// The whole point of #416: --base/--head must HARD-FAIL, not fall through to stdin.
|
|
312
|
-
assert.throws(() => parseCliArgs(['--base', 'a', '--head', 'b']), (e) =>
|
|
313
|
-
e instanceof ScanDiffCliError && e.code === USAGE_EXIT && /unknown flag/i.test(e.message) && /--base/.test(e.message))
|
|
314
|
-
})
|
|
315
|
-
|
|
316
|
-
test('parseCliArgs rejects any other unknown flag and a bare positional arg', () => {
|
|
317
|
-
assert.throws(() => parseCliArgs(['--nope']), (e) => e instanceof ScanDiffCliError && e.code === USAGE_EXIT && /--nope/.test(e.message))
|
|
318
|
-
assert.throws(() => parseCliArgs(['stray.patch']), (e) => e instanceof ScanDiffCliError && e.code === USAGE_EXIT)
|
|
319
|
-
})
|
|
320
|
-
|
|
321
|
-
test('parseCliArgs errors when --diff has no path', () => {
|
|
322
|
-
assert.throws(() => parseCliArgs(['--diff']), (e) => e instanceof ScanDiffCliError && e.code === USAGE_EXIT && /--diff/.test(e.message))
|
|
323
|
-
})
|
|
324
|
-
|
|
325
|
-
// --- CLI end-to-end (real subprocess): exit codes + stderr, the fail-open closed ---
|
|
326
|
-
|
|
327
|
-
// scan-diff detects a first-party-seam mock call. Writing that call literally in
|
|
328
|
-
// a fixture would put the detected pattern on an ADDED line of THIS file — which
|
|
329
|
-
// trips scan-diff's own findings channel over this PR's diff and makes the
|
|
330
|
-
// reviewer-parity proof non-empty for the detector's own test. Assemble the mock
|
|
331
|
-
// line from fragments so no single added source line matches the detector, while
|
|
332
|
-
// the runtime string is the exact diff line under test.
|
|
333
|
-
const mockLine = (target) => `+vi.` + `mock('${target}')`
|
|
334
|
-
const REAL_OVER_MOCK_DIFF = [
|
|
335
|
-
'--- a/src/__tests__/route.test.ts',
|
|
336
|
-
'+++ b/src/__tests__/route.test.ts',
|
|
337
|
-
'@@ -1,1 +1,2 @@',
|
|
338
|
-
' import x',
|
|
339
|
-
mockLine('@/lib/org-allowlist-client'),
|
|
340
|
-
'',
|
|
341
|
-
].join('\n')
|
|
342
|
-
|
|
343
|
-
const REAL_CLEAN_DIFF = `--- a/src/app.ts
|
|
344
|
-
+++ b/src/app.ts
|
|
345
|
-
@@ -1,0 +1,2 @@
|
|
346
|
-
+export function added() { return 1 }
|
|
347
|
-
+export const k = 2
|
|
348
|
-
`
|
|
349
|
-
|
|
350
|
-
test('CLI: empty stdin fails LOUD (non-zero + stderr), no clean-looking JSON', () => {
|
|
351
|
-
const r = runCli([], '')
|
|
352
|
-
assert.notEqual(r.status, 0, 'must NOT exit 0 on zero-byte input (the fail-open #416 closes)')
|
|
353
|
-
assert.equal(r.status, NO_INPUT_EXIT)
|
|
354
|
-
assert.equal(r.stdout.trim(), '', 'no vacuous {findings:[],leads:[]} on empty input')
|
|
355
|
-
assert.match(r.stderr, /no diff/i)
|
|
356
|
-
assert.match(r.stderr, /git diff .*scan-diff/, 'error points at the correct piped invocation')
|
|
357
|
-
})
|
|
358
|
-
|
|
359
|
-
test('CLI: whitespace-only stdin also fails loud (not a real diff)', () => {
|
|
360
|
-
const r = runCli([], ' \n\t\n')
|
|
361
|
-
assert.equal(r.status, NO_INPUT_EXIT)
|
|
362
|
-
assert.equal(r.stdout.trim(), '')
|
|
363
|
-
assert.match(r.stderr, /no diff/i)
|
|
364
|
-
})
|
|
365
|
-
|
|
366
|
-
test('CLI: --diff <empty-file> fails loud, same as empty stdin', () => {
|
|
367
|
-
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'scan-diff-'))
|
|
368
|
-
const empty = path.join(dir, 'empty.patch')
|
|
369
|
-
fs.writeFileSync(empty, '')
|
|
370
|
-
try {
|
|
371
|
-
const r = runCli(['--diff', empty], '')
|
|
372
|
-
assert.equal(r.status, NO_INPUT_EXIT)
|
|
373
|
-
assert.equal(r.stdout.trim(), '')
|
|
374
|
-
assert.match(r.stderr, /no diff|empty/i)
|
|
375
|
-
} finally {
|
|
376
|
-
fs.rmSync(dir, { recursive: true, force: true })
|
|
377
|
-
}
|
|
378
|
-
})
|
|
379
|
-
|
|
380
|
-
test('CLI: unknown flag (--base X --head Y) exits 2 + "unknown flag", not empty-stdin scan', () => {
|
|
381
|
-
const r = runCli(['--base', 'aaa', '--head', 'bbb'], '')
|
|
382
|
-
assert.equal(r.status, USAGE_EXIT)
|
|
383
|
-
assert.equal(r.stdout.trim(), '', 'the producer mistake no longer yields a false-clean proof')
|
|
384
|
-
assert.match(r.stderr, /unknown flag/i)
|
|
385
|
-
assert.match(r.stderr, /--base/)
|
|
386
|
-
})
|
|
387
|
-
|
|
388
|
-
test('CLI: a real diff with a planted over-mock still detected — guard did not break scanning', () => {
|
|
389
|
-
const r = runCli([], REAL_OVER_MOCK_DIFF)
|
|
390
|
-
assert.equal(r.status, 0)
|
|
391
|
-
const out = JSON.parse(r.stdout)
|
|
392
|
-
assert.equal(out.findings.length, 1, 'the real over-mock finding is still emitted')
|
|
393
|
-
assert.equal(out.findings[0].type, 'over-mock-internal-seam')
|
|
394
|
-
})
|
|
395
|
-
|
|
396
|
-
test('CLI: a real diff with no findings exits 0 with empty findings (legit clean result)', () => {
|
|
397
|
-
const r = runCli([], REAL_CLEAN_DIFF)
|
|
398
|
-
assert.equal(r.status, 0, 'input with content but zero findings is the legitimate clean result, not "no input"')
|
|
399
|
-
const out = JSON.parse(r.stdout)
|
|
400
|
-
assert.deepEqual(out.findings, [])
|
|
401
|
-
})
|
|
402
|
-
|
|
403
|
-
test('CLI: --diff <file> with a real diff works (the non-piped legit path)', () => {
|
|
404
|
-
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'scan-diff-'))
|
|
405
|
-
const p = path.join(dir, 'real.patch')
|
|
406
|
-
fs.writeFileSync(p, REAL_OVER_MOCK_DIFF)
|
|
407
|
-
try {
|
|
408
|
-
const r = runCli(['--diff', p], '')
|
|
409
|
-
assert.equal(r.status, 0)
|
|
410
|
-
assert.equal(JSON.parse(r.stdout).findings.length, 1)
|
|
411
|
-
} finally {
|
|
412
|
-
fs.rmSync(dir, { recursive: true, force: true })
|
|
413
|
-
}
|
|
414
|
-
})
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
// Drift check for the VENDORED over-mock classifier in scan-diff.mjs.
|
|
2
|
-
//
|
|
3
|
-
// scan-diff.mjs carries a self-contained copy of ensure-coverage's
|
|
4
|
-
// `mock-internal-seam` classifier so better-review enforces no-over-mock with zero
|
|
5
|
-
// dependency on ensure-coverage being installed. ensure-coverage stays CANONICAL
|
|
6
|
-
// ("if the two diverge, ensure-coverage wins and this copy is stale → resync").
|
|
7
|
-
// That resync rule used to be prose; this test mechanizes it: it runs the full
|
|
8
|
-
// classification decision tree through BOTH copies and fails on any divergence
|
|
9
|
-
// in verdict or reason, plus compares the default topology field-for-field.
|
|
10
|
-
//
|
|
11
|
-
// When the canonical module is absent (better-review installed standalone), the
|
|
12
|
-
// whole suite skips — there is nothing to be out of sync with.
|
|
13
|
-
|
|
14
|
-
import { test } from 'node:test'
|
|
15
|
-
import assert from 'node:assert/strict'
|
|
16
|
-
import fs from 'node:fs'
|
|
17
|
-
import path from 'node:path'
|
|
18
|
-
import { fileURLToPath } from 'node:url'
|
|
19
|
-
|
|
20
|
-
const here = path.dirname(fileURLToPath(import.meta.url))
|
|
21
|
-
const CANONICAL = path.resolve(here, '../../ensure-coverage/scripts/mock-policy-config.mjs')
|
|
22
|
-
const standalone = !fs.existsSync(CANONICAL)
|
|
23
|
-
|
|
24
|
-
const vendored = await import('./scan-diff.mjs')
|
|
25
|
-
const canonical = standalone ? null : await import(CANONICAL)
|
|
26
|
-
|
|
27
|
-
// One topology per declared-config shape we support; classifyMockTarget takes
|
|
28
|
-
// the same plain object in both copies (canonical compiles externals lazily).
|
|
29
|
-
const TOPOLOGIES = {
|
|
30
|
-
defaults: undefined, // both sides fall back to their own DEFAULT_*
|
|
31
|
-
declared: {
|
|
32
|
-
firstPartyAliasPrefixes: ['@/', '~/', '#app/'],
|
|
33
|
-
firstPartyPackages: ['@acme/', 'our-lib'],
|
|
34
|
-
externals: ['^@acme/generated-', '^virtual:'],
|
|
35
|
-
testUtilHints: ['__mocks__', 'fixtures', 'test-utils'],
|
|
36
|
-
},
|
|
37
|
-
badExternalRegex: {
|
|
38
|
-
firstPartyAliasPrefixes: ['@/'],
|
|
39
|
-
firstPartyPackages: [],
|
|
40
|
-
externals: ['[unclosed'], // must be ignored identically, never thrown
|
|
41
|
-
testUtilHints: ['mocks'],
|
|
42
|
-
},
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Targets covering every branch of the precedence chain:
|
|
46
|
-
// declared externals > stdlib > relative(util vs source) > alias > first-party
|
|
47
|
-
// package (exact / subpath / trailing-slash config) > bare package > no target.
|
|
48
|
-
const TARGETS = [
|
|
49
|
-
null, '', 'node:fs', 'node:path',
|
|
50
|
-
'./db', '../services/payment', './__mocks__/db', '../fixtures/user', './test-utils/render',
|
|
51
|
-
'../mocks/server', './helpers/seed', '@/lib/db', '@/server/auth', '~/utils/format',
|
|
52
|
-
'#app/db', '@acme/core', '@acme/core/client', '@acme/generated-client', 'our-lib',
|
|
53
|
-
'our-lib/deep', 'our-library', 'virtual:module', 'react', 'msw', '@tanstack/react-query',
|
|
54
|
-
'lodash/merge',
|
|
55
|
-
]
|
|
56
|
-
|
|
57
|
-
test('vendored classifier tracks ensure-coverage (canonical)', { skip: standalone && 'ensure-coverage not installed — standalone better-review, nothing to sync against' }, async (t) => {
|
|
58
|
-
await t.test('default topology fields match DEFAULT_MOCK_POLICY', () => {
|
|
59
|
-
const want = canonical.DEFAULT_MOCK_POLICY
|
|
60
|
-
const got = vendored.DEFAULT_MOCK_TOPOLOGY
|
|
61
|
-
for (const field of ['firstPartyAliasPrefixes', 'firstPartyPackages', 'externals', 'testUtilHints']) {
|
|
62
|
-
assert.deepEqual(got[field], want[field], `DEFAULT_MOCK_TOPOLOGY.${field} drifted from canonical DEFAULT_MOCK_POLICY.${field}`)
|
|
63
|
-
}
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
await t.test('extractMockTarget agrees on a line corpus', () => {
|
|
67
|
-
const LINES = [
|
|
68
|
-
`vi.${'mock'}('@/lib/db')`,
|
|
69
|
-
`jest.${'mock'}("../services/payment", () => ({}))`,
|
|
70
|
-
`vitest.${'mock'}(\`~/utils/format\`)`,
|
|
71
|
-
`vi.${'doMock'}('./db')`,
|
|
72
|
-
`jest.${'doMock'}('react')`,
|
|
73
|
-
`vi . mock ( '@acme/core' )`,
|
|
74
|
-
`vi.mock(someVariable)`, // non-literal → null
|
|
75
|
-
`expect(page).toBeTruthy()`,
|
|
76
|
-
'', null,
|
|
77
|
-
]
|
|
78
|
-
for (const line of LINES) {
|
|
79
|
-
assert.equal(vendored.extractMockTarget(line), canonical.extractMockTarget(line), `extractMockTarget diverged on: ${JSON.stringify(line)}`)
|
|
80
|
-
}
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
await t.test('classifyMockTarget agrees across the full precedence tree', () => {
|
|
84
|
-
for (const [name, topo] of Object.entries(TOPOLOGIES)) {
|
|
85
|
-
for (const target of TARGETS) {
|
|
86
|
-
const got = topo === undefined ? vendored.classifyMockTarget(target) : vendored.classifyMockTarget(target, topo)
|
|
87
|
-
const want = topo === undefined ? canonical.classifyMockTarget(target) : canonical.classifyMockTarget(target, topo)
|
|
88
|
-
assert.deepEqual(
|
|
89
|
-
{ internal: got.internal, reason: got.reason },
|
|
90
|
-
{ internal: want.internal, reason: want.reason },
|
|
91
|
-
`classifyMockTarget diverged on target=${JSON.stringify(target)} topology=${name}`
|
|
92
|
-
)
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
})
|
|
96
|
-
})
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
// Tests for persist-conventions: the real CLI against real files in a temp repo.
|
|
2
|
-
import { test } from 'node:test'
|
|
3
|
-
import assert from 'node:assert/strict'
|
|
4
|
-
import { spawnSync } from 'node:child_process'
|
|
5
|
-
import { lstatSync, mkdtempSync, readFileSync, readlinkSync, rmSync, writeFileSync } from 'node:fs'
|
|
6
|
-
import { tmpdir } from 'node:os'
|
|
7
|
-
import { join } from 'node:path'
|
|
8
|
-
import { fileURLToPath } from 'node:url'
|
|
9
|
-
|
|
10
|
-
import { markers } from './persist-conventions-core.mjs'
|
|
11
|
-
|
|
12
|
-
const CLI = fileURLToPath(new URL('./persist-conventions.mjs', import.meta.url))
|
|
13
|
-
const [START, END] = markers('tracker')
|
|
14
|
-
|
|
15
|
-
function withRepo(fn, remote) {
|
|
16
|
-
const dir = mkdtempSync(join(tmpdir(), 'contributing-'))
|
|
17
|
-
try {
|
|
18
|
-
if (remote) {
|
|
19
|
-
spawnSync('git', ['init', '-q', dir])
|
|
20
|
-
spawnSync('git', ['-C', dir, 'remote', 'add', 'origin', remote])
|
|
21
|
-
}
|
|
22
|
-
return fn(dir, (name) => readFileSync(join(dir, name), 'utf8'))
|
|
23
|
-
} finally {
|
|
24
|
-
rmSync(dir, { recursive: true, force: true })
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function run(dir, ...args) {
|
|
29
|
-
const res = spawnSync(process.execPath, [CLI, '--repo-root', dir, ...args], { encoding: 'utf8' })
|
|
30
|
-
return { code: res.status, out: res.stdout }
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
test('--write creates CONTRIBUTING.md with the section, the repo from origin, and the Linear projects', () => {
|
|
34
|
-
withRepo((dir, read) => {
|
|
35
|
-
const { code } = run(dir, '--linear-project', 'Checkout Risk', '--write')
|
|
36
|
-
assert.equal(code, 0)
|
|
37
|
-
const text = read('CONTRIBUTING.md')
|
|
38
|
-
assert.ok(text.startsWith('# Contributing\n'))
|
|
39
|
-
assert.ok(text.includes(START) && text.includes(END))
|
|
40
|
-
assert.match(text, /GitHub issues on `acme\/widgets`/)
|
|
41
|
-
assert.match(text, /Linear \(projects: Checkout Risk\)/)
|
|
42
|
-
assert.match(text, /gh issue list --repo acme\/widgets/)
|
|
43
|
-
assert.doesNotMatch(text, /\{\{/)
|
|
44
|
-
}, 'git@github.com:acme/widgets.git')
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
test('an https remote and an explicit --github-repo both name the repo', () => {
|
|
48
|
-
withRepo((dir, read) => {
|
|
49
|
-
assert.equal(run(dir, '--write').code, 0)
|
|
50
|
-
assert.match(read('CONTRIBUTING.md'), /GitHub issues on `acme\/widgets`\. Product work is tracked in Linear\./)
|
|
51
|
-
}, 'https://github.com/acme/widgets')
|
|
52
|
-
withRepo((dir, read) => {
|
|
53
|
-
assert.equal(run(dir, '--github-repo', 'acme/other', '--write').code, 0)
|
|
54
|
-
assert.match(read('CONTRIBUTING.md'), /`acme\/other`/)
|
|
55
|
-
})
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
test('without a GitHub remote or --github-repo it refuses with exit 2 and writes nothing', () => {
|
|
59
|
-
withRepo((dir) => {
|
|
60
|
-
const { code, out } = run(dir, '--write')
|
|
61
|
-
assert.equal(code, 2)
|
|
62
|
-
assert.match(out, /--github-repo/)
|
|
63
|
-
assert.throws(() => readFileSync(join(dir, 'CONTRIBUTING.md')))
|
|
64
|
-
})
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
test('--if-missing appends to an existing CONTRIBUTING.md once, then leaves the section alone', () => {
|
|
68
|
-
withRepo((dir, read) => {
|
|
69
|
-
writeFileSync(join(dir, 'CONTRIBUTING.md'), '# Contributing\n\nRun the tests.\n')
|
|
70
|
-
assert.equal(run(dir, '--if-missing', '--write').code, 0)
|
|
71
|
-
const first = read('CONTRIBUTING.md')
|
|
72
|
-
assert.ok(first.startsWith('# Contributing\n\nRun the tests.\n'))
|
|
73
|
-
assert.ok(first.includes(START))
|
|
74
|
-
|
|
75
|
-
const edited = first.replace('acme/widgets`. Product', 'acme/widgets`. Hand-edited. Product')
|
|
76
|
-
writeFileSync(join(dir, 'CONTRIBUTING.md'), edited)
|
|
77
|
-
const { code, out } = run(dir, '--if-missing', '--linear-project', 'Other', '--write')
|
|
78
|
-
assert.equal(code, 0)
|
|
79
|
-
assert.match(out, /already present/)
|
|
80
|
-
assert.equal(read('CONTRIBUTING.md'), edited)
|
|
81
|
-
}, 'git@github.com:acme/widgets.git')
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
test('without --write it previews and writes nothing', () => {
|
|
85
|
-
withRepo((dir) => {
|
|
86
|
-
const { code, out } = run(dir)
|
|
87
|
-
assert.equal(code, 0)
|
|
88
|
-
assert.match(out, /CONTRIBUTING\.md: would create/)
|
|
89
|
-
assert.throws(() => readFileSync(join(dir, 'CONTRIBUTING.md')))
|
|
90
|
-
}, 'git@github.com:acme/widgets.git')
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
test('the first run indexes CONTRIBUTING.md from AGENTS.md and links CLAUDE.md, once', () => {
|
|
94
|
-
withRepo((dir, read) => {
|
|
95
|
-
assert.equal(run(dir, '--if-missing', '--write').code, 0)
|
|
96
|
-
assert.match(read('AGENTS.md'), /\[CONTRIBUTING\.md\]\(CONTRIBUTING\.md\).*issue tracking/)
|
|
97
|
-
assert.ok(lstatSync(join(dir, 'CLAUDE.md')).isSymbolicLink())
|
|
98
|
-
assert.equal(readlinkSync(join(dir, 'CLAUDE.md')), 'AGENTS.md')
|
|
99
|
-
}, 'git@github.com:acme/widgets.git')
|
|
100
|
-
withRepo((dir, read) => {
|
|
101
|
-
writeFileSync(join(dir, 'AGENTS.md'), '# Agents\n\nSee CONTRIBUTING.md for everything.\n')
|
|
102
|
-
assert.equal(run(dir, '--if-missing', '--write').code, 0)
|
|
103
|
-
assert.equal(read('AGENTS.md'), '# Agents\n\nSee CONTRIBUTING.md for everything.\n')
|
|
104
|
-
}, 'git@github.com:acme/widgets.git')
|
|
105
|
-
})
|
|
106
|
-
|
|
107
|
-
test('--if-missing restores a missing AGENTS.md index even when the section is already present', () => {
|
|
108
|
-
withRepo((dir, read) => {
|
|
109
|
-
assert.equal(run(dir, '--write').code, 0)
|
|
110
|
-
const section = read('CONTRIBUTING.md')
|
|
111
|
-
writeFileSync(join(dir, 'AGENTS.md'), '# Agents\n')
|
|
112
|
-
const { code, out } = run(dir, '--if-missing', '--write')
|
|
113
|
-
assert.equal(code, 0)
|
|
114
|
-
assert.match(out, /already present; 1 index file\(s\) written/)
|
|
115
|
-
assert.match(read('AGENTS.md'), /^# Agents\n\n## Contributing\n/)
|
|
116
|
-
assert.equal(read('CONTRIBUTING.md'), section)
|
|
117
|
-
}, 'git@github.com:acme/widgets.git')
|
|
118
|
-
})
|