@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.
- package/README.md +71 -848
- package/dist/scripts/constants/environments.d.ts +1 -1
- package/dist/scripts/constants/environments.d.ts.map +1 -1
- package/dist/scripts/constants/environments.js +0 -20
- package/dist/scripts/constants/environments.js.map +1 -1
- package/dist/scripts/phase-runner.js +1 -1
- package/dist/scripts/phase-runner.js.map +1 -1
- package/dist/scripts/utils/multi-repo-validator.d.ts +18 -0
- package/dist/scripts/utils/multi-repo-validator.d.ts.map +1 -1
- package/dist/scripts/utils/multi-repo-validator.js +42 -0
- package/dist/scripts/utils/multi-repo-validator.js.map +1 -1
- package/dist/scripts/utils/tasks-format-validator.js +3 -3
- package/dist/scripts/utils/tasks-format-validator.js.map +1 -1
- package/dist/scripts/utils/template-finder.d.ts +2 -2
- package/dist/scripts/utils/template-finder.d.ts.map +1 -1
- package/dist/scripts/utils/template-finder.js +3 -8
- package/dist/scripts/utils/template-finder.js.map +1 -1
- package/dist/src/cli.d.ts.map +1 -1
- package/dist/src/cli.js +0 -8
- package/dist/src/cli.js.map +1 -1
- package/dist/src/commands/init.d.ts +0 -4
- package/dist/src/commands/init.d.ts.map +1 -1
- package/dist/src/commands/init.js +6 -30
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/setup-existing.d.ts +2 -6
- package/dist/src/commands/setup-existing.d.ts.map +1 -1
- package/dist/src/commands/setup-existing.js +8 -142
- package/dist/src/commands/setup-existing.js.map +1 -1
- package/docs/README.md +20 -83
- package/docs/getting-started/configuration.md +350 -0
- package/docs/getting-started/installation.md +59 -0
- package/docs/getting-started/quick-start.md +76 -0
- package/docs/guides/atlassian-integration.md +116 -0
- package/docs/guides/claude-code.md +155 -0
- package/docs/guides/multi-repo.md +117 -0
- package/docs/guides/workflow.md +382 -0
- package/docs/reference/ai-commands.md +92 -0
- package/docs/reference/cli.md +752 -0
- package/docs/reference/environment-variables.md +192 -0
- package/docs/troubleshooting.md +498 -0
- package/package.json +1 -3
- package/scripts/__tests__/create-project.test.ts +12 -12
- package/scripts/__tests__/setup-existing-project.test.ts +22 -22
- package/scripts/constants/__tests__/environments.test.ts +7 -50
- package/scripts/constants/environments.ts +1 -27
- package/scripts/phase-runner.ts +1 -1
- package/scripts/template/__tests__/renderer.test.ts +21 -21
- package/scripts/utils/__tests__/multi-repo-validator.test.ts +159 -1
- package/scripts/utils/multi-repo-validator.ts +50 -0
- package/scripts/utils/tasks-format-validator.ts +3 -3
- package/scripts/utils/template-finder.ts +5 -11
- package/templates/claude/agents/e2e-first-planner/AGENT.md +1 -1
- package/templates/claude/agents/pr-resolver/AGENT.md +15 -3
- package/templates/claude/commands/michi/e2e-plan.md +1 -1
- package/templates/claude/commands/michi/spec-design.md +2 -2
- package/templates/claude/commands/michi/spec-tasks.md +156 -0
- package/templates/claude/commands/michi/test-planning.md +1 -1
- package/templates/claude/commands/michi/validate-design.md +3 -3
- package/templates/claude/commands/michi-multi-repo/impl-all.md +30 -1
- package/templates/claude/commands/michi-multi-repo/propagate-specs.md +14 -1
- package/templates/claude/commands/michi-multi-repo/spec-review.md +16 -2
- package/templates/claude-agent/agents/repo-spec-executor.md +1 -1
- package/templates/claude-agent/commands/michi/spec-tasks.md +117 -0
- package/templates/claude-agent/rules/code-size-monitor.md +26 -0
- package/templates/claude-agent/rules/code-size-rules.md +32 -0
- package/templates/michi/cc-sdd-overrides/settings/rules/design-review-michi.md +1 -1
- package/docs/context.md +0 -59
- package/docs/michi-development/contributing/development.md +0 -341
- package/docs/michi-development/contributing/release.md +0 -365
- package/docs/michi-development/design/config-unification.md +0 -733
- package/docs/michi-development/design/design-config-current-state.md +0 -330
- package/docs/michi-development/design/design-config-implementation.md +0 -628
- package/docs/michi-development/design/design-config-migration.md +0 -952
- package/docs/michi-development/design/design-config-security.md +0 -771
- package/docs/michi-development/design/design-config-solution.md +0 -583
- package/docs/michi-development/design/design-config-testing.md +0 -892
- package/docs/michi-development/testing/manual-verification-flow.md +0 -871
- package/docs/michi-development/testing/manual-verification-other-tools.md +0 -1279
- package/docs/michi-development/testing/manual-verification-troubleshooting.md +0 -122
- package/docs/michi-development/testing/pre-publish-checklist.md +0 -560
- package/docs/michi-development/testing-strategy.md +0 -87
- package/docs/plan.md +0 -275
- package/docs/user-guide/getting-started/github-token-setup.md +0 -510
- package/docs/user-guide/getting-started/new-repository-setup.md +0 -704
- package/docs/user-guide/getting-started/quick-start.md +0 -212
- package/docs/user-guide/getting-started/setup.md +0 -819
- package/docs/user-guide/guides/agent-skills-integration.md +0 -222
- package/docs/user-guide/guides/customization.md +0 -537
- package/docs/user-guide/guides/internationalization.md +0 -540
- package/docs/user-guide/guides/migration-guide.md +0 -138
- package/docs/user-guide/guides/multi-project.md +0 -368
- package/docs/user-guide/guides/multi-repo-guide.md +0 -1590
- package/docs/user-guide/guides/phase-automation.md +0 -419
- package/docs/user-guide/guides/workflow.md +0 -574
- package/docs/user-guide/hands-on/README.md +0 -142
- package/docs/user-guide/hands-on/claude-agent-setup.md +0 -597
- package/docs/user-guide/hands-on/claude-setup.md +0 -452
- package/docs/user-guide/hands-on/cursor-setup.md +0 -353
- package/docs/user-guide/hands-on/troubleshooting.md +0 -964
- package/docs/user-guide/hands-on/verification-checklist.md +0 -439
- package/docs/user-guide/hands-on/workflow-walkthrough.md +0 -1078
- package/docs/user-guide/reference/config.md +0 -589
- package/docs/user-guide/reference/multi-repo-api.md +0 -771
- package/docs/user-guide/reference/quick-reference.md +0 -297
- package/docs/user-guide/reference/security-test-payloads.md +0 -50
- package/docs/user-guide/reference/tasks-template.md +0 -550
- package/docs/user-guide/release/ci-setup-java.md +0 -114
- package/docs/user-guide/release/ci-setup-nodejs.md +0 -94
- package/docs/user-guide/release/ci-setup-php.md +0 -102
- package/docs/user-guide/release/ci-setup-troubleshooting.md +0 -94
- package/docs/user-guide/release/ci-setup.md +0 -188
- package/docs/user-guide/release/release-flow.md +0 -476
- package/docs/user-guide/templates/test-specs/README.md +0 -173
- package/docs/user-guide/templates/test-specs/e2e-test-spec-template.md +0 -553
- package/docs/user-guide/templates/test-specs/integration-test-spec-template.md +0 -435
- package/docs/user-guide/templates/test-specs/performance-test-spec-template.md +0 -454
- package/docs/user-guide/templates/test-specs/security-test-spec-template.md +0 -625
- package/docs/user-guide/templates/test-specs/unit-test-spec-template.md +0 -328
- package/docs/user-guide/testing/integration-tests.md +0 -312
- package/docs/user-guide/testing/tdd-cycle.md +0 -349
- package/docs/user-guide/testing/test-execution-flow.md +0 -396
- package/docs/user-guide/testing/test-failure-handling.md +0 -521
- package/docs/user-guide/testing/test-planning-flow.md +0 -185
- package/docs/user-guide/testing-strategy.md +0 -185
- package/docs/verification-guide.md +0 -518
- package/templates/cline/rules/atlassian-integration.md +0 -36
- package/templates/cline/rules/michi-core.md +0 -56
- package/templates/codex/AGENTS.override.md +0 -277
- package/templates/codex/prompts/confluence-sync.md +0 -177
- package/templates/codex/rules/README.md +0 -210
- package/templates/cursor/commands/kiro/kiro-spec-impl.md +0 -244
- package/templates/cursor/commands/kiro/kiro-spec-tasks.md +0 -354
- package/templates/cursor/commands/michi/confluence-sync.md +0 -76
- package/templates/cursor/commands/michi/project-switch.md +0 -69
- package/templates/cursor/rules/atlassian-mcp.mdc +0 -188
- package/templates/cursor/rules/github-ssot.mdc +0 -151
- package/templates/cursor/rules/multi-project.mdc +0 -81
- package/templates/gemini/commands/README.md +0 -41
- 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
|
-
|