@sk8metal/michi-cli 0.10.1 → 0.12.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 (139) hide show
  1. package/README.md +71 -848
  2. package/dist/scripts/constants/environments.d.ts +1 -1
  3. package/dist/scripts/constants/environments.d.ts.map +1 -1
  4. package/dist/scripts/constants/environments.js +0 -20
  5. package/dist/scripts/constants/environments.js.map +1 -1
  6. package/dist/scripts/phase-runner.js +1 -1
  7. package/dist/scripts/phase-runner.js.map +1 -1
  8. package/dist/scripts/utils/multi-repo-validator.d.ts +18 -0
  9. package/dist/scripts/utils/multi-repo-validator.d.ts.map +1 -1
  10. package/dist/scripts/utils/multi-repo-validator.js +42 -0
  11. package/dist/scripts/utils/multi-repo-validator.js.map +1 -1
  12. package/dist/scripts/utils/tasks-format-validator.js +3 -3
  13. package/dist/scripts/utils/tasks-format-validator.js.map +1 -1
  14. package/dist/scripts/utils/template-finder.d.ts +2 -2
  15. package/dist/scripts/utils/template-finder.d.ts.map +1 -1
  16. package/dist/scripts/utils/template-finder.js +3 -8
  17. package/dist/scripts/utils/template-finder.js.map +1 -1
  18. package/dist/src/cli.d.ts.map +1 -1
  19. package/dist/src/cli.js +0 -8
  20. package/dist/src/cli.js.map +1 -1
  21. package/dist/src/commands/init.d.ts +0 -4
  22. package/dist/src/commands/init.d.ts.map +1 -1
  23. package/dist/src/commands/init.js +6 -30
  24. package/dist/src/commands/init.js.map +1 -1
  25. package/dist/src/commands/setup-existing.d.ts +2 -6
  26. package/dist/src/commands/setup-existing.d.ts.map +1 -1
  27. package/dist/src/commands/setup-existing.js +8 -142
  28. package/dist/src/commands/setup-existing.js.map +1 -1
  29. package/docs/README.md +20 -83
  30. package/docs/getting-started/configuration.md +350 -0
  31. package/docs/getting-started/installation.md +59 -0
  32. package/docs/getting-started/quick-start.md +76 -0
  33. package/docs/guides/atlassian-integration.md +116 -0
  34. package/docs/guides/claude-code.md +155 -0
  35. package/docs/guides/multi-repo.md +117 -0
  36. package/docs/guides/workflow.md +382 -0
  37. package/docs/reference/ai-commands.md +92 -0
  38. package/docs/reference/cli.md +752 -0
  39. package/docs/reference/environment-variables.md +192 -0
  40. package/docs/troubleshooting.md +498 -0
  41. package/package.json +1 -3
  42. package/scripts/__tests__/create-project.test.ts +12 -12
  43. package/scripts/__tests__/setup-existing-project.test.ts +22 -22
  44. package/scripts/constants/__tests__/environments.test.ts +7 -50
  45. package/scripts/constants/environments.ts +1 -27
  46. package/scripts/phase-runner.ts +1 -1
  47. package/scripts/template/__tests__/renderer.test.ts +21 -21
  48. package/scripts/utils/__tests__/multi-repo-validator.test.ts +159 -1
  49. package/scripts/utils/multi-repo-validator.ts +50 -0
  50. package/scripts/utils/tasks-format-validator.ts +3 -3
  51. package/scripts/utils/template-finder.ts +5 -11
  52. package/templates/claude/agents/e2e-first-planner/AGENT.md +1 -1
  53. package/templates/claude/agents/pr-resolver/AGENT.md +15 -3
  54. package/templates/claude/commands/michi/e2e-plan.md +1 -1
  55. package/templates/claude/commands/michi/spec-design.md +2 -2
  56. package/templates/claude/commands/michi/spec-tasks.md +156 -0
  57. package/templates/claude/commands/michi/test-planning.md +1 -1
  58. package/templates/claude/commands/michi/validate-design.md +3 -3
  59. package/templates/claude/commands/michi-multi-repo/impl-all.md +30 -1
  60. package/templates/claude/commands/michi-multi-repo/propagate-specs.md +14 -1
  61. package/templates/claude/commands/michi-multi-repo/spec-review.md +16 -2
  62. package/templates/claude-agent/agents/repo-spec-executor.md +1 -1
  63. package/templates/claude-agent/commands/michi/spec-tasks.md +117 -0
  64. package/templates/claude-agent/rules/code-size-monitor.md +26 -0
  65. package/templates/claude-agent/rules/code-size-rules.md +32 -0
  66. package/templates/michi/cc-sdd-overrides/settings/rules/design-review-michi.md +1 -1
  67. package/docs/context.md +0 -59
  68. package/docs/michi-development/contributing/development.md +0 -341
  69. package/docs/michi-development/contributing/release.md +0 -365
  70. package/docs/michi-development/design/config-unification.md +0 -733
  71. package/docs/michi-development/design/design-config-current-state.md +0 -330
  72. package/docs/michi-development/design/design-config-implementation.md +0 -628
  73. package/docs/michi-development/design/design-config-migration.md +0 -952
  74. package/docs/michi-development/design/design-config-security.md +0 -771
  75. package/docs/michi-development/design/design-config-solution.md +0 -583
  76. package/docs/michi-development/design/design-config-testing.md +0 -892
  77. package/docs/michi-development/testing/manual-verification-flow.md +0 -871
  78. package/docs/michi-development/testing/manual-verification-other-tools.md +0 -1279
  79. package/docs/michi-development/testing/manual-verification-troubleshooting.md +0 -122
  80. package/docs/michi-development/testing/pre-publish-checklist.md +0 -560
  81. package/docs/michi-development/testing-strategy.md +0 -87
  82. package/docs/plan.md +0 -275
  83. package/docs/user-guide/getting-started/github-token-setup.md +0 -510
  84. package/docs/user-guide/getting-started/new-repository-setup.md +0 -704
  85. package/docs/user-guide/getting-started/quick-start.md +0 -212
  86. package/docs/user-guide/getting-started/setup.md +0 -819
  87. package/docs/user-guide/guides/agent-skills-integration.md +0 -222
  88. package/docs/user-guide/guides/customization.md +0 -537
  89. package/docs/user-guide/guides/internationalization.md +0 -540
  90. package/docs/user-guide/guides/migration-guide.md +0 -138
  91. package/docs/user-guide/guides/multi-project.md +0 -368
  92. package/docs/user-guide/guides/multi-repo-guide.md +0 -1590
  93. package/docs/user-guide/guides/phase-automation.md +0 -419
  94. package/docs/user-guide/guides/workflow.md +0 -574
  95. package/docs/user-guide/hands-on/README.md +0 -142
  96. package/docs/user-guide/hands-on/claude-agent-setup.md +0 -597
  97. package/docs/user-guide/hands-on/claude-setup.md +0 -452
  98. package/docs/user-guide/hands-on/cursor-setup.md +0 -353
  99. package/docs/user-guide/hands-on/troubleshooting.md +0 -964
  100. package/docs/user-guide/hands-on/verification-checklist.md +0 -439
  101. package/docs/user-guide/hands-on/workflow-walkthrough.md +0 -1078
  102. package/docs/user-guide/reference/config.md +0 -589
  103. package/docs/user-guide/reference/multi-repo-api.md +0 -771
  104. package/docs/user-guide/reference/quick-reference.md +0 -297
  105. package/docs/user-guide/reference/security-test-payloads.md +0 -50
  106. package/docs/user-guide/reference/tasks-template.md +0 -550
  107. package/docs/user-guide/release/ci-setup-java.md +0 -114
  108. package/docs/user-guide/release/ci-setup-nodejs.md +0 -94
  109. package/docs/user-guide/release/ci-setup-php.md +0 -102
  110. package/docs/user-guide/release/ci-setup-troubleshooting.md +0 -94
  111. package/docs/user-guide/release/ci-setup.md +0 -188
  112. package/docs/user-guide/release/release-flow.md +0 -476
  113. package/docs/user-guide/templates/test-specs/README.md +0 -173
  114. package/docs/user-guide/templates/test-specs/e2e-test-spec-template.md +0 -553
  115. package/docs/user-guide/templates/test-specs/integration-test-spec-template.md +0 -435
  116. package/docs/user-guide/templates/test-specs/performance-test-spec-template.md +0 -454
  117. package/docs/user-guide/templates/test-specs/security-test-spec-template.md +0 -625
  118. package/docs/user-guide/templates/test-specs/unit-test-spec-template.md +0 -328
  119. package/docs/user-guide/testing/integration-tests.md +0 -312
  120. package/docs/user-guide/testing/tdd-cycle.md +0 -349
  121. package/docs/user-guide/testing/test-execution-flow.md +0 -396
  122. package/docs/user-guide/testing/test-failure-handling.md +0 -521
  123. package/docs/user-guide/testing/test-planning-flow.md +0 -185
  124. package/docs/user-guide/testing-strategy.md +0 -185
  125. package/docs/verification-guide.md +0 -518
  126. package/templates/cline/rules/atlassian-integration.md +0 -36
  127. package/templates/cline/rules/michi-core.md +0 -56
  128. package/templates/codex/AGENTS.override.md +0 -277
  129. package/templates/codex/prompts/confluence-sync.md +0 -177
  130. package/templates/codex/rules/README.md +0 -210
  131. package/templates/cursor/commands/kiro/kiro-spec-impl.md +0 -244
  132. package/templates/cursor/commands/kiro/kiro-spec-tasks.md +0 -354
  133. package/templates/cursor/commands/michi/confluence-sync.md +0 -76
  134. package/templates/cursor/commands/michi/project-switch.md +0 -69
  135. package/templates/cursor/rules/atlassian-mcp.mdc +0 -188
  136. package/templates/cursor/rules/github-ssot.mdc +0 -151
  137. package/templates/cursor/rules/multi-project.mdc +0 -81
  138. package/templates/gemini/commands/README.md +0 -41
  139. package/templates/gemini/rules/GEMINI.md +0 -80
@@ -1,892 +0,0 @@
1
- # Michi 設定統合設計書 - テスト戦略
2
-
3
- **バージョン**: 1.0
4
- **作成日**: 2025-01-11
5
- **ステータス**: Draft
6
- **親ドキュメント**: [config-unification.md](./config-unification.md)
7
-
8
- ---
9
-
10
- ## 8. テスト戦略
11
-
12
- 設定統一に伴う変更を安全に実施するためのテスト戦略です。
13
-
14
- ### 8.1 テストスコープ
15
-
16
- #### 8.1.1 対象範囲
17
-
18
- 設定統一により影響を受ける主要な機能をテスト対象とします:
19
-
20
- | カテゴリ | テスト対象 | テストレベル |
21
- |---------|-----------|------------|
22
- | **設定読み込み** | ConfigLoader の3層マージ | 単体 + 統合 |
23
- | **バリデーション** | Zodスキーマによる検証 | 単体 |
24
- | **パス解決** | リポジトリURLのパース | 単体 |
25
- | **コマンドライン** | michi init / migrate | 統合 + E2E |
26
- | **外部API** | Confluence/JIRA/GitHub連携 | 統合 + E2E |
27
- | **マイグレーション** | 既存設定の移行処理 | 統合 + E2E |
28
- | **後方互換性** | 旧形式設定のサポート | 統合 |
29
-
30
- #### 8.1.2 テスト優先順位
31
-
32
- **P0 (Critical): リリースブロッカー**
33
- - ConfigLoader の3層マージロジック
34
- - 必須項目のバリデーション
35
- - リポジトリURLパーサー
36
- - `michi migrate` コマンド
37
-
38
- **P1 (High): リリース前に必須**
39
- - `michi init` コマンド(新規・既存)
40
- - Confluence/JIRA/GitHub連携の動作確認
41
- - 設定ファイルのパーミッション
42
- - エラーメッセージの内容
43
-
44
- **P2 (Medium): リリース後でも対応可能**
45
- - 詳細なエラーメッセージ
46
- - ドライランモード
47
- - ロールバック機能
48
- - 設定バリデーションの詳細
49
-
50
- ### 8.2 単体テスト
51
-
52
- #### 8.2.1 ConfigLoader のテスト
53
-
54
- **ファイル**: `src/config/__tests__/config-loader.test.ts`
55
-
56
- ```typescript
57
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
58
- import { ConfigLoader } from '../config-loader.js';
59
- import { mkdirSync, writeFileSync, rmSync } from 'fs';
60
- import { tmpdir } from 'os';
61
- import { join } from 'path';
62
-
63
- describe('ConfigLoader', () => {
64
- let testDir: string;
65
- let globalEnvPath: string;
66
- let projectDir: string;
67
-
68
- beforeEach(() => {
69
- // テスト用の一時ディレクトリ作成
70
- testDir = join(tmpdir(), `michi-test-${Date.now()}`);
71
- mkdirSync(testDir, { recursive: true });
72
-
73
- // グローバル設定ディレクトリ
74
- globalEnvPath = join(testDir, '.michi', '.env');
75
- mkdirSync(join(testDir, '.michi'), { recursive: true });
76
-
77
- // プロジェクトディレクトリ
78
- projectDir = join(testDir, 'project');
79
- mkdirSync(join(projectDir, '.michi'), { recursive: true });
80
-
81
- // 環境変数を上書き
82
- process.env.HOME = testDir;
83
- });
84
-
85
- afterEach(() => {
86
- // テスト後のクリーンアップ
87
- rmSync(testDir, { recursive: true, force: true });
88
- });
89
-
90
- describe('3層マージ', () => {
91
- it('グローバル設定を正しく読み込む', () => {
92
- // グローバル .env を作成
93
- writeFileSync(globalEnvPath, `
94
- CONFLUENCE_URL=https://global.atlassian.net
95
- JIRA_URL=https://global.atlassian.net
96
- GITHUB_TOKEN=global-token
97
- `.trim());
98
-
99
- const loader = new ConfigLoader(projectDir);
100
- const config = loader.load();
101
-
102
- expect(config.confluence?.url).toBe('https://global.atlassian.net');
103
- expect(config.jira?.url).toBe('https://global.atlassian.net');
104
- expect(config.github?.token).toBe('global-token');
105
- });
106
-
107
- it('プロジェクト設定がグローバル設定を上書きする', () => {
108
- // グローバル .env
109
- writeFileSync(globalEnvPath, `
110
- CONFLUENCE_URL=https://global.atlassian.net
111
- GITHUB_TOKEN=global-token
112
- `.trim());
113
-
114
- // プロジェクト .env
115
- writeFileSync(join(projectDir, '.env'), `
116
- CONFLUENCE_URL=https://project.atlassian.net
117
- `.trim());
118
-
119
- const loader = new ConfigLoader(projectDir);
120
- const config = loader.load();
121
-
122
- // プロジェクト設定が優先される
123
- expect(config.confluence?.url).toBe('https://project.atlassian.net');
124
- // グローバル設定は保持される
125
- expect(config.github?.token).toBe('global-token');
126
- });
127
-
128
- it('プロジェクト config.json がグローバル設定を上書きする', () => {
129
- // グローバル .env
130
- writeFileSync(globalEnvPath, `
131
- CONFLUENCE_URL=https://global.atlassian.net
132
- `.trim());
133
-
134
- // プロジェクト config.json
135
- writeFileSync(join(projectDir, '.michi', 'config.json'), JSON.stringify({
136
- confluence: {
137
- pageCreationGranularity: 'by-section'
138
- }
139
- }, null, 2));
140
-
141
- const loader = new ConfigLoader(projectDir);
142
- const config = loader.load();
143
-
144
- expect(config.confluence?.url).toBe('https://global.atlassian.net');
145
- expect(config.confluence?.pageCreationGranularity).toBe('by-section');
146
- });
147
-
148
- it('優先順位: プロジェクト .env > プロジェクト config.json > グローバル .env', () => {
149
- // グローバル .env
150
- writeFileSync(globalEnvPath, `
151
- CONFLUENCE_SPACE=GLOBAL
152
- JIRA_PROJECT=GLOBAL
153
- `.trim());
154
-
155
- // プロジェクト config.json
156
- writeFileSync(join(projectDir, '.michi', 'config.json'), JSON.stringify({
157
- confluence: { space: 'CONFIG' },
158
- jira: { project: 'CONFIG' }
159
- }, null, 2));
160
-
161
- // プロジェクト .env
162
- writeFileSync(join(projectDir, '.env'), `
163
- CONFLUENCE_SPACE=PROJECT
164
- `.trim());
165
-
166
- const loader = new ConfigLoader(projectDir);
167
- const config = loader.load();
168
-
169
- // プロジェクト .env が最優先
170
- expect(config.confluence?.space).toBe('PROJECT');
171
- // プロジェクト config.json が次
172
- expect(config.jira?.project).toBe('CONFIG');
173
- });
174
- });
175
-
176
- describe('リポジトリURLパーサー', () => {
177
- it('HTTPS形式のURLを正しくパースする', () => {
178
- writeFileSync(join(projectDir, '.michi', 'project.json'), JSON.stringify({
179
- projectId: 'test-project',
180
- repository: 'https://github.com/myorg/myrepo.git'
181
- }, null, 2));
182
-
183
- const loader = new ConfigLoader(projectDir);
184
- const config = loader.load();
185
-
186
- expect(config.github?.repository).toBe('https://github.com/myorg/myrepo.git');
187
- expect(config.github?.repositoryOrg).toBe('myorg');
188
- expect(config.github?.repositoryName).toBe('myrepo');
189
- expect(config.github?.repositoryShort).toBe('myorg/myrepo');
190
- });
191
-
192
- it('SSH形式のURLを正しくパースする', () => {
193
- writeFileSync(join(projectDir, '.michi', 'project.json'), JSON.stringify({
194
- projectId: 'test-project',
195
- repository: 'git@github.com:myorg/myrepo.git'
196
- }, null, 2));
197
-
198
- const loader = new ConfigLoader(projectDir);
199
- const config = loader.load();
200
-
201
- expect(config.github?.repositoryOrg).toBe('myorg');
202
- expect(config.github?.repositoryName).toBe('myrepo');
203
- expect(config.github?.repositoryShort).toBe('myorg/myrepo');
204
- });
205
-
206
- it('.git拡張子なしのURLを正しくパースする', () => {
207
- writeFileSync(join(projectDir, '.michi', 'project.json'), JSON.stringify({
208
- projectId: 'test-project',
209
- repository: 'https://github.com/myorg/myrepo'
210
- }, null, 2));
211
-
212
- const loader = new ConfigLoader(projectDir);
213
- const config = loader.load();
214
-
215
- expect(config.github?.repositoryOrg).toBe('myorg');
216
- expect(config.github?.repositoryName).toBe('myrepo');
217
- });
218
-
219
- it('不正な形式のURLでエラーをスローする', () => {
220
- writeFileSync(join(projectDir, '.michi', 'project.json'), JSON.stringify({
221
- projectId: 'test-project',
222
- repository: 'invalid-url'
223
- }, null, 2));
224
-
225
- const loader = new ConfigLoader(projectDir);
226
-
227
- expect(() => loader.load()).toThrow('Invalid GitHub repository URL');
228
- });
229
- });
230
-
231
- describe('バリデーション', () => {
232
- it('必須項目が不足している場合エラーをスローする', () => {
233
- // グローバル .env なし、プロジェクト .env も最小限
234
- writeFileSync(join(projectDir, '.env'), `
235
- # 何も設定しない
236
- `.trim());
237
-
238
- const loader = new ConfigLoader(projectDir);
239
-
240
- expect(() => loader.load()).toThrow();
241
- });
242
-
243
- it('CONFLUENCE_URL が不正な場合エラーをスローする', () => {
244
- writeFileSync(globalEnvPath, `
245
- CONFLUENCE_URL=not-a-url
246
- `.trim());
247
-
248
- const loader = new ConfigLoader(projectDir);
249
-
250
- expect(() => loader.load()).toThrow();
251
- });
252
- });
253
-
254
- describe('後方互換性', () => {
255
- it('GITHUB_REPO が .env に存在する場合、警告を表示する', () => {
256
- const consoleSpy = vi.spyOn(console, 'warn');
257
-
258
- writeFileSync(join(projectDir, '.env'), `
259
- GITHUB_REPO=myorg/myrepo
260
- `.trim());
261
-
262
- const loader = new ConfigLoader(projectDir);
263
- loader.load();
264
-
265
- expect(consoleSpy).toHaveBeenCalledWith(
266
- expect.stringContaining('GITHUB_REPO is deprecated')
267
- );
268
- });
269
-
270
- it('project.json に repository が存在する場合、GITHUB_REPO は無視される', () => {
271
- writeFileSync(join(projectDir, '.michi', 'project.json'), JSON.stringify({
272
- projectId: 'test-project',
273
- repository: 'https://github.com/correct/repo.git'
274
- }, null, 2));
275
-
276
- writeFileSync(join(projectDir, '.env'), `
277
- GITHUB_REPO=wrong/repo
278
- `.trim());
279
-
280
- const loader = new ConfigLoader(projectDir);
281
- const config = loader.load();
282
-
283
- expect(config.github?.repositoryShort).toBe('correct/repo');
284
- });
285
- });
286
- });
287
- ```
288
-
289
- #### 8.2.2 バリデーションのテスト
290
-
291
- **ファイル**: `src/config/__tests__/validation.test.ts`
292
-
293
- ```typescript
294
- import { describe, it, expect } from 'vitest';
295
- import { AppConfigSchema } from '../config-schema.js';
296
-
297
- describe('設定バリデーション', () => {
298
- describe('Confluence設定', () => {
299
- it('有効なConfluence設定を受け入れる', () => {
300
- const config = {
301
- confluence: {
302
- url: 'https://example.atlassian.net',
303
- username: 'user@example.com',
304
- apiToken: 'token123',
305
- space: 'DEV',
306
- pageCreationGranularity: 'single'
307
- }
308
- };
309
-
310
- expect(() => AppConfigSchema.parse(config)).not.toThrow();
311
- });
312
-
313
- it('不正なURL形式を拒否する', () => {
314
- const config = {
315
- confluence: {
316
- url: 'not-a-url',
317
- username: 'user@example.com',
318
- apiToken: 'token123'
319
- }
320
- };
321
-
322
- expect(() => AppConfigSchema.parse(config)).toThrow();
323
- });
324
-
325
- it('pageCreationGranularityの不正な値を拒否する', () => {
326
- const config = {
327
- confluence: {
328
- url: 'https://example.atlassian.net',
329
- pageCreationGranularity: 'invalid-value'
330
- }
331
- };
332
-
333
- expect(() => AppConfigSchema.parse(config)).toThrow();
334
- });
335
- });
336
-
337
- describe('JIRA設定', () => {
338
- it('有効なJIRA設定を受け入れる', () => {
339
- const config = {
340
- jira: {
341
- url: 'https://example.atlassian.net',
342
- username: 'user@example.com',
343
- apiToken: 'token123',
344
- project: 'MYPROJ',
345
- createEpic: true,
346
- storyCreationGranularity: 'all'
347
- }
348
- };
349
-
350
- expect(() => AppConfigSchema.parse(config)).not.toThrow();
351
- });
352
- });
353
-
354
- describe('GitHub設定', () => {
355
- it('有効なGitHub設定を受け入れる', () => {
356
- const config = {
357
- github: {
358
- token: 'ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
359
- username: 'octocat',
360
- email: 'octocat@github.com',
361
- org: 'myorg'
362
- }
363
- };
364
-
365
- expect(() => AppConfigSchema.parse(config)).not.toThrow();
366
- });
367
-
368
- it('トークンの形式を検証する', () => {
369
- const config = {
370
- github: {
371
- token: 'invalid-token-format',
372
- username: 'octocat'
373
- }
374
- };
375
-
376
- expect(() => AppConfigSchema.parse(config)).toThrow();
377
- });
378
- });
379
- });
380
- ```
381
-
382
- ### 8.3 統合テスト
383
-
384
- #### 8.3.1 コマンドラインテスト
385
-
386
- **ファイル**: `src/__tests__/integration/cli.test.ts`
387
-
388
- ```typescript
389
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
390
- import { execSync } from 'child_process';
391
- import { mkdirSync, writeFileSync, rmSync, existsSync } from 'fs';
392
- import { tmpdir } from 'os';
393
- import { join } from 'path';
394
-
395
- describe('CLI統合テスト', () => {
396
- let testDir: string;
397
-
398
- beforeEach(() => {
399
- testDir = join(tmpdir(), `michi-cli-test-${Date.now()}`);
400
- mkdirSync(testDir, { recursive: true });
401
- process.chdir(testDir);
402
- });
403
-
404
- afterEach(() => {
405
- rmSync(testDir, { recursive: true, force: true });
406
- });
407
-
408
- describe('michi init', () => {
409
- it('新規プロジェクトを初期化できる', () => {
410
- // Git リポジトリを初期化
411
- execSync('git init', { cwd: testDir });
412
- execSync('git config user.name "Test User"', { cwd: testDir });
413
- execSync('git config user.email "test@example.com"', { cwd: testDir });
414
-
415
- // michi init を実行(対話的入力をスキップするため --yes オプションを使用)
416
- const output = execSync('michi init --yes --project-id test-project', {
417
- cwd: testDir,
418
- encoding: 'utf-8'
419
- });
420
-
421
- expect(output).toContain('プロジェクトの初期化が完了しました');
422
- expect(existsSync(join(testDir, '.michi', 'project.json'))).toBe(true);
423
- expect(existsSync(join(testDir, '.env'))).toBe(true);
424
- });
425
-
426
- it('既存プロジェクトにMichiを追加できる (--existing)', () => {
427
- // 既存のGitリポジトリをシミュレート
428
- execSync('git init', { cwd: testDir });
429
- execSync('git remote add origin https://github.com/test/repo.git', { cwd: testDir });
430
-
431
- const output = execSync('michi init --existing --yes', {
432
- cwd: testDir,
433
- encoding: 'utf-8'
434
- });
435
-
436
- expect(output).toContain('既存プロジェクトへの追加が完了しました');
437
-
438
- // project.json の repository が自動設定される
439
- const projectJson = JSON.parse(
440
- readFileSync(join(testDir, '.michi', 'project.json'), 'utf-8')
441
- );
442
- expect(projectJson.repository).toBe('https://github.com/test/repo.git');
443
- });
444
-
445
- it('Gitリポジトリがない場合、--existing でエラーになる', () => {
446
- expect(() => {
447
- execSync('michi init --existing --yes', {
448
- cwd: testDir,
449
- encoding: 'utf-8'
450
- });
451
- }).toThrow();
452
- });
453
- });
454
-
455
- describe('michi migrate', () => {
456
- beforeEach(() => {
457
- // 旧形式の設定ファイルを作成
458
- mkdirSync(join(testDir, '.michi'), { recursive: true });
459
-
460
- writeFileSync(join(testDir, '.env'), `
461
- CONFLUENCE_URL=https://example.atlassian.net
462
- CONFLUENCE_USERNAME=user@example.com
463
- CONFLUENCE_API_TOKEN=token123
464
- JIRA_URL=https://example.atlassian.net
465
- JIRA_USERNAME=user@example.com
466
- JIRA_API_TOKEN=token456
467
- GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
468
- GITHUB_USERNAME=testuser
469
- GITHUB_EMAIL=test@example.com
470
- GITHUB_ORG=myorg
471
- GITHUB_REPO=myorg/myrepo
472
- `.trim());
473
-
474
- writeFileSync(join(testDir, '.michi', 'project.json'), JSON.stringify({
475
- projectId: 'test-project'
476
- }, null, 2));
477
- });
478
-
479
- it('設定を正しく移行する', () => {
480
- const output = execSync('michi migrate --force', {
481
- cwd: testDir,
482
- encoding: 'utf-8',
483
- env: { ...process.env, HOME: testDir }
484
- });
485
-
486
- expect(output).toContain('設定の移行が完了しました');
487
-
488
- // グローバル .env が作成される
489
- const globalEnv = join(testDir, '.michi', '.env');
490
- expect(existsSync(globalEnv)).toBe(true);
491
-
492
- const globalContent = readFileSync(globalEnv, 'utf-8');
493
- expect(globalContent).toContain('CONFLUENCE_URL=');
494
- expect(globalContent).toContain('JIRA_URL=');
495
- expect(globalContent).toContain('GITHUB_TOKEN=');
496
-
497
- // プロジェクト .env から組織設定が削除される
498
- const projectEnv = readFileSync(join(testDir, '.env'), 'utf-8');
499
- expect(projectEnv).not.toContain('CONFLUENCE_URL=');
500
- expect(projectEnv).not.toContain('GITHUB_REPO=');
501
-
502
- // project.json に repository が追加される
503
- const projectJson = JSON.parse(
504
- readFileSync(join(testDir, '.michi', 'project.json'), 'utf-8')
505
- );
506
- expect(projectJson.repository).toBe('https://github.com/myorg/myrepo.git');
507
- });
508
-
509
- it('--dry-run で変更をシミュレートする', () => {
510
- const output = execSync('michi migrate --dry-run', {
511
- cwd: testDir,
512
- encoding: 'utf-8',
513
- env: { ...process.env, HOME: testDir }
514
- });
515
-
516
- expect(output).toContain('ドライランモード');
517
- expect(output).toContain('実際の変更は行われませんでした');
518
-
519
- // ファイルが変更されていないことを確認
520
- const globalEnv = join(testDir, '.michi', '.env');
521
- expect(existsSync(globalEnv)).toBe(false);
522
- });
523
-
524
- it('バックアップを作成する', () => {
525
- execSync('michi migrate --force', {
526
- cwd: testDir,
527
- encoding: 'utf-8',
528
- env: { ...process.env, HOME: testDir }
529
- });
530
-
531
- // バックアップディレクトリが作成されている
532
- const backups = readdirSync(testDir).filter(f => f.startsWith('.michi-backup-'));
533
- expect(backups.length).toBeGreaterThan(0);
534
- });
535
- });
536
-
537
- describe('michi config:validate', () => {
538
- it('有効な設定を検証する', () => {
539
- // グローバル設定
540
- mkdirSync(join(testDir, '.michi'), { recursive: true });
541
- writeFileSync(join(testDir, '.michi', '.env'), `
542
- CONFLUENCE_URL=https://example.atlassian.net
543
- JIRA_URL=https://example.atlassian.net
544
- GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
545
- `.trim());
546
-
547
- // プロジェクト設定
548
- const projectDir = join(testDir, 'project');
549
- mkdirSync(join(projectDir, '.michi'), { recursive: true });
550
- writeFileSync(join(projectDir, '.michi', 'project.json'), JSON.stringify({
551
- projectId: 'test-project',
552
- repository: 'https://github.com/myorg/myrepo.git'
553
- }, null, 2));
554
-
555
- const output = execSync('michi config:validate', {
556
- cwd: projectDir,
557
- encoding: 'utf-8',
558
- env: { ...process.env, HOME: testDir }
559
- });
560
-
561
- expect(output).toContain('設定ファイルは有効です');
562
- });
563
-
564
- it('不正な設定でエラーを表示する', () => {
565
- mkdirSync(join(testDir, '.michi'), { recursive: true });
566
- writeFileSync(join(testDir, '.env'), `
567
- CONFLUENCE_URL=not-a-url
568
- `.trim());
569
-
570
- expect(() => {
571
- execSync('michi config:validate', {
572
- cwd: testDir,
573
- encoding: 'utf-8'
574
- });
575
- }).toThrow();
576
- });
577
- });
578
- });
579
- ```
580
-
581
- #### 8.3.2 外部API連携テスト
582
-
583
- **ファイル**: `src/__tests__/integration/external-api.test.ts`
584
-
585
- ```typescript
586
- import { describe, it, expect, beforeAll } from 'vitest';
587
- import { ConfigLoader } from '../../config/config-loader.js';
588
- import { ConfluenceClient } from '../../confluence/client.js';
589
- import { JiraClient } from '../../jira/client.js';
590
- import { GitHubClient } from '../../github/client.js';
591
-
592
- // 注: これらのテストは実際のAPIキーが必要なため、CI環境では skip される
593
- // ローカルで実行する場合は、~/.michi/.env に実際の認証情報を設定すること
594
-
595
- describe('外部API連携テスト', () => {
596
- let config: ReturnType<ConfigLoader['load']>;
597
-
598
- beforeAll(() => {
599
- const loader = new ConfigLoader(process.cwd());
600
- config = loader.load();
601
- });
602
-
603
- describe('Confluence連携', () => {
604
- it.skipIf(!process.env.RUN_INTEGRATION_TESTS)(
605
- 'Confluenceに接続できる',
606
- async () => {
607
- const client = new ConfluenceClient(config);
608
- const spaces = await client.getSpaces();
609
-
610
- expect(Array.isArray(spaces)).toBe(true);
611
- }
612
- );
613
-
614
- it.skipIf(!process.env.RUN_INTEGRATION_TESTS)(
615
- 'スペース情報を取得できる',
616
- async () => {
617
- const client = new ConfluenceClient(config);
618
- const space = await client.getSpace(config.confluence!.space!);
619
-
620
- expect(space).toBeDefined();
621
- expect(space.key).toBe(config.confluence!.space);
622
- }
623
- );
624
- });
625
-
626
- describe('JIRA連携', () => {
627
- it.skipIf(!process.env.RUN_INTEGRATION_TESTS)(
628
- 'JIRAに接続できる',
629
- async () => {
630
- const client = new JiraClient(config);
631
- const projects = await client.getProjects();
632
-
633
- expect(Array.isArray(projects)).toBe(true);
634
- }
635
- );
636
-
637
- it.skipIf(!process.env.RUN_INTEGRATION_TESTS)(
638
- 'プロジェクト情報を取得できる',
639
- async () => {
640
- const client = new JiraClient(config);
641
- const project = await client.getProject(config.jira!.project!);
642
-
643
- expect(project).toBeDefined();
644
- expect(project.key).toBe(config.jira!.project);
645
- }
646
- );
647
- });
648
-
649
- describe('GitHub連携', () => {
650
- it.skipIf(!process.env.RUN_INTEGRATION_TESTS)(
651
- 'GitHubに接続できる',
652
- async () => {
653
- const client = new GitHubClient(config);
654
- const user = await client.getCurrentUser();
655
-
656
- expect(user).toBeDefined();
657
- expect(user.login).toBe(config.github!.username);
658
- }
659
- );
660
-
661
- it.skipIf(!process.env.RUN_INTEGRATION_TESTS)(
662
- 'リポジトリ情報を取得できる',
663
- async () => {
664
- const client = new GitHubClient(config);
665
- const repo = await client.getRepository();
666
-
667
- expect(repo).toBeDefined();
668
- expect(repo.full_name).toBe(config.github!.repositoryShort);
669
- }
670
- );
671
- });
672
- });
673
- ```
674
-
675
- ### 8.4 E2Eテスト
676
-
677
- #### 8.4.1 完全なワークフローテスト
678
-
679
- **ファイル**: `src/__tests__/e2e/full-workflow.test.ts`
680
-
681
- ```typescript
682
- import { describe, it, expect, beforeAll, afterAll } from 'vitest';
683
- import { execSync } from 'child_process';
684
- import { mkdirSync, rmSync } from 'fs';
685
- import { tmpdir } from 'os';
686
- import { join } from 'path';
687
-
688
- describe('E2E: 完全なワークフロー', () => {
689
- let testDir: string;
690
- let projectDir: string;
691
-
692
- beforeAll(() => {
693
- testDir = join(tmpdir(), `michi-e2e-${Date.now()}`);
694
- projectDir = join(testDir, 'my-project');
695
-
696
- mkdirSync(projectDir, { recursive: true });
697
- process.chdir(projectDir);
698
-
699
- // Git リポジトリを初期化
700
- execSync('git init', { cwd: projectDir });
701
- execSync('git config user.name "Test User"', { cwd: projectDir });
702
- execSync('git config user.email "test@example.com"', { cwd: projectDir });
703
- });
704
-
705
- afterAll(() => {
706
- rmSync(testDir, { recursive: true, force: true });
707
- });
708
-
709
- it('新規プロジェクト作成 → 設定 → Confluence同期', async () => {
710
- // ステップ1: プロジェクト初期化
711
- execSync('michi init --yes --project-id my-project', {
712
- cwd: projectDir,
713
- encoding: 'utf-8'
714
- });
715
-
716
- // ステップ2: グローバル設定を作成
717
- execSync('npm run config:global', {
718
- cwd: projectDir,
719
- input: 'n\nn\nn\n', // すべての設定をスキップ
720
- encoding: 'utf-8',
721
- env: { ...process.env, HOME: testDir }
722
- });
723
-
724
- // ステップ3: Confluence同期(ドライラン)
725
- const output = execSync(
726
- 'michi confluence:sync test-feature requirements --dry-run',
727
- {
728
- cwd: projectDir,
729
- encoding: 'utf-8',
730
- env: { ...process.env, HOME: testDir }
731
- }
732
- );
733
-
734
- expect(output).toContain('ドライランモード');
735
- }, 30000); // 30秒タイムアウト
736
-
737
- it('既存プロジェクト → 移行 → 検証', async () => {
738
- // ステップ1: 旧形式の設定を作成
739
- mkdirSync(join(projectDir, '.michi'), { recursive: true });
740
- writeFileSync(join(projectDir, '.env'), `
741
- CONFLUENCE_URL=https://example.atlassian.net
742
- GITHUB_REPO=myorg/myrepo
743
- `.trim());
744
-
745
- // ステップ2: 移行実行
746
- execSync('michi migrate --force', {
747
- cwd: projectDir,
748
- encoding: 'utf-8',
749
- env: { ...process.env, HOME: testDir }
750
- });
751
-
752
- // ステップ3: 設定検証
753
- const output = execSync('michi config:validate', {
754
- cwd: projectDir,
755
- encoding: 'utf-8',
756
- env: { ...process.env, HOME: testDir }
757
- });
758
-
759
- expect(output).toContain('設定ファイルは有効です');
760
- }, 30000);
761
- });
762
- ```
763
-
764
- ### 8.5 カバレッジ目標
765
-
766
- #### 8.5.1 目標値
767
-
768
- | カテゴリ | 目標カバレッジ | 現在値 | 期限 |
769
- |---------|-------------|--------|------|
770
- | **全体** | 95% | TBD | リリース前 |
771
- | **ConfigLoader** | 100% | TBD | Week 1 |
772
- | **バリデーション** | 100% | TBD | Week 1 |
773
- | **CLIコマンド** | 90% | TBD | Week 2 |
774
- | **マイグレーション** | 95% | TBD | Week 2 |
775
- | **外部API** | 80% | TBD | Week 3 |
776
-
777
- #### 8.5.2 カバレッジ計測
778
-
779
- ```bash
780
- # カバレッジレポートの生成
781
- npm run test:coverage
782
-
783
- # HTMLレポートの確認
784
- open coverage/index.html
785
-
786
- # 最小カバレッジのチェック(CI用)
787
- npm run test:coverage -- --min-coverage=95
788
- ```
789
-
790
- #### 8.5.3 除外項目
791
-
792
- 以下のファイルはカバレッジ計測から除外します:
793
-
794
- - テストファイル: `**/*.test.ts`, `**/*.spec.ts`
795
- - 型定義ファイル: `**/*.d.ts`
796
- - 設定ファイル: `**/config/**/*.ts` (一部)
797
- - CLIエントリポイント: `src/cli.ts` (メイン関数のみ)
798
-
799
- ### 8.6 テスト実行
800
-
801
- #### 8.6.1 ローカルでのテスト実行
802
-
803
- ```bash
804
- # すべてのテストを実行
805
- npm run test
806
-
807
- # 単体テストのみ
808
- npm run test src/config/__tests__
809
-
810
- # 統合テストのみ
811
- npm run test src/__tests__/integration
812
-
813
- # E2Eテストのみ
814
- npm run test src/__tests__/e2e
815
-
816
- # ウォッチモード(開発時)
817
- npm run test -- --watch
818
-
819
- # 特定のファイルのみ
820
- npm run test src/config/__tests__/config-loader.test.ts
821
- ```
822
-
823
- #### 8.6.2 CI/CDでのテスト実行
824
-
825
- **GitHub Actions**: `.github/workflows/test.yml`
826
-
827
- ```yaml
828
- name: Test
829
-
830
- on:
831
- push:
832
- branches: [main, develop]
833
- pull_request:
834
- branches: [main, develop]
835
-
836
- jobs:
837
- test:
838
- runs-on: ubuntu-latest
839
-
840
- steps:
841
- - uses: actions/checkout@v4
842
-
843
- - name: Setup Node.js
844
- uses: actions/setup-node@v4
845
- with:
846
- node-version: '20'
847
- cache: 'npm'
848
-
849
- - name: Install dependencies
850
- run: npm ci
851
-
852
- - name: Run unit tests
853
- run: npm run test:run
854
-
855
- - name: Run integration tests
856
- run: npm run test:run src/__tests__/integration
857
- env:
858
- RUN_INTEGRATION_TESTS: 'false' # CI環境では外部APIテストをスキップ
859
-
860
- - name: Generate coverage report
861
- run: npm run test:coverage
862
-
863
- - name: Upload coverage to Codecov
864
- uses: codecov/codecov-action@v3
865
- with:
866
- files: ./coverage/coverage-final.json
867
-
868
- - name: Check coverage threshold
869
- run: npm run test:coverage -- --min-coverage=95
870
- ```
871
-
872
- #### 8.6.3 テスト実行のベストプラクティス
873
-
874
- 1. **開発時**
875
- - ウォッチモードで関連するテストのみを実行
876
- - 変更したファイルに対応するテストを優先
877
-
878
- 2. **コミット前**
879
- - すべての単体テストを実行
880
- - 関連する統合テストを実行
881
-
882
- 3. **PR作成時**
883
- - すべてのテストを実行
884
- - カバレッジレポートを確認
885
-
886
- 4. **リリース前**
887
- - すべてのテストを実行(E2E含む)
888
- - 外部APIテストを手動で実行
889
- - カバレッジが目標値を満たしているか確認
890
-
891
- ---
892
-