@1aboveio/skills 1.0.8 → 1.0.10
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,346 +0,0 @@
|
|
|
1
|
-
import { test } from 'node:test'
|
|
2
|
-
import assert from 'node:assert/strict'
|
|
3
|
-
import { execFileSync, spawnSync } from 'node:child_process'
|
|
4
|
-
import { existsSync, mkdirSync, mkdtempSync, readFileSync, writeFileSync } from 'node:fs'
|
|
5
|
-
import { tmpdir } from 'node:os'
|
|
6
|
-
import { dirname, join } from 'node:path'
|
|
7
|
-
import { fileURLToPath } from 'node:url'
|
|
8
|
-
|
|
9
|
-
import { addEntry, changedRanges, emptyDropbox, fingerprint, fromReview, groupEntries, inUnitDiff, issueFor, markEntry, pendingRuns, planPost, post, sanitize } from './dropbox.mjs'
|
|
10
|
-
|
|
11
|
-
const script = join(dirname(fileURLToPath(import.meta.url)), 'dropbox.mjs')
|
|
12
|
-
const tmp = () => mkdtempSync(join(tmpdir(), 'dropbox-'))
|
|
13
|
-
|
|
14
|
-
// A repository on `main` with a.ts (10 lines) and a unit branch that edits line 3 and adds b.ts.
|
|
15
|
-
function unitRepo() {
|
|
16
|
-
const dir = tmp()
|
|
17
|
-
const git = (...args) => execFileSync('git', ['-C', dir, ...args], { encoding: 'utf8' })
|
|
18
|
-
git('init', '-q', '-b', 'main')
|
|
19
|
-
git('config', 'user.email', 't@example.com')
|
|
20
|
-
git('config', 'user.name', 't')
|
|
21
|
-
writeFileSync(join(dir, 'a.ts'), Array.from({ length: 10 }, (_, i) => `line ${i + 1}`).join('\n') + '\n')
|
|
22
|
-
git('add', '.')
|
|
23
|
-
git('commit', '-qm', 'base')
|
|
24
|
-
git('checkout', '-qb', 'unit')
|
|
25
|
-
const lines = readFileSync(join(dir, 'a.ts'), 'utf8').split('\n')
|
|
26
|
-
lines[2] = 'changed 3'
|
|
27
|
-
writeFileSync(join(dir, 'a.ts'), lines.join('\n'))
|
|
28
|
-
writeFileSync(join(dir, 'b.ts'), 'new\n')
|
|
29
|
-
git('add', '.')
|
|
30
|
-
git('commit', '-qm', 'unit')
|
|
31
|
-
return dir
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const entry = (over = {}) => ({ unit: '12', source: 'review', kind: 'docs', file: 'docs/a.md', line: 4, summary: 'typo in heading', ...over })
|
|
35
|
-
|
|
36
|
-
test('hunk ranges come from the new side; a pure deletion adds none', () => {
|
|
37
|
-
assert.deepEqual(changedRanges('@@ -3 +3 @@\n@@ -7,0 +8,2 @@\n@@ -12,3 +13,0 @@\n'), [[3, 3], [8, 9]])
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
test('the unit diff covers changed lines and added files, not untouched lines', () => {
|
|
41
|
-
const dir = unitRepo()
|
|
42
|
-
assert.equal(inUnitDiff({ dir, base: 'main', file: 'a.ts', line: 3 }), true)
|
|
43
|
-
assert.equal(inUnitDiff({ dir, base: 'main', file: 'a.ts', line: 7 }), false)
|
|
44
|
-
assert.equal(inUnitDiff({ dir, base: 'main', file: 'a.ts' }), true, 'without a line, any change to the file counts')
|
|
45
|
-
assert.equal(inUnitDiff({ dir, base: 'main', file: 'b.ts', line: 1 }), true)
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
test('uncommitted work in the workspace counts as the unit\'s change', () => {
|
|
49
|
-
const dir = unitRepo()
|
|
50
|
-
const lines = readFileSync(join(dir, 'a.ts'), 'utf8').split('\n')
|
|
51
|
-
lines[8] = 'edited 9'
|
|
52
|
-
writeFileSync(join(dir, 'a.ts'), lines.join('\n'))
|
|
53
|
-
assert.equal(inUnitDiff({ dir, base: 'main', file: 'a.ts', line: 9 }), true)
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
test('check exits 0 and reports inDiff true inside the diff and false outside', () => {
|
|
57
|
-
const dir = unitRepo()
|
|
58
|
-
const inside = spawnSync('node', [script, 'check', '--git', dir, '--base', 'main', '--file', 'a.ts', '--line', '3'], { encoding: 'utf8' })
|
|
59
|
-
assert.equal(inside.status, 0, inside.stderr)
|
|
60
|
-
assert.deepEqual([JSON.parse(inside.stdout).inDiff, JSON.parse(inside.stdout).do], [true, 'fix it in this unit'])
|
|
61
|
-
const outside = spawnSync('node', [script, 'check', '--git', dir, '--base', 'main', '--file', 'a.ts', '--line', '7'], { encoding: 'utf8' })
|
|
62
|
-
assert.equal(outside.status, 0, outside.stderr)
|
|
63
|
-
assert.equal(JSON.parse(outside.stdout).inDiff, false)
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
test('an implementer entry inside its own diff is refused; a review entry there is kept and marked', () => {
|
|
67
|
-
const box = emptyDropbox()
|
|
68
|
-
assert.equal(addEntry(box, entry({ source: 'implementer' }), true).refused, true)
|
|
69
|
-
assert.equal(box.entries.length, 0)
|
|
70
|
-
const kept = addEntry(box, entry({ source: 'review', finding: 'R2', severity: 'P2' }), true)
|
|
71
|
-
assert.equal(kept.inDiff, true)
|
|
72
|
-
assert.equal(box.entries[0].inDiff, true)
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
test('a P3 is skipped: add refuses it and from-review never adds it', () => {
|
|
76
|
-
const box = emptyDropbox()
|
|
77
|
-
assert.throws(() => addEntry(box, entry({ source: 'review', finding: 'R3', severity: 'P3' }), false), /a P3 is skipped/)
|
|
78
|
-
const review = { unit: '12', findings: [{ id: 'R3', severity: 'P3', summary: 'tidy comment', file: 'a.ts', line: 2 }] }
|
|
79
|
-
assert.deepEqual(fromReview(box, review), [])
|
|
80
|
-
assert.equal(review.findings[0].dropboxId, undefined)
|
|
81
|
-
assert.equal(box.entries.length, 0)
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
test('the CLI add refuses an implementer entry inside the diff with `refused` (exit 0) and writes nothing', () => {
|
|
85
|
-
const dir = unitRepo()
|
|
86
|
-
const path = join(tmp(), 'dropbox.json')
|
|
87
|
-
const args = ['add', '--dropbox', path, '--unit', '7', '--source', 'implementer', '--kind', 'bug', '--file', 'a.ts', '--line', '3', '--summary', 's', '--git', dir, '--base', 'main']
|
|
88
|
-
const refused = spawnSync('node', [script, ...args], { encoding: 'utf8' })
|
|
89
|
-
assert.equal(refused.status, 0, refused.stderr)
|
|
90
|
-
assert.ok(JSON.parse(refused.stdout).refused)
|
|
91
|
-
assert.equal(existsSync(path), false)
|
|
92
|
-
const ok = spawnSync('node', [script, ...args.map((a) => (a === '3' ? '7' : a))], { encoding: 'utf8' })
|
|
93
|
-
assert.equal(ok.status, 0, ok.stderr)
|
|
94
|
-
assert.equal(JSON.parse(readFileSync(path, 'utf8')).entries[0].inDiff, false)
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
test('a P0/P1, a missing location, or an unknown kind is rejected', () => {
|
|
98
|
-
const box = emptyDropbox()
|
|
99
|
-
assert.throws(() => addEntry(box, entry({ severity: 'P1' })), /never goes in the dropbox/)
|
|
100
|
-
assert.throws(() => addEntry(box, entry({ file: undefined })), /--file is required/)
|
|
101
|
-
assert.throws(() => addEntry(box, entry({ kind: 'nit' })), /--kind must be/)
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
test('the same problem is one entry, and a review finding id is one entry across rounds', () => {
|
|
105
|
-
const box = emptyDropbox()
|
|
106
|
-
const first = addEntry(box, entry())
|
|
107
|
-
assert.deepEqual(addEntry(box, entry({ summary: ' Typo in heading ' })), { id: first.id, duplicate: true, inDiff: null })
|
|
108
|
-
const r = addEntry(box, entry({ finding: 'R3', file: 'x.ts', summary: 'one wording' }))
|
|
109
|
-
assert.equal(addEntry(box, entry({ finding: 'R3', file: 'x.ts', line: 9, summary: 'reworded next round' })).id, r.id)
|
|
110
|
-
assert.equal(box.entries.length, 2)
|
|
111
|
-
})
|
|
112
|
-
|
|
113
|
-
test('local absolute paths never reach an issue, and summaries are capped', () => {
|
|
114
|
-
assert.equal(sanitize('see /Users/someone/run/a.log and /tmp/x'), 'see <local path> and <local path>')
|
|
115
|
-
assert.equal(sanitize('see src/tmp/cache.ts and app/home/page.tsx'), 'see src/tmp/cache.ts and app/home/page.tsx', 'repository paths stay')
|
|
116
|
-
assert.ok(sanitize('x'.repeat(400)).length <= 280)
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
test('entries group by parent PR and by file and kind across PRs', () => {
|
|
120
|
-
const box = emptyDropbox()
|
|
121
|
-
addEntry(box, entry({ file: 'a.ts', kind: 'test', summary: '1', pr: 'https://github.com/o/r/pull/5' }))
|
|
122
|
-
addEntry(box, entry({ file: 'b.ts', kind: 'docs', summary: '2', pr: 'https://github.com/o/r/pull/5' }))
|
|
123
|
-
addEntry(box, entry({ file: 'a.ts', kind: 'test', summary: '3', pr: 'https://github.com/o/r/pull/6' }))
|
|
124
|
-
addEntry(box, entry({ file: 'z.ts', kind: 'ci', summary: '4', pr: 'https://github.com/o/r/pull/7' }))
|
|
125
|
-
const groups = groupEntries(box.entries)
|
|
126
|
-
assert.deepEqual(groups.map((g) => [g.id, g.entries.map((e) => e.summary)]), [['G1', ['1', '2', '3']], ['G4', ['4']]])
|
|
127
|
-
})
|
|
128
|
-
|
|
129
|
-
test('the issue lists each location, its parent PR, what it blocks, and a fingerprint marker', () => {
|
|
130
|
-
const box = emptyDropbox()
|
|
131
|
-
addEntry(box, entry({ file: 'a.ts', line: 3, kind: 'test', summary: 'weak assert', pr: 'https://github.com/o/r/pull/5', finding: 'R2', blocks: ['https://github.com/o/r/issues/44'] }))
|
|
132
|
-
addEntry(box, entry({ file: 'a.ts', line: 9, kind: 'test', summary: 'no failure case', pr: 'https://github.com/o/r/pull/5' }))
|
|
133
|
-
const { title, body } = issueFor(groupEntries(box.entries)[0])
|
|
134
|
-
assert.equal(title, '2 test follow-ups in a.ts')
|
|
135
|
-
assert.match(body, /`a\.ts:3` \(test\) weak assert \(from #5, finding R2\)/)
|
|
136
|
-
assert.match(body, /Blocks o\/r#44/)
|
|
137
|
-
for (const e of box.entries) assert.ok(body.includes(`dropbox-fp:${e.fingerprint}`))
|
|
138
|
-
})
|
|
139
|
-
|
|
140
|
-
test('plan drops an entry whose file is gone and never posts decisions', () => {
|
|
141
|
-
const dir = unitRepo()
|
|
142
|
-
const box = emptyDropbox()
|
|
143
|
-
addEntry(box, entry({ file: 'a.ts', summary: 'kept' }))
|
|
144
|
-
addEntry(box, entry({ file: 'gone.ts', summary: 'gone' }))
|
|
145
|
-
addEntry(box, { unit: '12', source: 'implementer', decision: true, summary: 'should refunds round half-even?' })
|
|
146
|
-
const plan = planPost(box, { dir, ref: 'unit' })
|
|
147
|
-
assert.deepEqual(plan.dropped, ['D2'])
|
|
148
|
-
assert.deepEqual(plan.groups.flatMap((g) => g.entries.map((e) => e.summary)), ['kept'])
|
|
149
|
-
assert.deepEqual(plan.decisions.map((d) => d.summary), ['should refunds round half-even?'])
|
|
150
|
-
})
|
|
151
|
-
|
|
152
|
-
function fakeGh(openIssues = []) {
|
|
153
|
-
const calls = []
|
|
154
|
-
return {
|
|
155
|
-
calls,
|
|
156
|
-
gh: (args, input) => {
|
|
157
|
-
calls.push({ args, input })
|
|
158
|
-
if (args[1] === 'list') return JSON.stringify(openIssues)
|
|
159
|
-
if (args[1] === 'create') return `https://github.com/o/r/issues/${100 + calls.length}\n`
|
|
160
|
-
throw new Error(`unexpected gh ${args.join(' ')}`)
|
|
161
|
-
},
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
test('post files one labeled issue per group and records each entry\'s URL', () => {
|
|
166
|
-
const box = emptyDropbox()
|
|
167
|
-
addEntry(box, entry({ file: 'a.ts', summary: '1', pr: 'https://github.com/o/r/pull/5' }))
|
|
168
|
-
addEntry(box, entry({ file: 'b.ts', summary: '2', pr: 'https://github.com/o/r/pull/5' }))
|
|
169
|
-
addEntry(box, entry({ file: 'c.ts', kind: 'ci', summary: '3' }))
|
|
170
|
-
const { gh, calls } = fakeGh()
|
|
171
|
-
const result = post(box, { repo: 'o/r' }, { gh })
|
|
172
|
-
const creates = calls.filter((c) => c.args[1] === 'create')
|
|
173
|
-
assert.equal(creates.length, 2)
|
|
174
|
-
for (const c of creates) assert.deepEqual(c.args.slice(c.args.indexOf('--label'), c.args.indexOf('--label') + 2), ['--label', 'ready-for-agent'])
|
|
175
|
-
assert.equal(result.posted.length, 2)
|
|
176
|
-
assert.ok(box.entries.every((e) => e.status === 'posted' && e.postedUrl.startsWith('https://github.com/o/r/issues/')))
|
|
177
|
-
})
|
|
178
|
-
|
|
179
|
-
test('post skips an entry an open issue already carries, and --only limits the groups', () => {
|
|
180
|
-
const box = emptyDropbox()
|
|
181
|
-
addEntry(box, entry({ file: 'a.ts', summary: '1' }))
|
|
182
|
-
addEntry(box, entry({ file: 'c.ts', kind: 'ci', summary: '3' }))
|
|
183
|
-
addEntry(box, entry({ file: 'd.ts', kind: 'perf', summary: '4' }))
|
|
184
|
-
const filed = { url: 'https://github.com/o/r/issues/9', body: `<!-- dropbox-fp:${box.entries[0].fingerprint} -->` }
|
|
185
|
-
const { gh, calls } = fakeGh([filed])
|
|
186
|
-
const result = post(box, { repo: 'o/r', only: ['G2'] }, { gh })
|
|
187
|
-
assert.deepEqual(result.alreadyFiled, ['D1'])
|
|
188
|
-
assert.equal(box.entries[0].postedUrl, filed.url)
|
|
189
|
-
assert.equal(calls.filter((c) => c.args[1] === 'create').length, 1)
|
|
190
|
-
assert.equal(box.entries[1].status, 'posted')
|
|
191
|
-
assert.equal(box.entries[2].status, 'pending', 'G3 was not asked for')
|
|
192
|
-
})
|
|
193
|
-
|
|
194
|
-
test('from-review adds each undisposed P2 and writes its dropboxId back', () => {
|
|
195
|
-
const box = emptyDropbox()
|
|
196
|
-
const review = { unit: '12', findings: [
|
|
197
|
-
{ id: 'R1', severity: 'P1', summary: 'blocker', file: 'a.ts', line: 1 },
|
|
198
|
-
{ id: 'R2', severity: 'P2', pass: 'tests', summary: 'weak test', file: 'a.ts', line: 3 },
|
|
199
|
-
{ id: 'R3', severity: 'P2', summary: 'filed already', file: 'a.ts', followUpUrl: 'https://github.com/o/r/issues/1' },
|
|
200
|
-
] }
|
|
201
|
-
const added = fromReview(box, review, { pr: 'https://github.com/o/r/pull/5', inDiffOf: (file, line) => line === 3 })
|
|
202
|
-
assert.deepEqual(added, ['D1'])
|
|
203
|
-
assert.equal(review.findings[1].dropboxId, 'D1')
|
|
204
|
-
assert.equal(review.findings[0].dropboxId, undefined)
|
|
205
|
-
assert.deepEqual([box.entries[0].kind, box.entries[0].inDiff, box.entries[0].finding], ['test', true, 'R2'])
|
|
206
|
-
assert.deepEqual(fromReview(box, review), [], 'a second run adds nothing')
|
|
207
|
-
})
|
|
208
|
-
|
|
209
|
-
test('pending lists runs with unposted entries and exits 0 either way', () => {
|
|
210
|
-
const runs = tmp()
|
|
211
|
-
mkdirSync(join(runs, 'r1'))
|
|
212
|
-
mkdirSync(join(runs, 'r2'))
|
|
213
|
-
const box = emptyDropbox()
|
|
214
|
-
addEntry(box, entry())
|
|
215
|
-
writeFileSync(join(runs, 'r1', 'dropbox.json'), JSON.stringify(box))
|
|
216
|
-
writeFileSync(join(runs, 'r2', 'dropbox.json'), JSON.stringify(emptyDropbox()))
|
|
217
|
-
assert.deepEqual(pendingRuns(runs).map((r) => [r.run, r.pending]), [['r1', 1]])
|
|
218
|
-
const cli = spawnSync('node', [script, 'pending', '--runs', runs], { encoding: 'utf8' })
|
|
219
|
-
assert.equal(cli.status, 0, cli.stderr)
|
|
220
|
-
assert.equal(JSON.parse(cli.stdout).runs.length, 1)
|
|
221
|
-
const none = spawnSync('node', [script, 'pending', '--runs', join(runs, 'none')], { encoding: 'utf8' })
|
|
222
|
-
assert.equal(none.status, 0, none.stderr)
|
|
223
|
-
assert.deepEqual(JSON.parse(none.stdout).runs, [])
|
|
224
|
-
})
|
|
225
|
-
|
|
226
|
-
test('a problem found again at a shifted line is the same problem', () => {
|
|
227
|
-
assert.equal(fingerprint(entry({ line: 4 })), fingerprint(entry({ line: 40 })))
|
|
228
|
-
})
|
|
229
|
-
|
|
230
|
-
test('the diff can be taken at a given head, whatever the checkout', () => {
|
|
231
|
-
const dir = unitRepo()
|
|
232
|
-
const head = execFileSync('git', ['-C', dir, 'rev-parse', 'unit'], { encoding: 'utf8' }).trim()
|
|
233
|
-
execFileSync('git', ['-C', dir, 'checkout', '-q', 'main'])
|
|
234
|
-
assert.equal(inUnitDiff({ dir, base: 'main', file: 'a.ts', line: 3 }), false, 'the checkout is main')
|
|
235
|
-
assert.equal(inUnitDiff({ dir, base: 'main', head, file: 'a.ts', line: 3 }), true)
|
|
236
|
-
})
|
|
237
|
-
|
|
238
|
-
test('from-review takes the unit diff at the review head', () => {
|
|
239
|
-
const box = emptyDropbox()
|
|
240
|
-
const seen = []
|
|
241
|
-
fromReview(box, { unit: '1', head: 'abc', findings: [{ id: 'R2', severity: 'P2', summary: 's', file: 'a.ts', line: 3 }] }, { inDiffOf: (...args) => { seen.push(args); return false } })
|
|
242
|
-
assert.deepEqual(seen, [['a.ts', 3, 'abc']])
|
|
243
|
-
})
|
|
244
|
-
|
|
245
|
-
test('an urgent entry is filed alone with --ids, and is not filed again at the end', () => {
|
|
246
|
-
const box = emptyDropbox()
|
|
247
|
-
addEntry(box, entry({ file: 'a.ts', summary: 'nit', pr: 'https://github.com/o/r/pull/5' }))
|
|
248
|
-
addEntry(box, entry({ file: 'b.ts', kind: 'bug', summary: 'cross-org role change', pr: 'https://github.com/o/r/pull/5' }))
|
|
249
|
-
const first = fakeGh()
|
|
250
|
-
post(box, { repo: 'o/r', ids: ['D2'] }, { gh: first.gh })
|
|
251
|
-
const created = first.calls.filter((c) => c.args[1] === 'create')
|
|
252
|
-
assert.equal(created.length, 1)
|
|
253
|
-
assert.match(created[0].input, /cross-org role change/)
|
|
254
|
-
assert.doesNotMatch(created[0].input, /nit/, 'its PR group-mate stays for the end')
|
|
255
|
-
const end = fakeGh()
|
|
256
|
-
post(box, { repo: 'o/r' }, { gh: end.gh })
|
|
257
|
-
const bodies = end.calls.filter((c) => c.args[1] === 'create').map((c) => c.input)
|
|
258
|
-
assert.equal(bodies.length, 1)
|
|
259
|
-
assert.doesNotMatch(bodies[0], /cross-org/)
|
|
260
|
-
assert.throws(() => post(box, { repo: 'o/r', ids: ['D9'] }, { gh: end.gh }), /no entry D9/)
|
|
261
|
-
})
|
|
262
|
-
|
|
263
|
-
test('post finds already-filed entries among open issues carrying its label', () => {
|
|
264
|
-
const { gh, calls } = fakeGh()
|
|
265
|
-
post(emptyDropbox(), { repo: 'o/r', label: 'agent-ready' }, { gh })
|
|
266
|
-
const list = calls.find((c) => c.args[1] === 'list').args
|
|
267
|
-
assert.deepEqual(list.slice(list.indexOf('--label'), list.indexOf('--label') + 2), ['--label', 'agent-ready'])
|
|
268
|
-
})
|
|
269
|
-
|
|
270
|
-
test('mark drops an entry or turns it into a decision; a posted entry cannot change', () => {
|
|
271
|
-
const box = emptyDropbox()
|
|
272
|
-
addEntry(box, entry({ file: 'a.ts', summary: '1' }))
|
|
273
|
-
addEntry(box, entry({ file: 'b.ts', summary: '2' }))
|
|
274
|
-
markEntry(box, { id: 'D1', drop: 'file removed by epic #40' })
|
|
275
|
-
markEntry(box, { id: 'D2', decision: true })
|
|
276
|
-
assert.deepEqual(box.entries.map((e) => [e.status, e.route]), [['dropped', 'dropbox'], ['pending', 'decision']])
|
|
277
|
-
assert.deepEqual(planPost(box).groups, [])
|
|
278
|
-
assert.throws(() => markEntry(box, { id: 'D1' }), /--drop <reason> or --decision/)
|
|
279
|
-
box.entries[1].status = 'posted'
|
|
280
|
-
assert.throws(() => markEntry(box, { id: 'D2', drop: 'x' }), /already posted/)
|
|
281
|
-
})
|
|
282
|
-
|
|
283
|
-
test('pending ignores decisions, which their own run reports', () => {
|
|
284
|
-
const runs = tmp()
|
|
285
|
-
mkdirSync(join(runs, 'r1'))
|
|
286
|
-
const box = emptyDropbox()
|
|
287
|
-
addEntry(box, { unit: '1', source: 'implementer', decision: true, summary: 'which period?' })
|
|
288
|
-
writeFileSync(join(runs, 'r1', 'dropbox.json'), JSON.stringify(box))
|
|
289
|
-
assert.deepEqual(pendingRuns(runs), [])
|
|
290
|
-
})
|
|
291
|
-
|
|
292
|
-
test('a file the unit deleted is inside its diff; a path that never existed is an error', () => {
|
|
293
|
-
const dir = unitRepo()
|
|
294
|
-
execFileSync('git', ['-C', dir, 'rm', '-q', 'a.ts'])
|
|
295
|
-
execFileSync('git', ['-C', dir, 'commit', '-qm', 'drop a'])
|
|
296
|
-
assert.equal(inUnitDiff({ dir, base: 'main', file: 'a.ts', line: 7 }), true)
|
|
297
|
-
assert.throws(() => inUnitDiff({ dir, base: 'main', file: 'nope.ts', line: 1 }), /exists neither/)
|
|
298
|
-
assert.throws(() => inUnitDiff({ dir, base: 'main', file: 'b.ts', line: Number('abc') }), /positive integer/)
|
|
299
|
-
})
|
|
300
|
-
|
|
301
|
-
test('check and add refuse bad input instead of calling it outside the diff', () => {
|
|
302
|
-
const dir = unitRepo()
|
|
303
|
-
const run = (...args) => spawnSync('node', [script, ...args], { encoding: 'utf8' })
|
|
304
|
-
assert.equal(run('check', '--git', dir, '--base', 'main', '--file', 'a.ts', '--line', 'abc').status, 2)
|
|
305
|
-
assert.equal(run('check', '--git', dir, '--base', 'main', '--file', 'a.ts:5').status, 2)
|
|
306
|
-
assert.equal(run('check', '--git', dir, '--base', 'main', '--file', 'a.tss', '--line', '1').status, 2)
|
|
307
|
-
const path = join(tmp(), 'dropbox.json')
|
|
308
|
-
const noDiff = run('add', '--dropbox', path, '--unit', '7', '--source', 'implementer', '--kind', 'bug', '--file', 'a.ts', '--line', '3', '--summary', 's')
|
|
309
|
-
assert.equal(noDiff.status, 2, 'an implementer entry needs the diff check')
|
|
310
|
-
assert.match(noDiff.stderr, /--git, --base/)
|
|
311
|
-
assert.throws(() => addEntry(emptyDropbox(), entry({ file: join(dir, 'a.ts') })), /repository-relative/)
|
|
312
|
-
})
|
|
313
|
-
|
|
314
|
-
test('a bare issue number in --blocks becomes a link', () => {
|
|
315
|
-
const box = emptyDropbox()
|
|
316
|
-
addEntry(box, entry({ blocks: ['55'] }))
|
|
317
|
-
assert.match(issueFor(groupEntries(box.entries)[0]).body, /Blocks #55/)
|
|
318
|
-
})
|
|
319
|
-
|
|
320
|
-
test('post drops entries whose file is gone at --ref and posts --only groups by plan id', () => {
|
|
321
|
-
const dir = unitRepo()
|
|
322
|
-
const path = join(tmp(), 'dropbox.json')
|
|
323
|
-
const box = emptyDropbox()
|
|
324
|
-
addEntry(box, entry({ file: 'gone.ts', summary: 'gone' }))
|
|
325
|
-
addEntry(box, entry({ file: 'a.ts', kind: 'test', summary: 'kept 1' }))
|
|
326
|
-
addEntry(box, entry({ file: 'b.ts', kind: 'ci', summary: 'kept 2' }))
|
|
327
|
-
writeFileSync(path, JSON.stringify(box))
|
|
328
|
-
const bin = tmp()
|
|
329
|
-
const log = join(bin, 'log')
|
|
330
|
-
writeFileSync(join(bin, 'gh'), `#!/bin/sh\necho "$@" >> ${log}\ncat >> ${log}\ncase "$2" in list) echo '[]';; create) echo https://github.com/o/r/issues/101;; esac\n`, { mode: 0o755 })
|
|
331
|
-
const env = { ...process.env, PATH: `${bin}:${process.env.PATH}` }
|
|
332
|
-
const planned = JSON.parse(spawnSync('node', [script, 'plan', '--dropbox', path, '--git', dir, '--ref', 'unit'], { encoding: 'utf8' }).stdout)
|
|
333
|
-
assert.deepEqual(planned.groups.map((g) => g.id), ['G2', 'G3'])
|
|
334
|
-
const result = spawnSync('node', [script, 'post', '--dropbox', path, '--repo', 'o/r', '--git', dir, '--ref', 'unit', '--only', 'G3'], { encoding: 'utf8', env })
|
|
335
|
-
assert.equal(result.status, 0, result.stderr)
|
|
336
|
-
const after = JSON.parse(readFileSync(path, 'utf8')).entries
|
|
337
|
-
assert.deepEqual(after.map((e) => e.status), ['dropped', 'pending', 'posted'])
|
|
338
|
-
assert.doesNotMatch(readFileSync(log, 'utf8'), /gone/)
|
|
339
|
-
assert.equal(spawnSync('node', [script, 'plan', '--dropbox', path, '--ref', 'unit'], { encoding: 'utf8' }).status, 2, '--ref needs --git')
|
|
340
|
-
})
|
|
341
|
-
|
|
342
|
-
test('post --ids names a decision as one', () => {
|
|
343
|
-
const box = emptyDropbox()
|
|
344
|
-
addEntry(box, { unit: '1', source: 'implementer', decision: true, summary: 'which period?' })
|
|
345
|
-
assert.throws(() => post(box, { repo: 'o/r', ids: ['D1'] }, { gh: fakeGh().gh }), /D1 is a decision/)
|
|
346
|
-
})
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { test } from 'node:test'
|
|
2
|
-
import assert from 'node:assert/strict'
|
|
3
|
-
import { execFileSync, 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
|
-
|
|
9
|
-
import { pathsIn, evidencePaths, decideReuse, parseCliArgs, FindingsReuseError } from './findings-reuse.mjs'
|
|
10
|
-
|
|
11
|
-
const SCRIPT = path.join(path.dirname(fileURLToPath(import.meta.url)), 'findings-reuse.mjs')
|
|
12
|
-
|
|
13
|
-
// A repo at `base`; then `src/charge/layout.ts` changes on main.
|
|
14
|
-
function fixture(t) {
|
|
15
|
-
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'findings-reuse-'))
|
|
16
|
-
t.after(() => fs.rmSync(dir, { recursive: true, force: true }))
|
|
17
|
-
const git = (...a) => execFileSync('git', a, { cwd: dir, encoding: 'utf8' }).trim()
|
|
18
|
-
const write = (f, text) => { fs.mkdirSync(path.dirname(path.join(dir, f)), { recursive: true }); fs.writeFileSync(path.join(dir, f), text) }
|
|
19
|
-
git('init', '-q', '-b', 'main'); git('config', 'user.email', 't@e.com'); git('config', 'user.name', 'T')
|
|
20
|
-
write('src/charge/layout.ts', 'a\n'); write('src/history/list.ts', 'b\n'); write('db/schema.sql', 'c\n')
|
|
21
|
-
git('add', '-A'); git('commit', '-qm', 'base')
|
|
22
|
-
const base = git('rev-parse', 'HEAD')
|
|
23
|
-
write('src/charge/layout.ts', 'a2\n'); git('commit', '-qam', 'earlier units merged')
|
|
24
|
-
const prior = {
|
|
25
|
-
version: 2, base,
|
|
26
|
-
units: {
|
|
27
|
-
charge: { slice: { prerequisites: [{ what: 'layout store', status: 'present', evidence: 'src/charge/layout.ts:3' }] }, readFirst: [] },
|
|
28
|
-
history: { slice: { prerequisites: [{ what: 'list view', status: 'present', evidence: 'src/history/list.ts:1' }] }, readFirst: ['src/history/'] },
|
|
29
|
-
migrate: { slice: { prerequisites: [{ what: 'cursor column', status: 'missing', separate: false, evidence: 'not in db/schema.sql' }] } },
|
|
30
|
-
},
|
|
31
|
-
}
|
|
32
|
-
const priorFile = path.join(dir, '..', `${path.basename(dir)}-prior.json`)
|
|
33
|
-
fs.writeFileSync(priorFile, JSON.stringify(prior))
|
|
34
|
-
t.after(() => fs.rmSync(priorFile, { force: true }))
|
|
35
|
-
const run = (...args) => spawnSync('node', [SCRIPT, '--prior', priorFile, ...args], { cwd: dir, encoding: 'utf8' })
|
|
36
|
-
return { dir, git, write, base, prior, run }
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
test('pathsIn finds repository paths in evidence prose, without line numbers', () => {
|
|
40
|
-
assert.deepEqual(pathsIn('packages/core/page.ts:12'), ['packages/core/page.ts'])
|
|
41
|
-
assert.deepEqual(pathsIn('not in db/schema.sql'), ['db/schema.sql'])
|
|
42
|
-
assert.deepEqual(pathsIn('no authority in backend/services'), ['backend/services'])
|
|
43
|
-
assert.deepEqual(pathsIn('e2e job green'), [])
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
test('evidencePaths reads prerequisites, readFirst (or legacy start), and the suite', () => {
|
|
47
|
-
const entry = {
|
|
48
|
-
slice: { prerequisites: [{ evidence: 'a/b.ts:1' }] },
|
|
49
|
-
start: ['c/d.ts'],
|
|
50
|
-
proof: { suite: { evidence: 'scripts/journey.py; green in CI', check: 'gh workflow run android.yml', local: 'bash scripts/charge-journey.sh' } },
|
|
51
|
-
}
|
|
52
|
-
// A renamed local journey script invalidates the reuse, like any other evidence path.
|
|
53
|
-
assert.deepEqual(evidencePaths(entry), ['a/b.ts', 'android.yml', 'c/d.ts', 'scripts/charge-journey.sh', 'scripts/journey.py'])
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
test('a unit is reused unless a file its evidence names changed; directories match files under them', () => {
|
|
57
|
-
const prior = { units: { a: { slice: { prerequisites: [{ evidence: 'src/x.ts:1' }] } }, b: { readFirst: ['src/y/'] } } }
|
|
58
|
-
assert.deepEqual(decideReuse(prior, ['a', 'b', 'c'], ['src/y/z.ts'], true), { reuse: ['a'], recheck: [{ id: 'b', changed: ['src/y/z.ts'] }], new: ['c'] })
|
|
59
|
-
assert.deepEqual(decideReuse(prior, ['a'], [], false).recheck.map((r) => r.id), ['a'], 'unknown base rechecks everything')
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
test('CLI: after an earlier unit changed the charge layout, only the charge unit is rechecked', (t) => {
|
|
63
|
-
const f = fixture(t)
|
|
64
|
-
const out = JSON.parse(f.run().stdout)
|
|
65
|
-
assert.deepEqual(out.reuse, ['history', 'migrate'])
|
|
66
|
-
assert.deepEqual(out.recheck, [{ id: 'charge', changed: ['src/charge/layout.ts'] }])
|
|
67
|
-
assert.deepEqual(out.new, [])
|
|
68
|
-
const only = JSON.parse(f.run('--units', 'history,newunit').stdout)
|
|
69
|
-
assert.deepEqual(only.reuse, ['history'])
|
|
70
|
-
assert.deepEqual(only.new, ['newunit'])
|
|
71
|
-
})
|
|
72
|
-
|
|
73
|
-
test('CLI: a missing prerequisite whose evidence file changed is rechecked (it may now exist)', (t) => {
|
|
74
|
-
const f = fixture(t)
|
|
75
|
-
f.write('db/schema.sql', 'c\ncursor\n'); f.git('commit', '-qam', 'add cursor')
|
|
76
|
-
assert.deepEqual(JSON.parse(f.run().stdout).recheck.map((r) => r.id), ['charge', 'migrate'])
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
test('CLI: a prior base not in this repository rechecks every unit; bad input exits 2', (t) => {
|
|
80
|
-
const f = fixture(t)
|
|
81
|
-
const priorFile = path.join(f.dir, 'gone.json')
|
|
82
|
-
fs.writeFileSync(priorFile, JSON.stringify({ ...f.prior, base: '0'.repeat(40) }))
|
|
83
|
-
const out = JSON.parse(spawnSync('node', [SCRIPT, '--prior', priorFile], { cwd: f.dir, encoding: 'utf8' }).stdout)
|
|
84
|
-
assert.deepEqual(out.reuse, [])
|
|
85
|
-
assert.equal(out.recheck.length, 3)
|
|
86
|
-
assert.equal(spawnSync('node', [SCRIPT, '--prior', path.join(f.dir, 'missing.json')], { cwd: f.dir }).status, 2)
|
|
87
|
-
assert.throws(() => parseCliArgs([]), FindingsReuseError)
|
|
88
|
-
})
|