sillyspec 3.20.2 → 3.20.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/skills/sillyspec-archive/SKILL.md +21 -21
- package/.claude/skills/sillyspec-auto/SKILL.md +83 -83
- package/.claude/skills/sillyspec-brainstorm/SKILL.md +44 -44
- package/.claude/skills/sillyspec-commit/SKILL.md +106 -106
- package/.claude/skills/sillyspec-continue/SKILL.md +45 -45
- package/.claude/skills/sillyspec-doctor/SKILL.md +31 -31
- package/.claude/skills/sillyspec-execute/SKILL.md +30 -30
- package/.claude/skills/sillyspec-explore/SKILL.md +109 -109
- package/.claude/skills/sillyspec-knowledge/SKILL.md +269 -269
- package/.claude/skills/sillyspec-plan/SKILL.md +21 -21
- package/.claude/skills/sillyspec-propose/SKILL.md +21 -21
- package/.claude/skills/sillyspec-quick/SKILL.md +21 -21
- package/.claude/skills/sillyspec-resume/SKILL.md +68 -68
- package/.claude/skills/sillyspec-scan/SKILL.md +21 -21
- package/.claude/skills/sillyspec-state/SKILL.md +54 -54
- package/.claude/skills/sillyspec-status/SKILL.md +21 -21
- package/.claude/skills/sillyspec-verify/SKILL.md +21 -21
- package/.claude/skills/sillyspec-workspace/SKILL.md +157 -157
- package/.husky/pre-push +13 -13
- package/CLAUDE.md +18 -18
- package/README.md +198 -188
- package/SKILL.md +90 -91
- package/bin/sillyspec.js +2 -2
- package/docs/brainstorm-plan-contract.md +64 -64
- package/docs/plan-execute-contract.md +123 -123
- package/docs/platform-scan-protocol.md +298 -298
- package/docs/revision-mode.md +115 -115
- package/docs/sillyspec/file-lifecycle/known-implementation-gaps.md +99 -99
- package/docs/sillyspec/file-lifecycle/platform-workflows-sync.md +218 -218
- package/docs/sillyspec/file-lifecycle/stage-artifacts.md +167 -167
- package/docs/sillyspec/file-lifecycle/storage-and-state.md +148 -148
- package/docs/sillyspec/file-lifecycle/worktree-and-guard.md +211 -193
- package/docs/sillyspec/file-lifecycle.md +125 -125
- package/docs/workflow-contract-regression.md +106 -106
- package/docs/worktree-isolation.md +252 -252
- package/package.json +40 -40
- package/packages/dashboard/dist/assets/index-Bq_Z2hne.js +7446 -7446
- package/packages/dashboard/dist/assets/index-O2W5RV4z.css +1 -1
- package/packages/dashboard/dist/index.html +16 -16
- package/packages/dashboard/index.html +15 -15
- package/packages/dashboard/package-lock.json +2384 -2384
- package/packages/dashboard/package.json +25 -25
- package/packages/dashboard/server/executor.js +86 -86
- package/packages/dashboard/server/index.js +588 -588
- package/packages/dashboard/server/parser.js +526 -526
- package/packages/dashboard/server/watcher.js +344 -344
- package/packages/dashboard/src/App.vue +558 -558
- package/packages/dashboard/src/components/ActionBar.vue +93 -93
- package/packages/dashboard/src/components/CommandPalette.vue +96 -96
- package/packages/dashboard/src/components/DetailPanel.vue +137 -137
- package/packages/dashboard/src/components/LogStream.vue +65 -65
- package/packages/dashboard/src/components/PipelineStage.vue +95 -95
- package/packages/dashboard/src/components/PipelineView.vue +156 -156
- package/packages/dashboard/src/components/ProjectList.vue +210 -210
- package/packages/dashboard/src/components/StageBadge.vue +67 -67
- package/packages/dashboard/src/components/StepCard.vue +94 -94
- package/packages/dashboard/src/components/detail/DocsDetail.vue +48 -48
- package/packages/dashboard/src/components/detail/GitDetail.vue +61 -61
- package/packages/dashboard/src/components/detail/TechDetail.vue +43 -43
- package/packages/dashboard/src/composables/useDashboard.js +170 -170
- package/packages/dashboard/src/composables/useKeyboard.js +119 -119
- package/packages/dashboard/src/composables/useWebSocket.js +129 -129
- package/packages/dashboard/src/main.js +8 -8
- package/packages/dashboard/src/style.css +132 -132
- package/packages/dashboard/vite.config.js +18 -18
- package/src/brainstorm-postcheck.js +158 -158
- package/src/change-list.js +52 -52
- package/src/change-risk-profile.js +352 -352
- package/src/classify-change.js +73 -73
- package/src/constants.js +70 -70
- package/src/contract-matrix.js +278 -278
- package/src/db.js +201 -201
- package/src/endpoint-extractor.js +315 -315
- package/src/hooks/claude-pre-tool-use.cjs +125 -125
- package/src/hooks/worktree-guard.js +653 -653
- package/src/index.js +922 -900
- package/src/init.js +431 -431
- package/src/knowledge-match.js +130 -130
- package/src/migrate.js +117 -117
- package/src/modules.js +482 -482
- package/src/progress.js +1734 -1734
- package/src/run.js +3465 -3358
- package/src/scan-postcheck.js +387 -383
- package/src/setup.js +398 -398
- package/src/stage-contract.js +700 -700
- package/src/stages/archive.js +160 -160
- package/src/stages/brainstorm-auto.js +229 -229
- package/src/stages/brainstorm.js +645 -645
- package/src/stages/doctor.js +365 -365
- package/src/stages/execute.js +625 -625
- package/src/stages/explore.js +34 -34
- package/src/stages/index.js +29 -29
- package/src/stages/knowledge.js +498 -498
- package/src/stages/plan-postcheck.js +511 -513
- package/src/stages/plan.js +582 -582
- package/src/stages/propose.js +174 -174
- package/src/stages/quick.js +82 -82
- package/src/stages/scan.js +558 -558
- package/src/stages/status.js +65 -65
- package/src/stages/verify.js +322 -322
- package/src/sync.js +497 -497
- package/src/task-review.js +346 -346
- package/src/workflow.js +785 -785
- package/src/worktree-apply.js +549 -549
- package/src/worktree-deps.js +185 -0
- package/src/worktree.js +982 -932
- package/templates/workflows/archive-impact.yaml +79 -79
- package/templates/workflows/scan-docs.yaml +132 -132
- package/test/brainstorm-plan-contract.test.mjs +273 -273
- package/test/check-syntax.mjs +26 -26
- package/test/contract-artifacts.test.mjs +323 -323
- package/test/decision-supersede.test.mjs +277 -277
- package/test/knowledge-match.test.mjs +231 -231
- package/test/plan-execute-contract.test.mjs +330 -330
- package/test/plan-optimization.test.mjs +572 -572
- package/test/platform-artifacts.test.mjs +166 -166
- package/test/platform-failure-samples.test.mjs +199 -199
- package/test/platform-recovery-chain.test.mjs +167 -167
- package/test/platform-recovery.test.mjs +136 -136
- package/test/platform-scan-p0.test.mjs +168 -168
- package/test/revision-v1.test.mjs +1145 -1145
- package/test/run-scan-project-parse.test.mjs +200 -200
- package/test/run-tests.mjs +48 -48
- package/test/scan-knowledge.test.mjs +175 -175
- package/test/scan-paths.test.mjs +68 -68
- package/test/scan-postcheck.test.mjs +197 -197
- package/test/spec-dir.test.mjs +206 -206
- package/test/stage-contract.test.mjs +299 -299
- package/test/stage-definitions.test.mjs +39 -39
- package/test/wait-gates.test.mjs +496 -496
- package/test/worktree-deps-provision.test.mjs +148 -0
- package/test/worktree-guard.test.mjs +71 -71
- package/test/worktree-native-overlay.test.mjs +188 -188
|
@@ -1,179 +1,179 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 平台 scan 恢复链路测试
|
|
3
|
-
*
|
|
4
|
-
* 验证:
|
|
5
|
-
* 1. 首次带 --spec-dir 跑 scan,pointer 文件被创建
|
|
6
|
-
* 2. 后续 --done 不带参数能从 pointer 恢复平台参数
|
|
7
|
-
* 3. scan 完成后 pointer 状态标记为 scan_completed
|
|
8
|
-
* 4. pointer 异常残留能被检测
|
|
9
|
-
*
|
|
10
|
-
* 跑法: node test/platform-recovery-chain.test.mjs
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import { join, basename } from 'path'
|
|
14
|
-
import { existsSync, mkdirSync, rmSync, readFileSync, writeFileSync } from 'fs'
|
|
15
|
-
import { execSync } from 'child_process'
|
|
16
|
-
import { fileURLToPath } from 'url'
|
|
1
|
+
/**
|
|
2
|
+
* 平台 scan 恢复链路测试
|
|
3
|
+
*
|
|
4
|
+
* 验证:
|
|
5
|
+
* 1. 首次带 --spec-dir 跑 scan,pointer 文件被创建
|
|
6
|
+
* 2. 后续 --done 不带参数能从 pointer 恢复平台参数
|
|
7
|
+
* 3. scan 完成后 pointer 状态标记为 scan_completed
|
|
8
|
+
* 4. pointer 异常残留能被检测
|
|
9
|
+
*
|
|
10
|
+
* 跑法: node test/platform-recovery-chain.test.mjs
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { join, basename } from 'path'
|
|
14
|
+
import { existsSync, mkdirSync, rmSync, readFileSync, writeFileSync } from 'fs'
|
|
15
|
+
import { execSync } from 'child_process'
|
|
16
|
+
import { fileURLToPath } from 'url'
|
|
17
17
|
import { dirname } from 'path'
|
|
18
18
|
import { randomUUID } from 'crypto'
|
|
19
19
|
import { tmpdir } from 'os'
|
|
20
|
-
|
|
21
|
-
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
22
|
-
const binCLI = join(__dirname, '..', 'src', 'index.js')
|
|
23
|
-
const passed = []
|
|
24
|
-
const failed = []
|
|
25
|
-
|
|
26
|
-
function assert(label, condition, detail) {
|
|
27
|
-
if (condition) {
|
|
28
|
-
passed.push(label)
|
|
29
|
-
console.log(` ✅ PASS: ${label}`)
|
|
30
|
-
} else {
|
|
31
|
-
failed.push({ label, detail })
|
|
32
|
-
console.log(` ❌ FAIL: ${label}`)
|
|
33
|
-
if (detail) console.log(` ${detail}`)
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function cleanup(dir) {
|
|
38
|
-
try { rmSync(dir, { recursive: true, force: true }) } catch {}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function run(cmd, opts = {}) {
|
|
42
|
-
try {
|
|
43
|
-
return execSync(cmd, { encoding: 'utf8', timeout: 15_000, ...opts })
|
|
44
|
-
} catch (e) {
|
|
45
|
-
return e.stdout || e.message
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// ── 测试 1:pointer 文件创建和内容 ──
|
|
50
|
-
console.log('\n=== Test 1: pointer 文件创建 ===')
|
|
51
|
-
{
|
|
20
|
+
|
|
21
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
22
|
+
const binCLI = join(__dirname, '..', 'src', 'index.js')
|
|
23
|
+
const passed = []
|
|
24
|
+
const failed = []
|
|
25
|
+
|
|
26
|
+
function assert(label, condition, detail) {
|
|
27
|
+
if (condition) {
|
|
28
|
+
passed.push(label)
|
|
29
|
+
console.log(` ✅ PASS: ${label}`)
|
|
30
|
+
} else {
|
|
31
|
+
failed.push({ label, detail })
|
|
32
|
+
console.log(` ❌ FAIL: ${label}`)
|
|
33
|
+
if (detail) console.log(` ${detail}`)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function cleanup(dir) {
|
|
38
|
+
try { rmSync(dir, { recursive: true, force: true }) } catch {}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function run(cmd, opts = {}) {
|
|
42
|
+
try {
|
|
43
|
+
return execSync(cmd, { encoding: 'utf8', timeout: 15_000, ...opts })
|
|
44
|
+
} catch (e) {
|
|
45
|
+
return e.stdout || e.message
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ── 测试 1:pointer 文件创建和内容 ──
|
|
50
|
+
console.log('\n=== Test 1: pointer 文件创建 ===')
|
|
51
|
+
{
|
|
52
52
|
const tmpCwd = join(tmpdir(), `recovery-test-${randomUUID().slice(0, 8)}`)
|
|
53
53
|
const tmpSpec = join(tmpdir(), `recovery-test-spec-${randomUUID().slice(0, 8)}`)
|
|
54
54
|
const tmpRuntime = join(tmpdir(), `recovery-test-rt-${randomUUID().slice(0, 8)}`)
|
|
55
|
-
|
|
56
|
-
try {
|
|
57
|
-
mkdirSync(tmpCwd, { recursive: true })
|
|
58
|
-
writeFileSync(join(tmpCwd, 'package.json'), '{}')
|
|
59
|
-
|
|
60
|
-
// init 项目(使用外部 specDir)
|
|
61
|
-
const initOut = run(`node "${binCLI}" init "${tmpCwd}" --spec-root "${tmpSpec}"`)
|
|
62
|
-
assert('init 成功', !initOut.includes('❌'))
|
|
63
|
-
|
|
64
|
-
// run scan 会触发参数持久化
|
|
65
|
-
run(`node "${binCLI}" --dir "${tmpCwd}" run scan --spec-root "${tmpSpec}" 2>&1 || true`)
|
|
66
|
-
|
|
67
|
-
const pointerPath = join(tmpCwd, '.sillyspec-platform.json')
|
|
68
|
-
assert('pointer 文件存在', existsSync(pointerPath))
|
|
69
|
-
|
|
70
|
-
const pointer = JSON.parse(readFileSync(pointerPath, 'utf8'))
|
|
71
|
-
assert('pointer 有 specRoot', !!pointer.specRoot)
|
|
72
|
-
assert('pointer 有 savedAt', !!pointer.savedAt)
|
|
73
|
-
assert('pointer specRoot 指向外部', pointer.specRoot === tmpSpec)
|
|
74
|
-
|
|
75
|
-
// pointer 不应包含 status(初始创建时)
|
|
76
|
-
assert('初始 pointer 无 status 字段', !('status' in pointer))
|
|
77
|
-
} finally {
|
|
78
|
-
cleanup(tmpCwd)
|
|
79
|
-
cleanup(tmpSpec)
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// ── 测试 2:--done 不带参数能恢复 ──
|
|
84
|
-
console.log('\n=== Test 2: --done 恢复平台参数 ===')
|
|
85
|
-
{
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
mkdirSync(tmpCwd, { recursive: true })
|
|
58
|
+
writeFileSync(join(tmpCwd, 'package.json'), '{}')
|
|
59
|
+
|
|
60
|
+
// init 项目(使用外部 specDir)
|
|
61
|
+
const initOut = run(`node "${binCLI}" init "${tmpCwd}" --spec-root "${tmpSpec}"`)
|
|
62
|
+
assert('init 成功', !initOut.includes('❌'))
|
|
63
|
+
|
|
64
|
+
// run scan 会触发参数持久化
|
|
65
|
+
run(`node "${binCLI}" --dir "${tmpCwd}" run scan --spec-root "${tmpSpec}" 2>&1 || true`)
|
|
66
|
+
|
|
67
|
+
const pointerPath = join(tmpCwd, '.sillyspec-platform.json')
|
|
68
|
+
assert('pointer 文件存在', existsSync(pointerPath))
|
|
69
|
+
|
|
70
|
+
const pointer = JSON.parse(readFileSync(pointerPath, 'utf8'))
|
|
71
|
+
assert('pointer 有 specRoot', !!pointer.specRoot)
|
|
72
|
+
assert('pointer 有 savedAt', !!pointer.savedAt)
|
|
73
|
+
assert('pointer specRoot 指向外部', pointer.specRoot === tmpSpec)
|
|
74
|
+
|
|
75
|
+
// pointer 不应包含 status(初始创建时)
|
|
76
|
+
assert('初始 pointer 无 status 字段', !('status' in pointer))
|
|
77
|
+
} finally {
|
|
78
|
+
cleanup(tmpCwd)
|
|
79
|
+
cleanup(tmpSpec)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// ── 测试 2:--done 不带参数能恢复 ──
|
|
84
|
+
console.log('\n=== Test 2: --done 恢复平台参数 ===')
|
|
85
|
+
{
|
|
86
86
|
const tmpCwd = join(tmpdir(), `recovery-test2-${randomUUID().slice(0, 8)}`)
|
|
87
87
|
const tmpSpec = join(tmpdir(), `recovery-test2-spec-${randomUUID().slice(0, 8)}`)
|
|
88
88
|
const tmpRuntime = join(tmpdir(), `recovery-test2-rt-${randomUUID().slice(0, 8)}`)
|
|
89
|
-
const scanRunId = `scan-${Date.now()}`
|
|
90
|
-
|
|
91
|
-
try {
|
|
92
|
-
mkdirSync(tmpCwd, { recursive: true })
|
|
93
|
-
writeFileSync(join(tmpCwd, 'package.json'), '{}')
|
|
94
|
-
|
|
95
|
-
// init + 触发 run 写入 pointer
|
|
96
|
-
run(`node "${binCLI}" init "${tmpCwd}" --spec-root "${tmpSpec}"`)
|
|
97
|
-
run(`node "${binCLI}" --dir "${tmpCwd}" run scan --spec-root "${tmpSpec}" 2>&1 || true`)
|
|
98
|
-
|
|
99
|
-
// 手动模拟一个"scan 第一步 --done"(不带 --spec-root)
|
|
100
|
-
// 关键验证:--done 时能从 pointer 恢复参数
|
|
101
|
-
const doneOut = run(`node "${binCLI}" --dir "${tmpCwd}" run scan --done --input "test" --output "test output" 2>&1`, { cwd: tmpCwd })
|
|
102
|
-
|
|
103
|
-
// --done 应该能找到平台参数,不应该报"需要 --spec-root"
|
|
104
|
-
assert('--done 恢复成功', !doneOut.includes('需要 --spec-root') && !doneOut.includes('缺少 specRoot'))
|
|
105
|
-
} finally {
|
|
106
|
-
cleanup(tmpCwd)
|
|
107
|
-
cleanup(tmpSpec)
|
|
108
|
-
cleanup(tmpRuntime)
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// ── 测试 3:manifest 包含路径和 pointer 信息 ──
|
|
113
|
-
console.log('\n=== Test 3: manifest 路径字段 ===')
|
|
114
|
-
{
|
|
115
|
-
const { readFile } = await import('fs/promises')
|
|
116
|
-
const runSrc = await readFile(join(__dirname, '..', 'src', 'run.js'), 'utf8')
|
|
117
|
-
|
|
118
|
-
// manifest 初始化中包含三路径
|
|
119
|
-
assert('manifest 有 source_root: cwd', runSrc.includes('source_root: cwd'))
|
|
120
|
-
assert('manifest 有 spec_root', runSrc.includes('spec_root: platformOpts'))
|
|
121
|
-
assert('manifest 有 runtime_root', runSrc.includes('runtime_root: platformOpts'))
|
|
122
|
-
assert('manifest 有 platform_pointer_path', runSrc.includes('platform_pointer_path:'))
|
|
123
|
-
assert('manifest platform_pointer_status 使用枚举', runSrc.includes('POINTER_STATUS') || runSrc.includes('POINTER_STATUS.ACTIVE'))
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// ── 测试 4:异常 pointer 检测 ──
|
|
127
|
-
console.log('\n=== Test 4: 异常 pointer 残留检测 ===')
|
|
128
|
-
{
|
|
89
|
+
const scanRunId = `scan-${Date.now()}`
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
mkdirSync(tmpCwd, { recursive: true })
|
|
93
|
+
writeFileSync(join(tmpCwd, 'package.json'), '{}')
|
|
94
|
+
|
|
95
|
+
// init + 触发 run 写入 pointer
|
|
96
|
+
run(`node "${binCLI}" init "${tmpCwd}" --spec-root "${tmpSpec}"`)
|
|
97
|
+
run(`node "${binCLI}" --dir "${tmpCwd}" run scan --spec-root "${tmpSpec}" 2>&1 || true`)
|
|
98
|
+
|
|
99
|
+
// 手动模拟一个"scan 第一步 --done"(不带 --spec-root)
|
|
100
|
+
// 关键验证:--done 时能从 pointer 恢复参数
|
|
101
|
+
const doneOut = run(`node "${binCLI}" --dir "${tmpCwd}" run scan --done --input "test" --output "test output" 2>&1`, { cwd: tmpCwd })
|
|
102
|
+
|
|
103
|
+
// --done 应该能找到平台参数,不应该报"需要 --spec-root"
|
|
104
|
+
assert('--done 恢复成功', !doneOut.includes('需要 --spec-root') && !doneOut.includes('缺少 specRoot'))
|
|
105
|
+
} finally {
|
|
106
|
+
cleanup(tmpCwd)
|
|
107
|
+
cleanup(tmpSpec)
|
|
108
|
+
cleanup(tmpRuntime)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ── 测试 3:manifest 包含路径和 pointer 信息 ──
|
|
113
|
+
console.log('\n=== Test 3: manifest 路径字段 ===')
|
|
114
|
+
{
|
|
115
|
+
const { readFile } = await import('fs/promises')
|
|
116
|
+
const runSrc = await readFile(join(__dirname, '..', 'src', 'run.js'), 'utf8')
|
|
117
|
+
|
|
118
|
+
// manifest 初始化中包含三路径
|
|
119
|
+
assert('manifest 有 source_root: cwd', runSrc.includes('source_root: cwd'))
|
|
120
|
+
assert('manifest 有 spec_root', runSrc.includes('spec_root: platformOpts'))
|
|
121
|
+
assert('manifest 有 runtime_root', runSrc.includes('runtime_root: platformOpts'))
|
|
122
|
+
assert('manifest 有 platform_pointer_path', runSrc.includes('platform_pointer_path:'))
|
|
123
|
+
assert('manifest platform_pointer_status 使用枚举', runSrc.includes('POINTER_STATUS') || runSrc.includes('POINTER_STATUS.ACTIVE'))
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ── 测试 4:异常 pointer 检测 ──
|
|
127
|
+
console.log('\n=== Test 4: 异常 pointer 残留检测 ===')
|
|
128
|
+
{
|
|
129
129
|
const tmpCwd = join(tmpdir(), `recovery-test4-${randomUUID().slice(0, 8)}`)
|
|
130
130
|
const tmpSpec = join(tmpdir(), `recovery-test4-spec-${randomUUID().slice(0, 8)}`)
|
|
131
|
-
|
|
132
|
-
try {
|
|
133
|
-
mkdirSync(tmpCwd, { recursive: true })
|
|
134
|
-
writeFileSync(join(tmpCwd, 'package.json'), '{}')
|
|
135
|
-
|
|
136
|
-
// init + 触发 run 写入 pointer
|
|
137
|
-
run(`node "${binCLI}" init "${tmpCwd}" --spec-root "${tmpSpec}"`)
|
|
138
|
-
run(`node "${binCLI}" --dir "${tmpCwd}" run scan --spec-root "${tmpSpec}" 2>&1 || true`)
|
|
139
|
-
|
|
140
|
-
const pointerPath = join(tmpCwd, '.sillyspec-platform.json')
|
|
141
|
-
assert('pointer 文件存在', existsSync(pointerPath))
|
|
142
|
-
|
|
143
|
-
// 模拟损坏的 pointer(缺少 specRoot)
|
|
144
|
-
writeFileSync(pointerPath, JSON.stringify({ workspaceId: 'fake', savedAt: new Date().toISOString() }))
|
|
145
|
-
const badOut = run(`node "${binCLI}" --dir "${tmpCwd}" run scan 2>&1`)
|
|
146
|
-
assert('损坏 pointer 报错', badOut.includes('缺少 specRoot') || badOut.includes('❌'))
|
|
147
|
-
|
|
148
|
-
// 模拟有效 pointer(手动修复)
|
|
149
|
-
writeFileSync(pointerPath, JSON.stringify({
|
|
150
|
-
specRoot: tmpSpec,
|
|
131
|
+
|
|
132
|
+
try {
|
|
133
|
+
mkdirSync(tmpCwd, { recursive: true })
|
|
134
|
+
writeFileSync(join(tmpCwd, 'package.json'), '{}')
|
|
135
|
+
|
|
136
|
+
// init + 触发 run 写入 pointer
|
|
137
|
+
run(`node "${binCLI}" init "${tmpCwd}" --spec-root "${tmpSpec}"`)
|
|
138
|
+
run(`node "${binCLI}" --dir "${tmpCwd}" run scan --spec-root "${tmpSpec}" 2>&1 || true`)
|
|
139
|
+
|
|
140
|
+
const pointerPath = join(tmpCwd, '.sillyspec-platform.json')
|
|
141
|
+
assert('pointer 文件存在', existsSync(pointerPath))
|
|
142
|
+
|
|
143
|
+
// 模拟损坏的 pointer(缺少 specRoot)
|
|
144
|
+
writeFileSync(pointerPath, JSON.stringify({ workspaceId: 'fake', savedAt: new Date().toISOString() }))
|
|
145
|
+
const badOut = run(`node "${binCLI}" --dir "${tmpCwd}" run scan 2>&1`)
|
|
146
|
+
assert('损坏 pointer 报错', badOut.includes('缺少 specRoot') || badOut.includes('❌'))
|
|
147
|
+
|
|
148
|
+
// 模拟有效 pointer(手动修复)
|
|
149
|
+
writeFileSync(pointerPath, JSON.stringify({
|
|
150
|
+
specRoot: tmpSpec,
|
|
151
151
|
runtimeRoot: join(tmpdir(), 'fake-rt'),
|
|
152
|
-
workspaceId: 'test',
|
|
153
|
-
scanRunId: 'scan-test',
|
|
154
|
-
savedAt: new Date().toISOString(),
|
|
155
|
-
}))
|
|
156
|
-
// init 不应报错
|
|
157
|
-
const goodOut = run(`node "${binCLI}" --dir "${tmpCwd}" run scan --help 2>&1`)
|
|
158
|
-
assert('有效 pointer 不报错', !goodOut.includes('缺少 specRoot'))
|
|
159
|
-
} finally {
|
|
160
|
-
cleanup(tmpCwd)
|
|
161
|
-
cleanup(tmpSpec)
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// ── 结果 ──
|
|
166
|
-
console.log(`\n${'='.repeat(50)}`)
|
|
167
|
-
console.log(`✅ 通过: ${passed.length} ❌ 失败: ${failed.length}`)
|
|
168
|
-
console.log(`${'='.repeat(50)}`)
|
|
169
|
-
|
|
170
|
-
if (failed.length > 0) {
|
|
171
|
-
console.log('\n失败详情:')
|
|
172
|
-
for (const f of failed) {
|
|
173
|
-
console.log(` ❌ ${f.label}`)
|
|
174
|
-
if (f.detail) console.log(` ${f.detail}`)
|
|
175
|
-
}
|
|
176
|
-
throw new Error('platform-recovery-chain test failed')
|
|
177
|
-
} else {
|
|
178
|
-
console.log('\n🎉 平台恢复链路测试全部通过!')
|
|
179
|
-
}
|
|
152
|
+
workspaceId: 'test',
|
|
153
|
+
scanRunId: 'scan-test',
|
|
154
|
+
savedAt: new Date().toISOString(),
|
|
155
|
+
}))
|
|
156
|
+
// init 不应报错
|
|
157
|
+
const goodOut = run(`node "${binCLI}" --dir "${tmpCwd}" run scan --help 2>&1`)
|
|
158
|
+
assert('有效 pointer 不报错', !goodOut.includes('缺少 specRoot'))
|
|
159
|
+
} finally {
|
|
160
|
+
cleanup(tmpCwd)
|
|
161
|
+
cleanup(tmpSpec)
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// ── 结果 ──
|
|
166
|
+
console.log(`\n${'='.repeat(50)}`)
|
|
167
|
+
console.log(`✅ 通过: ${passed.length} ❌ 失败: ${failed.length}`)
|
|
168
|
+
console.log(`${'='.repeat(50)}`)
|
|
169
|
+
|
|
170
|
+
if (failed.length > 0) {
|
|
171
|
+
console.log('\n失败详情:')
|
|
172
|
+
for (const f of failed) {
|
|
173
|
+
console.log(` ❌ ${f.label}`)
|
|
174
|
+
if (f.detail) console.log(` ${f.detail}`)
|
|
175
|
+
}
|
|
176
|
+
throw new Error('platform-recovery-chain test failed')
|
|
177
|
+
} else {
|
|
178
|
+
console.log('\n🎉 平台恢复链路测试全部通过!')
|
|
179
|
+
}
|
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* platform-recovery.test.mjs — 平台模式参数恢复 + stage-contract 路径测试
|
|
3
|
-
*/
|
|
4
|
-
|
|
1
|
+
/**
|
|
2
|
+
* platform-recovery.test.mjs — 平台模式参数恢复 + stage-contract 路径测试
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
5
|
import { join, resolve, dirname, basename } from 'path'
|
|
6
6
|
import { existsSync, mkdirSync, writeFileSync, rmSync, readFileSync } from 'fs'
|
|
7
7
|
import { fileURLToPath, pathToFileURL } from 'url'
|
|
8
8
|
import { execSync } from 'child_process'
|
|
9
9
|
import { tmpdir } from 'os'
|
|
10
|
-
|
|
11
|
-
const __filename = fileURLToPath(import.meta.url)
|
|
12
|
-
const __dirname = dirname(__filename)
|
|
13
|
-
const root = resolve(__dirname, '..')
|
|
14
|
-
const binCLI = join(root, 'bin', 'sillyspec.js')
|
|
15
|
-
|
|
16
|
-
let passed = 0, failed = 0
|
|
17
|
-
|
|
18
|
-
function assert(cond, msg) {
|
|
19
|
-
if (cond) { console.log(` ✅ PASS: ${msg}`); passed++ }
|
|
20
|
-
else { console.log(` ❌ FAIL: ${msg}`); failed++ }
|
|
21
|
-
}
|
|
22
|
-
|
|
10
|
+
|
|
11
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
12
|
+
const __dirname = dirname(__filename)
|
|
13
|
+
const root = resolve(__dirname, '..')
|
|
14
|
+
const binCLI = join(root, 'bin', 'sillyspec.js')
|
|
15
|
+
|
|
16
|
+
let passed = 0, failed = 0
|
|
17
|
+
|
|
18
|
+
function assert(cond, msg) {
|
|
19
|
+
if (cond) { console.log(` ✅ PASS: ${msg}`); passed++ }
|
|
20
|
+
else { console.log(` ❌ FAIL: ${msg}`); failed++ }
|
|
21
|
+
}
|
|
22
|
+
|
|
23
23
|
const P = 'recover'
|
|
24
24
|
function setup(name) {
|
|
25
25
|
const d = join(tmpdir(), `${P}-${name}`)
|
|
@@ -39,129 +39,129 @@ function hasPathSegments(value, segments) {
|
|
|
39
39
|
}
|
|
40
40
|
return false
|
|
41
41
|
}
|
|
42
|
-
|
|
43
|
-
function run(cmd) {
|
|
44
|
-
return execSync(cmd, { encoding: 'utf8', timeout: 15000, stdio: ['pipe', 'pipe', 'pipe'] })
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const DOCS = ['ARCHITECTURE.md','CONVENTIONS.md','STRUCTURE.md','INTEGRATIONS.md','TESTING.md','CONCERNS.md','PROJECT.md']
|
|
48
|
-
function writeSpecDocs(dir) {
|
|
49
|
-
for (const d of DOCS) {
|
|
50
|
-
const p = join(dir, 'scan', d)
|
|
51
|
-
mkdirSync(dirname(p), { recursive: true })
|
|
52
|
-
writeFileSync(p, 'author: bot\ncreated_at: now\n# doc\n')
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
function writeLocalDocs(cwd) {
|
|
56
|
-
for (const d of DOCS) {
|
|
57
|
-
const p = join(cwd, '.sillyspec', 'docs', basename(cwd), 'scan', d)
|
|
58
|
-
mkdirSync(dirname(p), { recursive: true })
|
|
59
|
-
writeFileSync(p, 'author: bot\ncreated_at: now\n# doc\n')
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// ── Test 1: platform-scan.json 写入位置 ──
|
|
64
|
-
console.log('\n=== Test 1: platform-scan.json 写入位置 ===')
|
|
65
|
-
{
|
|
66
|
-
const cwd = setup('t1'), sd = spec('t1')
|
|
67
|
-
run(`node "${binCLI}" init "${cwd}" --spec-dir "${sd}"`)
|
|
68
|
-
run(`node "${binCLI}" --dir "${cwd}" --spec-dir "${sd}" run scan --spec-root "${sd}" --runtime-root "${sd}/runtime" --workspace-id ws1 --scan-run-id sr1`)
|
|
69
|
-
|
|
70
|
-
const inSpecDir = join(sd, '.runtime', 'platform-scan.json')
|
|
71
|
-
const pointerFile = join(cwd, '.sillyspec-platform.json')
|
|
72
|
-
assert(existsSync(inSpecDir), `platform-scan.json 在 specDir/.runtime/`)
|
|
73
|
-
assert(existsSync(pointerFile), `恢复指针在 cwd/.sillyspec-platform.json(不在 .sillyspec 内)`)
|
|
74
|
-
|
|
75
|
-
const content = JSON.parse(readFileSync(inSpecDir, 'utf8'))
|
|
76
|
-
assert(content.specRoot === sd, `specRoot 指向 specDir`)
|
|
77
|
-
assert(content.workspaceId === 'ws1', `workspaceId 保存正确`)
|
|
78
|
-
assert(content.scanRunId === 'sr1', `scanRunId 保存正确`)
|
|
79
|
-
// 关键:cwd/.sillyspec/ 不应被创建
|
|
80
|
-
assert(!existsSync(join(cwd, '.sillyspec')), `cwd/.sillyspec/ 未被创建(源码零污染)`)
|
|
81
|
-
clean(cwd, sd)
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// ── Test 2: 残留清理:旧版本创建的 cwd/.sillyspec 会被自动删除 ──
|
|
85
|
-
console.log('\n=== Test 2: 旧版本残留清理 ===')
|
|
86
|
-
{
|
|
87
|
-
const cwd = setup('t2'), sd = spec('t2')
|
|
88
|
-
// 模拟旧版本创建的残留
|
|
89
|
-
mkdirSync(join(cwd, '.sillyspec', '.runtime'), { recursive: true })
|
|
90
|
-
writeFileSync(join(cwd, '.sillyspec', '.runtime', 'old.db'), 'x')
|
|
91
|
-
mkdirSync(join(cwd, '.sillyspec', 'changes'), { recursive: true })
|
|
92
|
-
assert(existsSync(join(cwd, '.sillyspec')), `残留存在`)
|
|
93
|
-
// init 时应清理
|
|
94
|
-
run(`node "${binCLI}" init "${cwd}" --spec-dir "${sd}"`)
|
|
95
|
-
assert(!existsSync(join(cwd, '.sillyspec')), `init 清理了 cwd/.sillyspec/`)
|
|
96
|
-
// run 时也不应再创建
|
|
97
|
-
run(`node "${binCLI}" --dir "${cwd}" --spec-dir "${sd}" run scan --spec-root "${sd}" --runtime-root "${sd}/runtime" --workspace-id ws --scan-run-id sr 2>&1`)
|
|
98
|
-
assert(!existsSync(join(cwd, '.sillyspec')), `run 后 cwd/.sillyspec/ 仍不存在`)
|
|
99
|
-
clean(cwd, sd)
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// ── Test 3: --done 不带 --spec-root 时恢复 ──
|
|
103
|
-
console.log('\n=== Test 3: --done 恢复平台参数 ===')
|
|
104
|
-
{
|
|
105
|
-
const cwd = setup('t2'), sd = spec('t2')
|
|
106
|
-
run(`node "${binCLI}" init "${cwd}" --spec-dir "${sd}"`)
|
|
107
|
-
run(`node "${binCLI}" --dir "${cwd}" --spec-dir "${sd}" run scan --spec-root "${sd}" --runtime-root "${sd}/runtime" --workspace-id ws2 --scan-run-id sr2`)
|
|
108
|
-
// --done 不带任何平台参数
|
|
109
|
-
const output = run(`node "${binCLI}" --dir "${cwd}" run scan --done --change default --dir "${cwd}" --input "test" --output "test done" 2>&1`)
|
|
110
|
-
assert(output.includes('平台模式'), `恢复成功:包含平台模式指令`)
|
|
111
|
-
assert(output.includes(sd), `恢复成功:包含 specDir 路径`)
|
|
112
|
-
clean(cwd, sd)
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// ── Test 3-6: stage-contract 路径(通过 runValidators) ──
|
|
116
|
-
const { runValidators } = await import(pathToFileURL(join(root, 'src', 'stage-contract.js')).href)
|
|
117
|
-
|
|
118
|
-
console.log('\n=== Test 5: specDir 有文档 → 校验通过 ===')
|
|
119
|
-
{
|
|
120
|
-
const cwd = setup('t3'), sd = spec('t3')
|
|
121
|
-
const proj = basename(cwd)
|
|
122
|
-
const scanDir = join(sd, 'docs', proj)
|
|
123
|
-
writeSpecDocs(scanDir)
|
|
124
|
-
const result = runValidators('scan', cwd, 'default', { projectName: proj, specRoot: sd })
|
|
125
|
-
assert(result.ok, `specDir 有文档: ok=${result.ok}, errors=${JSON.stringify(result.errors)}`)
|
|
126
|
-
clean(cwd, sd)
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
console.log('\n=== Test 5: specDir 缺文档 → 校验失败,路径不含 .sillyspec ===')
|
|
130
|
-
{
|
|
131
|
-
const cwd = setup('t4'), sd = spec('t4')
|
|
132
|
-
const proj = basename(cwd)
|
|
133
|
-
mkdirSync(join(sd, 'docs'), { recursive: true })
|
|
134
|
-
const result = runValidators('scan', cwd, 'default', { projectName: proj, specRoot: sd })
|
|
135
|
-
assert(!result.ok, `specDir 缺文档: ok=${result.ok}`)
|
|
42
|
+
|
|
43
|
+
function run(cmd) {
|
|
44
|
+
return execSync(cmd, { encoding: 'utf8', timeout: 15000, stdio: ['pipe', 'pipe', 'pipe'] })
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const DOCS = ['ARCHITECTURE.md','CONVENTIONS.md','STRUCTURE.md','INTEGRATIONS.md','TESTING.md','CONCERNS.md','PROJECT.md']
|
|
48
|
+
function writeSpecDocs(dir) {
|
|
49
|
+
for (const d of DOCS) {
|
|
50
|
+
const p = join(dir, 'scan', d)
|
|
51
|
+
mkdirSync(dirname(p), { recursive: true })
|
|
52
|
+
writeFileSync(p, 'author: bot\ncreated_at: now\n# doc\n')
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function writeLocalDocs(cwd) {
|
|
56
|
+
for (const d of DOCS) {
|
|
57
|
+
const p = join(cwd, '.sillyspec', 'docs', basename(cwd), 'scan', d)
|
|
58
|
+
mkdirSync(dirname(p), { recursive: true })
|
|
59
|
+
writeFileSync(p, 'author: bot\ncreated_at: now\n# doc\n')
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ── Test 1: platform-scan.json 写入位置 ──
|
|
64
|
+
console.log('\n=== Test 1: platform-scan.json 写入位置 ===')
|
|
65
|
+
{
|
|
66
|
+
const cwd = setup('t1'), sd = spec('t1')
|
|
67
|
+
run(`node "${binCLI}" init "${cwd}" --spec-dir "${sd}"`)
|
|
68
|
+
run(`node "${binCLI}" --dir "${cwd}" --spec-dir "${sd}" run scan --spec-root "${sd}" --runtime-root "${sd}/runtime" --workspace-id ws1 --scan-run-id sr1`)
|
|
69
|
+
|
|
70
|
+
const inSpecDir = join(sd, '.runtime', 'platform-scan.json')
|
|
71
|
+
const pointerFile = join(cwd, '.sillyspec-platform.json')
|
|
72
|
+
assert(existsSync(inSpecDir), `platform-scan.json 在 specDir/.runtime/`)
|
|
73
|
+
assert(existsSync(pointerFile), `恢复指针在 cwd/.sillyspec-platform.json(不在 .sillyspec 内)`)
|
|
74
|
+
|
|
75
|
+
const content = JSON.parse(readFileSync(inSpecDir, 'utf8'))
|
|
76
|
+
assert(content.specRoot === sd, `specRoot 指向 specDir`)
|
|
77
|
+
assert(content.workspaceId === 'ws1', `workspaceId 保存正确`)
|
|
78
|
+
assert(content.scanRunId === 'sr1', `scanRunId 保存正确`)
|
|
79
|
+
// 关键:cwd/.sillyspec/ 不应被创建
|
|
80
|
+
assert(!existsSync(join(cwd, '.sillyspec')), `cwd/.sillyspec/ 未被创建(源码零污染)`)
|
|
81
|
+
clean(cwd, sd)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ── Test 2: 残留清理:旧版本创建的 cwd/.sillyspec 会被自动删除 ──
|
|
85
|
+
console.log('\n=== Test 2: 旧版本残留清理 ===')
|
|
86
|
+
{
|
|
87
|
+
const cwd = setup('t2'), sd = spec('t2')
|
|
88
|
+
// 模拟旧版本创建的残留
|
|
89
|
+
mkdirSync(join(cwd, '.sillyspec', '.runtime'), { recursive: true })
|
|
90
|
+
writeFileSync(join(cwd, '.sillyspec', '.runtime', 'old.db'), 'x')
|
|
91
|
+
mkdirSync(join(cwd, '.sillyspec', 'changes'), { recursive: true })
|
|
92
|
+
assert(existsSync(join(cwd, '.sillyspec')), `残留存在`)
|
|
93
|
+
// init 时应清理
|
|
94
|
+
run(`node "${binCLI}" init "${cwd}" --spec-dir "${sd}"`)
|
|
95
|
+
assert(!existsSync(join(cwd, '.sillyspec')), `init 清理了 cwd/.sillyspec/`)
|
|
96
|
+
// run 时也不应再创建
|
|
97
|
+
run(`node "${binCLI}" --dir "${cwd}" --spec-dir "${sd}" run scan --spec-root "${sd}" --runtime-root "${sd}/runtime" --workspace-id ws --scan-run-id sr 2>&1`)
|
|
98
|
+
assert(!existsSync(join(cwd, '.sillyspec')), `run 后 cwd/.sillyspec/ 仍不存在`)
|
|
99
|
+
clean(cwd, sd)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ── Test 3: --done 不带 --spec-root 时恢复 ──
|
|
103
|
+
console.log('\n=== Test 3: --done 恢复平台参数 ===')
|
|
104
|
+
{
|
|
105
|
+
const cwd = setup('t2'), sd = spec('t2')
|
|
106
|
+
run(`node "${binCLI}" init "${cwd}" --spec-dir "${sd}"`)
|
|
107
|
+
run(`node "${binCLI}" --dir "${cwd}" --spec-dir "${sd}" run scan --spec-root "${sd}" --runtime-root "${sd}/runtime" --workspace-id ws2 --scan-run-id sr2`)
|
|
108
|
+
// --done 不带任何平台参数
|
|
109
|
+
const output = run(`node "${binCLI}" --dir "${cwd}" run scan --done --change default --dir "${cwd}" --input "test" --output "test done" 2>&1`)
|
|
110
|
+
assert(output.includes('平台模式'), `恢复成功:包含平台模式指令`)
|
|
111
|
+
assert(output.includes(sd), `恢复成功:包含 specDir 路径`)
|
|
112
|
+
clean(cwd, sd)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// ── Test 3-6: stage-contract 路径(通过 runValidators) ──
|
|
116
|
+
const { runValidators } = await import(pathToFileURL(join(root, 'src', 'stage-contract.js')).href)
|
|
117
|
+
|
|
118
|
+
console.log('\n=== Test 5: specDir 有文档 → 校验通过 ===')
|
|
119
|
+
{
|
|
120
|
+
const cwd = setup('t3'), sd = spec('t3')
|
|
121
|
+
const proj = basename(cwd)
|
|
122
|
+
const scanDir = join(sd, 'docs', proj)
|
|
123
|
+
writeSpecDocs(scanDir)
|
|
124
|
+
const result = runValidators('scan', cwd, 'default', { projectName: proj, specRoot: sd })
|
|
125
|
+
assert(result.ok, `specDir 有文档: ok=${result.ok}, errors=${JSON.stringify(result.errors)}`)
|
|
126
|
+
clean(cwd, sd)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
console.log('\n=== Test 5: specDir 缺文档 → 校验失败,路径不含 .sillyspec ===')
|
|
130
|
+
{
|
|
131
|
+
const cwd = setup('t4'), sd = spec('t4')
|
|
132
|
+
const proj = basename(cwd)
|
|
133
|
+
mkdirSync(join(sd, 'docs'), { recursive: true })
|
|
134
|
+
const result = runValidators('scan', cwd, 'default', { projectName: proj, specRoot: sd })
|
|
135
|
+
assert(!result.ok, `specDir 缺文档: ok=${result.ok}`)
|
|
136
136
|
assert(result.errors.length > 0, `有 errors`)
|
|
137
137
|
const errMsg = result.errors[0]
|
|
138
138
|
assert(!hasPathSegments(errMsg, ['.sillyspec', 'docs']), `路径不含 .sillyspec: ${errMsg}`)
|
|
139
139
|
assert(hasPathSegments(errMsg, ['docs', proj, 'scan']), `路径含 docs/${proj}/scan: ${errMsg}`)
|
|
140
|
-
clean(cwd, sd)
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
console.log('\n=== Test 7: 非平台模式有文档 → 校验通过 ===')
|
|
144
|
-
{
|
|
145
|
-
const cwd = setup('t5')
|
|
146
|
-
const proj = basename(cwd)
|
|
147
|
-
writeLocalDocs(cwd)
|
|
148
|
-
const result = runValidators('scan', cwd, 'default', { projectName: proj })
|
|
149
|
-
assert(result.ok, `非平台有文档: ok=${result.ok}`)
|
|
150
|
-
clean(cwd)
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
console.log('\n=== Test 7: 非平台模式缺文档 → 路径含 .sillyspec ===')
|
|
154
|
-
{
|
|
155
|
-
const cwd = setup('t6')
|
|
156
|
-
const proj = basename(cwd)
|
|
157
|
-
const result = runValidators('scan', cwd, 'default', { projectName: proj })
|
|
140
|
+
clean(cwd, sd)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
console.log('\n=== Test 7: 非平台模式有文档 → 校验通过 ===')
|
|
144
|
+
{
|
|
145
|
+
const cwd = setup('t5')
|
|
146
|
+
const proj = basename(cwd)
|
|
147
|
+
writeLocalDocs(cwd)
|
|
148
|
+
const result = runValidators('scan', cwd, 'default', { projectName: proj })
|
|
149
|
+
assert(result.ok, `非平台有文档: ok=${result.ok}`)
|
|
150
|
+
clean(cwd)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
console.log('\n=== Test 7: 非平台模式缺文档 → 路径含 .sillyspec ===')
|
|
154
|
+
{
|
|
155
|
+
const cwd = setup('t6')
|
|
156
|
+
const proj = basename(cwd)
|
|
157
|
+
const result = runValidators('scan', cwd, 'default', { projectName: proj })
|
|
158
158
|
assert(!result.ok, `非平台缺文档: ok=${result.ok}`)
|
|
159
159
|
const errMsg = result.errors[0]
|
|
160
160
|
assert(hasPathSegments(errMsg, ['.sillyspec', 'docs']), `路径含 .sillyspec/docs: ${errMsg}`)
|
|
161
|
-
clean(cwd)
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
console.log(`\n${'='.repeat(50)}`)
|
|
165
|
-
console.log(`✅ 通过: ${passed} ❌ 失败: ${failed}`)
|
|
166
|
-
console.log(`${'='.repeat(50)}`)
|
|
167
|
-
if (failed > 0) throw new Error(`${failed} test(s) failed`)
|
|
161
|
+
clean(cwd)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
console.log(`\n${'='.repeat(50)}`)
|
|
165
|
+
console.log(`✅ 通过: ${passed} ❌ 失败: ${failed}`)
|
|
166
|
+
console.log(`${'='.repeat(50)}`)
|
|
167
|
+
if (failed > 0) throw new Error(`${failed} test(s) failed`)
|