create-dwy 0.19.0 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/package.json +1 -1
  3. package/src/cli-help.js +28 -2
  4. package/src/prompt-style.js +82 -0
  5. package/src/searchable-select.js +12 -29
  6. package/src/sync-all.js +95 -583
  7. package/src/sync-codex.js +14 -7
  8. package/src/sync-opencode.js +8 -22
  9. package/src/sync.js +60 -149
  10. package/src/tab-multiselect.js +21 -64
  11. package/templates/ai-tools/CHANGELOG.md +195 -0
  12. package/templates/ai-tools/CLAUDE.md +39 -58
  13. package/templates/ai-tools/VERSION +1 -1
  14. package/templates/ai-tools/hooks/Git/pre-git-commit-ai-signature-check.sh +5 -4
  15. package/templates/ai-tools/rules/Android/dwy-android-core.md +147 -13
  16. package/templates/ai-tools/rules/Android/dwy-android-layering.md +142 -0
  17. package/templates/ai-tools/rules/Android/dwy-android-mvp.md +55 -0
  18. package/templates/ai-tools/rules/Android/dwy-android-support-only.md +2 -0
  19. package/templates/ai-tools/rules/Docker/dwy-docker.md +23 -1
  20. package/templates/ai-tools/rules/Flutter/dwy-flutter-core.md +2 -0
  21. package/templates/ai-tools/rules/Flutter/dwy-flutter-testing.md +1 -1
  22. package/templates/ai-tools/rules/HarmonyOS/dwy-arkts-ui-build.md +12 -8
  23. package/templates/ai-tools/rules/Python/dwy-python-backend.md +4 -43
  24. package/templates/ai-tools/rules/Python/dwy-python-base-lib.md +1 -1
  25. package/templates/ai-tools/rules/Python/dwy-python-layering.md +169 -0
  26. package/templates/ai-tools/rules/Vue/dwy-vue-core.md +11 -49
  27. package/templates/ai-tools/rules/Vue/dwy-vue-layering.md +153 -0
  28. package/templates/ai-tools/rules/iOS/dwy-apple-layering.md +133 -0
  29. package/templates/ai-tools/rules/iOS/dwy-swift-core.md +2 -0
  30. package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-code-craft.md +83 -0
  31. package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-dependency-freshness.md +27 -66
  32. package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-git-commit.md +8 -219
  33. package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-oss-commercial-license.md +27 -123
  34. package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-prod-branch.md +31 -0
  35. package/templates/ai-tools/rules//346/225/260/346/215/256/345/272/223/dwy-postgres.md +17 -10
  36. package/templates/ai-tools/rules//346/225/260/346/215/256/345/272/223/dwy-redis.md +6 -3
  37. package/templates/ai-tools/skills/Docker/dwy-deploy-first/SKILL.md +14 -14
  38. package/templates/ai-tools/skills/Docker/dwy-deploy-first/references/chapter-01-mirrors.md +2 -2
  39. package/templates/ai-tools/skills/Docker/dwy-docker/SKILL.md +21 -13
  40. package/templates/ai-tools/skills/Docker/dwy-docker/references/ask-templates.md +13 -12
  41. package/templates/ai-tools/skills/Docker/dwy-docker/references/templates.md +3 -1
  42. package/templates/ai-tools/skills/Docker/dwy-docker/references/version-rules.md +10 -6
  43. package/templates/ai-tools/skills/Docker/dwy-docker/scripts/query_dockerhub.py +21 -6
  44. package/templates/ai-tools/skills/Docker/dwy-docker/scripts/scan_images.sh +2 -2
  45. package/templates/ai-tools/skills/Docker/dwy-mirror-source/SKILL.md +44 -30
  46. package/templates/ai-tools/skills/Docker/dwy-mirror-source/references/dockerfile-snippets.md +2 -0
  47. package/templates/ai-tools/skills/Docker/dwy-mirror-source/references/mirror-providers.md +8 -8
  48. package/templates/ai-tools/skills/Docker/dwy-mirror-source/scripts/apply_mirrors.py +14 -1
  49. package/templates/ai-tools/skills/Docker/dwy-mirror-source/scripts/check_mirrors.py +4 -0
  50. package/templates/ai-tools/skills/DolphinDB/dwy-dolphindb/SKILL.md +8 -6
  51. package/templates/ai-tools/skills/DolphinDB/dwy-dolphindb/references/digest/python-sdk.md +3 -3
  52. package/templates/ai-tools/skills/DolphinDB/dwy-dolphindb/references/digest/stream-engine.md +2 -2
  53. package/templates/ai-tools/skills/DolphinDB/dwy-dolphindb/references/review-rules/anti-patterns.md +3 -1
  54. package/templates/ai-tools/skills/DolphinDB/dwy-dolphindb/references/review-rules/change-checklist.md +4 -4
  55. package/templates/ai-tools/skills/DolphinDB/dwy-dolphindb/references/review-rules/lethal-violations.md +5 -5
  56. package/templates/ai-tools/skills/DolphinDB/dwy-dolphindb/references/review-rules/perf-baselines.md +5 -5
  57. package/templates/ai-tools/skills/Python/dwy-eapi/SKILL.md +59 -200
  58. package/templates/ai-tools/skills/Python/dwy-eapi/shared/anticorruption.md +13 -0
  59. package/templates/ai-tools/skills/Python/dwy-eapi/shared/layering.md +5 -0
  60. package/templates/ai-tools/skills/Python/dwy-eapi/shared/response-envelope.md +20 -0
  61. package/templates/ai-tools/skills/Python/dwy-eapi/shared/timezone.md +7 -0
  62. package/templates/ai-tools/skills/Python/dwy-eapi/v0/breaking.md +15 -0
  63. package/templates/ai-tools/skills/Python/dwy-eapi/v0/email-canonical.md +43 -0
  64. package/templates/ai-tools/skills/Python/dwy-eapi/v0/index.md +103 -0
  65. package/templates/ai-tools/skills/Python/dwy-eapi/v0/providers-email.md +49 -0
  66. package/templates/ai-tools/skills/Python/dwy-eapi/versions.yml +13 -0
  67. package/templates/ai-tools/skills/Vue/dwy-eui/SKILL.md +62 -306
  68. package/templates/ai-tools/skills/Vue/dwy-eui/references/eui-design-guide.md +8 -1
  69. package/templates/ai-tools/skills/Vue/dwy-eui/references/eui-integration-guide.md +0 -31
  70. package/templates/ai-tools/skills/Vue/dwy-eui/shared/config-theme.md +8 -0
  71. package/templates/ai-tools/skills/Vue/dwy-eui/shared/form.md +6 -0
  72. package/templates/ai-tools/skills/Vue/dwy-eui/shared/overlay.md +6 -0
  73. package/templates/ai-tools/skills/Vue/dwy-eui/shared/reka.md +5 -0
  74. package/templates/ai-tools/skills/Vue/dwy-eui/v1/index.md +8 -0
  75. package/templates/ai-tools/skills/Vue/dwy-eui/v2/breaking.md +14 -0
  76. package/templates/ai-tools/skills/Vue/dwy-eui/v2/index.md +55 -0
  77. package/templates/ai-tools/skills/Vue/dwy-eui/v2/lookup.md +29 -0
  78. package/templates/ai-tools/skills/Vue/dwy-eui/versions.yml +12 -0
  79. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-architecture/SKILL.md +11 -14
  80. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-product-launcher/references/state-and-contract.md +1 -1
  81. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-prototype/SKILL.md +1 -1
  82. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-github-action-publish/SKILL.md +32 -15
  83. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-github-action-publish/references/electron-release-workflow-template.yml +3 -1
  84. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-github-action-publish/references/release-workflow-template.yml +8 -5
  85. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-ios-app-store-release/SKILL.md +1 -1
  86. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-ios-app-store-release/assets/appstore-submission.yaml +1 -1
  87. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-ios-app-store-release/references/aso.md +2 -0
  88. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-ios-app-store-release/references/in-app-purchase-review.md +1 -1
  89. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/SKILL.md +4 -3
  90. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/android.md +1 -1
  91. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/backend-deploy.md +1 -1
  92. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/changelog.md +45 -27
  93. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/frontend-deploy.md +1 -1
  94. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/generic-release.md +1 -1
  95. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/git-tag.md +1 -1
  96. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/harmony.md +1 -1
  97. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/ios.md +1 -1
  98. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-semver/SKILL.md +8 -0
  99. package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/SKILL.md +7 -7
  100. package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/references/checks-runtime.md +63 -59
  101. package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/scripts/check_base.sh +10 -1
  102. package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/scripts/check_capacity.sh +21 -23
  103. package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/scripts/check_db.sh +9 -1
  104. package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/scripts/check_docker.sh +20 -24
  105. package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/scripts/check_logs.sh +3 -3
  106. package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/dwy-doubao-tts/SKILL.md +4 -2
  107. package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/dwy-doubao-tts/references/api.md +1 -1
  108. package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/dwy-doubao-tts/scripts/synthesize.py +1 -0
  109. package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/media-platform-packaging/SKILL.md +7 -5
  110. package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/media-platform-packaging/references/platform-copy-examples.md +5 -3
  111. package/templates/ai-tools/skills//351/200/232/347/224/250/dwy-dependency-choice/SKILL.md +140 -0
  112. package/templates/ai-tools/skills//351/200/232/347/224/250/dwy-git-commit/SKILL.md +113 -0
  113. package/templates/ai-tools/skills//351/200/232/347/224/250/dwy-shared/SKILL.md +16 -13
  114. package/templates/ai-tools/skills//351/200/232/347/224/250/dwy-whiteboard-prototype/SKILL.md +1 -1
  115. package/src/sync-packs.js +0 -223
  116. package/src/sync-scene.js +0 -178
  117. package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-tdd-development.md +0 -115
package/src/sync-scene.js DELETED
@@ -1,178 +0,0 @@
1
- /**
2
- * 按包勾选交互:技术栈包 → 场景包。
3
- * 每包一个 Tab,Tab 下列 checkbox;←/→ 切包,Space 勾子项,a 全选本包。
4
- * 由 dwy / dwy sync 在「按场景和技术栈」模式下调用。
5
- */
6
-
7
- import { isCancel } from '@clack/prompts'
8
- import { tabMultiselect } from './tab-multiselect.js'
9
- import {
10
- SCENE_PACKS,
11
- STACK_PACKS,
12
- listPackItems,
13
- normalizePacks,
14
- } from './sync-packs.js'
15
- import { chalk } from './utils.js'
16
-
17
- /** 子 value 三段分隔:packId / type / name。packId 不含冒号。 */
18
- const PACK_CHILD_RE = /^([^:]+):(skills|rules|hooks):(.+)$/
19
-
20
- /**
21
- * 子条目在 Tab 多选里的稳定 value。
22
- * 带 packId:同一 skill 可同时出现在 Vue 与 Python,取消一边不能误伤另一边。
23
- *
24
- * @param {string} packId
25
- * @param {'skills' | 'rules' | 'hooks'} type
26
- * @param {string} name
27
- */
28
- export function packChildKey(packId, type, name) {
29
- return `${packId}:${type}:${name}`
30
- }
31
-
32
- /**
33
- * 解析 packChildKey。对不上的丢掉,避免脏 initialValues 撑进结果。
34
- *
35
- * @param {unknown} key
36
- * @returns {{ packId: string, type: 'skills' | 'rules' | 'hooks', name: string } | null}
37
- */
38
- export function parsePackChildKey(key) {
39
- const match = String(key).match(PACK_CHILD_RE)
40
- if (!match) return null
41
- return { packId: match[1], type: match[2], name: match[3] }
42
- }
43
-
44
- /**
45
- * 每个包一个 Tab。空包跳过,避免 Tab 下面没有可选项。
46
- *
47
- * @param {object[]} packs
48
- * @param {object} scans
49
- * @param {{ skillsOnly?: boolean, group?: string }} [opts]
50
- * @returns {Array<{ id: string, label: string, group?: string, options: Array<{ value: string, label: string, type: string, description?: string }> }>}
51
- */
52
- export function packTabs(packs, scans, { skillsOnly = false, group } = {}) {
53
- const tabs = []
54
- for (const pack of packs) {
55
- const items = listPackItems(pack, scans, { skillsOnly })
56
- if (items.length === 0) continue
57
- tabs.push({
58
- id: pack.id,
59
- label: pack.label,
60
- ...(group ? { group } : {}),
61
- options: items.map(item => ({
62
- value: packChildKey(pack.id, item.type, item.name),
63
- label: item.name,
64
- type: item.type,
65
- description: item.description || pack.description,
66
- })),
67
- })
68
- }
69
- return tabs
70
- }
71
-
72
- /**
73
- * 已选包 → 其下全部子 value,给 tabMultiselect initialValues。
74
- * 打开时该包 Tab 显示为全勾,用户可再取消。
75
- *
76
- * @param {object[]} packs
77
- * @param {object} scans
78
- * @param {string[]} selectedPackIds
79
- * @param {{ skillsOnly?: boolean }} [opts]
80
- */
81
- export function initialChildValues(packs, scans, selectedPackIds, { skillsOnly = false } = {}) {
82
- const selected = new Set(selectedPackIds)
83
- const values = []
84
- for (const pack of packs) {
85
- if (!selected.has(pack.id)) continue
86
- for (const item of listPackItems(pack, scans, { skillsOnly })) {
87
- values.push(packChildKey(pack.id, item.type, item.name))
88
- }
89
- }
90
- return values
91
- }
92
-
93
- /**
94
- * 把勾上的子 value 还原成条目 + 所属包。
95
- * 只认扫描结果里真实存在的名字,防止 key 伪造。
96
- *
97
- * @param {string[]} keys
98
- * @param {object} scans
99
- * @param {{ skillsOnly?: boolean }} [opts]
100
- */
101
- export function itemsFromChildKeys(keys, scans, { skillsOnly = false } = {}) {
102
- const parsed = keys.map(parsePackChildKey).filter(Boolean)
103
- const packIds = [...new Set(parsed.map(item => item.packId))]
104
- const names = { skills: new Set(), rules: new Set(), hooks: new Set() }
105
- for (const item of parsed) names[item.type].add(item.name)
106
- const pick = (type) => (scans[type] || []).filter(item => names[type].has(item.name))
107
- if (skillsOnly) {
108
- return { packIds, skills: pick('skills'), rules: [], commands: [], hooks: [] }
109
- }
110
- return {
111
- packIds,
112
- skills: pick('skills'),
113
- rules: pick('rules'),
114
- commands: [],
115
- hooks: pick('hooks'),
116
- }
117
- }
118
-
119
- /**
120
- * 打印本次按包勾选的包名与条数。
121
- *
122
- * @param {string[]} stacks
123
- * @param {string[]} scenes
124
- * @param {{ skills: object[], rules: object[], hooks: object[] }} selected
125
- */
126
- function printPackSummary(stacks, scenes, selected) {
127
- const stackLabels = STACK_PACKS.filter(pack => stacks.includes(pack.id)).map(pack => pack.label)
128
- const sceneLabels = SCENE_PACKS.filter(pack => scenes.includes(pack.id)).map(pack => pack.label)
129
- const packText = [...stackLabels, ...sceneLabels].join(' + ') || '(未选包)'
130
- console.log(chalk.gray('\n包展开:'))
131
- console.log(chalk.gray(` ${packText}`))
132
- console.log(chalk.gray(` Skills ${selected.skills.length} Rules ${selected.rules.length} Hooks ${selected.hooks.length}`))
133
- }
134
-
135
- /**
136
- * 按包选择条目。技术栈 Tab 在左、场景 Tab 在右,同一屏勾选。
137
- * 返回 { skills, rules, commands, hooks, packs } 或 null(取消)。
138
- *
139
- * @param {object} scans
140
- * @param {{ packs?: { stacks?: string[], scenes?: string[] } }} syncState
141
- * @param {{ skillsOnly?: boolean }} [opts]
142
- */
143
- export async function interactiveSelectByPacks(scans, syncState, { skillsOnly = false } = {}) {
144
- const previous = normalizePacks(syncState?.packs)
145
- const packOpts = { skillsOnly }
146
- const defaultStacks = previous.stacks.length > 0 ? previous.stacks : ['common']
147
- const tabs = [
148
- ...packTabs(STACK_PACKS, scans, { ...packOpts, group: 'stack' }),
149
- ...packTabs(SCENE_PACKS, scans, { ...packOpts, group: 'scene' }),
150
- ]
151
- const keys = await tabMultiselect({
152
- message: '选择技术栈和场景包',
153
- tabs,
154
- initialValues: [
155
- ...initialChildValues(STACK_PACKS, scans, defaultStacks, packOpts),
156
- ...initialChildValues(SCENE_PACKS, scans, previous.scenes, packOpts),
157
- ],
158
- required: true,
159
- maxItems: 12,
160
- })
161
- if (isCancel(keys)) return null
162
-
163
- const stacks = STACK_PACKS.map(pack => pack.id).filter(id =>
164
- keys.some(key => parsePackChildKey(key)?.packId === id),
165
- )
166
- const scenes = SCENE_PACKS.map(pack => pack.id).filter(id =>
167
- keys.some(key => parsePackChildKey(key)?.packId === id),
168
- )
169
- const selected = itemsFromChildKeys(keys, scans, packOpts)
170
- printPackSummary(stacks, scenes, selected)
171
- return {
172
- skills: selected.skills,
173
- rules: selected.rules,
174
- commands: selected.commands,
175
- hooks: selected.hooks,
176
- packs: { stacks, scenes },
177
- }
178
- }
@@ -1,115 +0,0 @@
1
- ---
2
- description: TDD 开发流程(新功能/bug 修复/重构的测试先行与回归策略)与测试-源码分隔(测试工程独立 tests/ 目录、镜像源码结构、各栈目录约定)
3
- ---
4
-
5
- # TDD 开发规则
6
-
7
- 所有代码变更必须遵循测试驱动开发流程。测试不是事后补的,是开发的起点。
8
-
9
- ## 开发流程
10
-
11
- ### 新功能
12
-
13
- 1. 先写测试 — 定义预期行为,测试此时应失败
14
- 2. 再写实现 — 用最简代码让测试通过
15
- 3. 重构优化 — 在测试保护下重构,确保始终通过
16
-
17
- ### Bug 修复
18
-
19
- 1. 先写复现测试 — 写一个能暴露 bug 的测试用例,确认它失败
20
- 2. 再修代码 — 修复 bug,确认测试通过
21
- 3. 跑回归 — 确认没有引入新问题
22
-
23
- ### 重构
24
-
25
- 1. 先跑现有测试 — 确认当前全部通过
26
- 2. 再重构 — 修改代码
27
- 3. 再跑测试 — 确认行为没变
28
-
29
- ## 测试与源码分隔(硬约束)
30
-
31
- 测试工程必须与源码分隔,不混在一起。
32
-
33
- ### 核心规则
34
-
35
- - 测试代码**禁止**与源码同目录混放;测试集中在项目根 / 包根的独立 `tests/` 目录。
36
- - `tests/` **镜像源码结构**,测试文件与被测源码一一对应、可反查:
37
- - `src/foo/bar.ts` → `tests/foo/bar.test.ts`
38
- - `src/services/user.py` → `tests/test_user.py`
39
- - 源码目录(`src/`)保持纯净,**只放产品代码**;打包 / 发布产物天然不含测试,CI 用一个目录通配即可圈定全部测试。
40
- - 测试用的 fixture / mock 数据 / 测试工具,放 `tests/` 内(如 `tests/fixtures/`、`tests/conftest.py`),不散落进源码目录。
41
-
42
- ### 为什么(约束的根因)
43
-
44
- 测试混入源码目录会引发一连串问题,分隔后一次性收口:
45
-
46
- - **被打包进发布产物**:测试随源码一起发到 npm / PyPI,污染产物、撑大体积。
47
- - **source map / 内部细节泄露**:测试常含内部实现假设、mock 的真实数据,泄露到产物有安全风险。
48
- - **覆盖率 / 构建统计污染**:测试文件被计入源码统计,覆盖率、行数失真。
49
- - **CI 难圈定范围**:源码与测试交织,CI 无法用一条目录规则排除测试。
50
- - **AI / 工具难定位**:找不到「源码↔测试」对应关系,反复把测试误写进 `src/`。
51
-
52
- ### 各栈目录与命令速查表
53
-
54
- | 栈 | 测试目录 | 命名约定 | 运行命令 |
55
- |----|---------|---------|---------|
56
- | Vue / Vitest | `tests/` 镜像 `src/` | `*.test.ts` | `pnpm vitest run [路径]` |
57
- | Python / pytest | `tests/` | `test_*.py` | `pytest tests/ -v` / `uv run pytest` |
58
- | Android | `src/test/`(单元)+ `src/androidTest/`(仪器) | `*Test.kt` | `./gradlew test` / `./gradlew connectedAndroidTest` |
59
- | Node / CLI | `tests/` 镜像 `src/` | `*.test.ts` | 对应 runner(vitest / jest) |
60
-
61
- 注:Android 的 `src/test` / `src/androidTest` 是 Gradle 生态**强约定**的独立测试源集(test source set),与 `src/main` 源码完全分隔,符合本规范"测试与源码分隔"理念,按其生态默认即可,**属合规分隔**,不视为"测试写进 src"。
62
-
63
- **通用准则(表中未列的栈)**:遵循「独立测试目录 + 镜像源码结构 + 该生态主流 runner 约定」三条原则自行判断,不硬套上表布局。
64
-
65
- ## 回归测试
66
-
67
- ### 开发中:只跑受影响的测试
68
-
69
- 每次代码改动后,只运行受影响的测试文件,提高开发效率:
70
-
71
- ```bash
72
- # 后端:只跑改动涉及的测试文件
73
- uv run pytest tests/test_factor.py -v # 改了 factor 相关代码
74
- uv run pytest tests/test_tick.py tests/test_upload.py -v # 改了多个模块
75
-
76
- # 前端:只跑对应目录
77
- pnpm vitest run tests/stores/ # 改了 store
78
- pnpm vitest run tests/utils/format.test.ts # 改了单个工具函数
79
- ```
80
-
81
- 判断"受影响的测试"的规则:
82
- - 改了 `services/xxx.py` → 跑 `tests/test_xxx.py`
83
- - 改了 `routers/xxx.py` → 跑 `tests/test_xxx.py`
84
- - 改了 `schemas/` 或 `models/` → 跑引用了它们的测试
85
- - 改了 `conftest.py` 或公共依赖 → 全量回归
86
- - 不确定影响范围 → 全量回归
87
-
88
- ### 提交前:跑受影响的测试即可
89
-
90
- 提交前确保受影响的测试通过即可,不要求全量回归。全量测试留给 CI 或手动触发。
91
-
92
- ## TEST_CASES.md
93
-
94
- 每个包/项目维护一份 `TEST_CASES.md`,记录所有测试用例的清单。**`TEST_CASES.md` 跟测试目录走,不放项目根**:
95
-
96
- - 有 `tests/` → 放 `tests/TEST_CASES.md`
97
- - 测试源集分散(如 Android `src/test/`)→ 放对应测试根(`src/test/TEST_CASES.md`),与该模块测试同处
98
- - 理念:测试清单是测试工程的一部分,归测试目录,与「测试与源码分隔」一致
99
-
100
- 内容要求:
101
-
102
- - 人类可读,方便审查和手动编辑
103
- - 新增/修改/删除功能时必须同步更新
104
- - 内容与实际测试文件保持一致
105
- - 作为版本回测基准
106
-
107
- ## 禁止事项
108
-
109
- - **禁止**不写测试就提交功能代码
110
- - **禁止**写完代码再补测试(先测试后代码)
111
- - **禁止**测试不通过就提交
112
- - **禁止** TEST_CASES.md 与实际测试不同步
113
- - **禁止**测试文件写进 `src/` 源码目录(Android `src/test`、`src/androidTest` 例外,是 Gradle 标准测试源集)
114
- - **禁止**测试目录结构与源码脱节(找不到「源码↔测试」对应关系)
115
- - **禁止**把测试 fixture / mock 数据散落进源码目录