@sk8metal/michi-cli 0.0.1 → 0.0.2
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/CHANGELOG.md +24 -0
- package/README.md +24 -24
- package/dist/scripts/__tests__/validate-phase.test.d.ts +5 -0
- package/dist/scripts/__tests__/validate-phase.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/validate-phase.test.js +162 -0
- package/dist/scripts/__tests__/validate-phase.test.js.map +1 -0
- package/dist/scripts/utils/__tests__/config-validator.test.d.ts +5 -0
- package/dist/scripts/utils/__tests__/config-validator.test.d.ts.map +1 -0
- package/dist/scripts/utils/__tests__/config-validator.test.js +247 -0
- package/dist/scripts/utils/__tests__/config-validator.test.js.map +1 -0
- package/dist/scripts/utils/__tests__/feature-name-validator.test.d.ts +5 -0
- package/dist/scripts/utils/__tests__/feature-name-validator.test.d.ts.map +1 -0
- package/dist/scripts/utils/__tests__/feature-name-validator.test.js +106 -0
- package/dist/scripts/utils/__tests__/feature-name-validator.test.js.map +1 -0
- package/dist/src/__tests__/cli.test.d.ts +5 -0
- package/dist/src/__tests__/cli.test.d.ts.map +1 -0
- package/dist/src/__tests__/cli.test.js +58 -0
- package/dist/src/__tests__/cli.test.js.map +1 -0
- package/docs/setup.md +1 -1
- package/package.json +5 -3
- package/scripts/__tests__/README.md +101 -0
- package/scripts/__tests__/validate-phase.test.ts +185 -0
- package/scripts/config/config-schema.ts +130 -0
- package/scripts/config/default-config.json +57 -0
- package/scripts/config-interactive.ts +494 -0
- package/scripts/confluence-sync.ts +503 -0
- package/scripts/create-project.ts +293 -0
- package/scripts/jira-sync.ts +644 -0
- package/scripts/list-projects.ts +85 -0
- package/scripts/markdown-to-confluence.ts +161 -0
- package/scripts/multi-project-estimate.ts +255 -0
- package/scripts/phase-runner.ts +303 -0
- package/scripts/pr-automation.ts +67 -0
- package/scripts/pre-flight-check.ts +285 -0
- package/scripts/resource-dashboard.ts +124 -0
- package/scripts/setup-env.sh +52 -0
- package/scripts/setup-existing-project.ts +381 -0
- package/scripts/setup-existing.sh +145 -0
- package/scripts/utils/__tests__/config-validator.test.ts +302 -0
- package/scripts/utils/__tests__/feature-name-validator.test.ts +129 -0
- package/scripts/utils/config-loader.ts +326 -0
- package/scripts/utils/config-validator.ts +347 -0
- package/scripts/utils/confluence-hierarchy.ts +854 -0
- package/scripts/utils/feature-name-validator.ts +135 -0
- package/scripts/utils/project-meta.ts +69 -0
- package/scripts/validate-phase.ts +279 -0
- package/scripts/workflow-orchestrator.ts +178 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* feature-name-validator.ts のテスト
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it, expect } from 'vitest';
|
|
5
|
+
import { validateFeatureName, suggestFeatureName } from '../feature-name-validator.js';
|
|
6
|
+
describe('validateFeatureName', () => {
|
|
7
|
+
describe('有効なfeature名', () => {
|
|
8
|
+
it('小文字の英数字とハイフンのみ', () => {
|
|
9
|
+
const result = validateFeatureName('user-auth');
|
|
10
|
+
expect(result.valid).toBe(true);
|
|
11
|
+
expect(result.errors).toHaveLength(0);
|
|
12
|
+
});
|
|
13
|
+
it('単一単語', () => {
|
|
14
|
+
const result = validateFeatureName('payment');
|
|
15
|
+
expect(result.valid).toBe(true);
|
|
16
|
+
expect(result.errors).toHaveLength(0);
|
|
17
|
+
});
|
|
18
|
+
it('複数単語(ハイフン区切り)', () => {
|
|
19
|
+
const result = validateFeatureName('health-check-endpoint');
|
|
20
|
+
expect(result.valid).toBe(true);
|
|
21
|
+
expect(result.errors).toHaveLength(0);
|
|
22
|
+
});
|
|
23
|
+
it('数字を含む', () => {
|
|
24
|
+
const result = validateFeatureName('api-v2');
|
|
25
|
+
expect(result.valid).toBe(true);
|
|
26
|
+
expect(result.errors).toHaveLength(0);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
describe('無効なfeature名', () => {
|
|
30
|
+
it('大文字を含む', () => {
|
|
31
|
+
const result = validateFeatureName('UserAuth');
|
|
32
|
+
expect(result.valid).toBe(false);
|
|
33
|
+
expect(result.errors.some(e => e.includes('大文字'))).toBe(true);
|
|
34
|
+
});
|
|
35
|
+
it('日本語を含む', () => {
|
|
36
|
+
const result = validateFeatureName('ユーザー認証');
|
|
37
|
+
expect(result.valid).toBe(false);
|
|
38
|
+
expect(result.errors.some(e => e.includes('日本語'))).toBe(true);
|
|
39
|
+
});
|
|
40
|
+
it('アンダースコアを含む', () => {
|
|
41
|
+
const result = validateFeatureName('user_auth');
|
|
42
|
+
expect(result.valid).toBe(false);
|
|
43
|
+
expect(result.errors.some(e => e.includes('アンダースコア'))).toBe(true);
|
|
44
|
+
});
|
|
45
|
+
it('スペースを含む', () => {
|
|
46
|
+
const result = validateFeatureName('user auth');
|
|
47
|
+
expect(result.valid).toBe(false);
|
|
48
|
+
expect(result.errors.some(e => e.includes('スペース'))).toBe(true);
|
|
49
|
+
});
|
|
50
|
+
it('先頭にハイフン', () => {
|
|
51
|
+
const result = validateFeatureName('-user-auth');
|
|
52
|
+
expect(result.valid).toBe(false);
|
|
53
|
+
expect(result.errors.some(e => e.includes('先頭'))).toBe(true);
|
|
54
|
+
});
|
|
55
|
+
it('末尾にハイフン', () => {
|
|
56
|
+
const result = validateFeatureName('user-auth-');
|
|
57
|
+
expect(result.valid).toBe(false);
|
|
58
|
+
expect(result.errors.some(e => e.includes('末尾'))).toBe(true);
|
|
59
|
+
});
|
|
60
|
+
it('連続したハイフン', () => {
|
|
61
|
+
const result = validateFeatureName('user--auth');
|
|
62
|
+
expect(result.valid).toBe(false);
|
|
63
|
+
expect(result.errors.some(e => e.includes('連続'))).toBe(true);
|
|
64
|
+
});
|
|
65
|
+
it('空文字', () => {
|
|
66
|
+
const result = validateFeatureName('');
|
|
67
|
+
expect(result.valid).toBe(false);
|
|
68
|
+
expect(result.errors.some(e => e.includes('空'))).toBe(true);
|
|
69
|
+
});
|
|
70
|
+
it('特殊文字を含む', () => {
|
|
71
|
+
const result = validateFeatureName('user@auth');
|
|
72
|
+
expect(result.valid).toBe(false);
|
|
73
|
+
expect(result.errors.some(e => e.includes('使用できない文字'))).toBe(true);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
describe('suggestFeatureName', () => {
|
|
78
|
+
it('大文字を小文字に変換', () => {
|
|
79
|
+
expect(suggestFeatureName('UserAuth')).toBe('userauth');
|
|
80
|
+
});
|
|
81
|
+
it('アンダースコアをハイフンに変換', () => {
|
|
82
|
+
expect(suggestFeatureName('user_auth')).toBe('user-auth');
|
|
83
|
+
});
|
|
84
|
+
it('スペースをハイフンに変換', () => {
|
|
85
|
+
expect(suggestFeatureName('user auth')).toBe('user-auth');
|
|
86
|
+
});
|
|
87
|
+
it('日本語を削除', () => {
|
|
88
|
+
expect(suggestFeatureName('ユーザーuser認証auth')).toBe('userauth');
|
|
89
|
+
});
|
|
90
|
+
it('複数のスペースを1つのハイフンに', () => {
|
|
91
|
+
expect(suggestFeatureName('user auth')).toBe('user-auth');
|
|
92
|
+
});
|
|
93
|
+
it('先頭・末尾のハイフンを削除', () => {
|
|
94
|
+
expect(suggestFeatureName('-user-auth-')).toBe('user-auth');
|
|
95
|
+
});
|
|
96
|
+
it('連続ハイフンを1つに', () => {
|
|
97
|
+
expect(suggestFeatureName('user--auth')).toBe('user-auth');
|
|
98
|
+
});
|
|
99
|
+
it('特殊文字を削除', () => {
|
|
100
|
+
expect(suggestFeatureName('user@#$auth')).toBe('userauth');
|
|
101
|
+
});
|
|
102
|
+
it('複合的な変換', () => {
|
|
103
|
+
expect(suggestFeatureName('User_Auth Feature!')).toBe('user-auth-feature');
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
//# sourceMappingURL=feature-name-validator.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feature-name-validator.test.js","sourceRoot":"","sources":["../../../../scripts/utils/__tests__/feature-name-validator.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/cli.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLIツールの単体テスト
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
5
|
+
import { Command } from 'commander';
|
|
6
|
+
import { createCLI } from '../cli.js';
|
|
7
|
+
describe('CLI Tool', () => {
|
|
8
|
+
let program;
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
program = createCLI();
|
|
11
|
+
});
|
|
12
|
+
describe('基本動作', () => {
|
|
13
|
+
it('CLIツールが正しく作成される', () => {
|
|
14
|
+
expect(program).toBeInstanceOf(Command);
|
|
15
|
+
expect(program.name()).toBe('michi');
|
|
16
|
+
});
|
|
17
|
+
it('すべてのコマンドが登録されている', () => {
|
|
18
|
+
const commandNames = program.commands.map(cmd => cmd.name());
|
|
19
|
+
expect(commandNames).toContain('jira:sync');
|
|
20
|
+
expect(commandNames).toContain('confluence:sync');
|
|
21
|
+
expect(commandNames).toContain('phase:run');
|
|
22
|
+
expect(commandNames).toContain('validate:phase');
|
|
23
|
+
expect(commandNames).toContain('preflight');
|
|
24
|
+
expect(commandNames).toContain('project:list');
|
|
25
|
+
expect(commandNames).toContain('project:dashboard');
|
|
26
|
+
expect(commandNames).toContain('workflow:run');
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
describe('jira:syncコマンド', () => {
|
|
30
|
+
it('コマンドが存在する', () => {
|
|
31
|
+
const command = program.commands.find(cmd => cmd.name() === 'jira:sync');
|
|
32
|
+
expect(command).toBeDefined();
|
|
33
|
+
expect(command?.description()).toContain('JIRA');
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
describe('confluence:syncコマンド', () => {
|
|
37
|
+
it('コマンドが存在する', () => {
|
|
38
|
+
const command = program.commands.find(cmd => cmd.name() === 'confluence:sync');
|
|
39
|
+
expect(command).toBeDefined();
|
|
40
|
+
expect(command?.description()).toContain('Confluence');
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
describe('phase:runコマンド', () => {
|
|
44
|
+
it('コマンドが存在する', () => {
|
|
45
|
+
const command = program.commands.find(cmd => cmd.name() === 'phase:run');
|
|
46
|
+
expect(command).toBeDefined();
|
|
47
|
+
expect(command?.description()).toContain('phase');
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
describe('validate:phaseコマンド', () => {
|
|
51
|
+
it('コマンドが存在する', () => {
|
|
52
|
+
const command = program.commands.find(cmd => cmd.name() === 'validate:phase');
|
|
53
|
+
expect(command).toBeDefined();
|
|
54
|
+
expect(command?.description()).toContain('Validate');
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
//# sourceMappingURL=cli.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.test.js","sourceRoot":"","sources":["../../../src/__tests__/cli.test.ts"],"names":[],"mappings":"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"}
|
package/docs/setup.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sk8metal/michi-cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"description": "Managed Intelligent Comprehensive Hub for Integration - AI-driven development workflow automation",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
},
|
|
10
10
|
"repository": {
|
|
11
11
|
"type": "git",
|
|
12
|
-
"url": "
|
|
12
|
+
"url": "https://github.com/sk8metalme/michi.git"
|
|
13
13
|
},
|
|
14
14
|
"homepage": "https://github.com/sk8metalme/michi#readme",
|
|
15
15
|
"bugs": {
|
|
@@ -29,12 +29,14 @@
|
|
|
29
29
|
"github"
|
|
30
30
|
],
|
|
31
31
|
"bin": {
|
|
32
|
-
"michi": "dist/src/cli.js"
|
|
32
|
+
"michi": "./dist/src/cli.js"
|
|
33
33
|
},
|
|
34
34
|
"files": [
|
|
35
35
|
"dist",
|
|
36
|
+
"scripts",
|
|
36
37
|
"docs",
|
|
37
38
|
"README.md",
|
|
39
|
+
"CHANGELOG.md",
|
|
38
40
|
"LICENSE",
|
|
39
41
|
"env.example",
|
|
40
42
|
"mcp.json.example"
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# スクリプト単体テスト
|
|
2
|
+
|
|
3
|
+
## 概要
|
|
4
|
+
|
|
5
|
+
このディレクトリには、`scripts/` 内の各スクリプトの単体テストが含まれています。
|
|
6
|
+
|
|
7
|
+
## テストファイル
|
|
8
|
+
|
|
9
|
+
- `validate-phase.test.ts`: フェーズバリデーションのテスト(7テストケース)
|
|
10
|
+
|
|
11
|
+
**注**: pre-flight-check.tsとjira-sync.tsは内部関数がexportされていないため、
|
|
12
|
+
統合テスト(E2E)でカバーする方針としました。
|
|
13
|
+
|
|
14
|
+
## テスト実行
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# すべてのテストを実行
|
|
18
|
+
npm test
|
|
19
|
+
|
|
20
|
+
# 特定のテストファイルのみ実行
|
|
21
|
+
npm test validate-phase.test.ts
|
|
22
|
+
|
|
23
|
+
# ウォッチモード(開発時)
|
|
24
|
+
npm test -- --watch
|
|
25
|
+
|
|
26
|
+
# カバレッジ付き
|
|
27
|
+
npm test -- --coverage
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## テスト戦略
|
|
31
|
+
|
|
32
|
+
### モック化
|
|
33
|
+
|
|
34
|
+
以下のモジュールをモック化:
|
|
35
|
+
- `fs`: ファイルシステム操作
|
|
36
|
+
- `axios`: HTTP API呼び出し
|
|
37
|
+
- `dotenv`: 環境変数読み込み
|
|
38
|
+
- `./utils/project-meta.js`: プロジェクトメタデータ
|
|
39
|
+
|
|
40
|
+
### テスト方針
|
|
41
|
+
|
|
42
|
+
**単体テスト**: validate-phase.ts のみ
|
|
43
|
+
- ✅ 基本動作(成功ケース、Confluence未作成)
|
|
44
|
+
- ✅ フェーズ固有の検証(前提条件、JIRA、営業日表記)
|
|
45
|
+
- ✅ エッジケース(不正なフェーズ名)
|
|
46
|
+
|
|
47
|
+
**統合テスト(E2E)**: その他のスクリプト
|
|
48
|
+
- pre-flight-check.ts: 実際の環境で実行して確認
|
|
49
|
+
- jira-sync.ts: 実際のJIRA APIで動作確認
|
|
50
|
+
- phase-runner.ts: エンドツーエンドで動作確認
|
|
51
|
+
|
|
52
|
+
**理由**:
|
|
53
|
+
- 内部関数をexportするリファクタリングは過剰
|
|
54
|
+
- 統合テストの方が実際の動作を保証できる
|
|
55
|
+
- テストのメンテナンスコストを削減
|
|
56
|
+
|
|
57
|
+
## テスト戦略の原則
|
|
58
|
+
|
|
59
|
+
### ✅ 単体テストを書くべき
|
|
60
|
+
|
|
61
|
+
- ビジネスロジック(validate-phase.ts)
|
|
62
|
+
- 純粋関数(引数→戻り値が明確)
|
|
63
|
+
- エッジケースが多い処理
|
|
64
|
+
|
|
65
|
+
### ❌ 単体テストを避けるべき
|
|
66
|
+
|
|
67
|
+
- API呼び出しが中心のスクリプト(pre-flight-check.ts、jira-sync.ts)
|
|
68
|
+
- 外部依存が多いスクリプト(phase-runner.ts)
|
|
69
|
+
- CLIラッパースクリプト
|
|
70
|
+
|
|
71
|
+
→ これらは**統合テスト(E2E)**で十分
|
|
72
|
+
|
|
73
|
+
## 統合テスト(E2E)
|
|
74
|
+
|
|
75
|
+
単体テストでカバーできないスクリプトは、統合テストとして実行:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# プリフライトチェック
|
|
79
|
+
npm run preflight
|
|
80
|
+
|
|
81
|
+
# フェーズ実行(実際のConfluence/JIRA API使用)
|
|
82
|
+
npm run phase:run test-feature requirements
|
|
83
|
+
npm run phase:run test-feature design
|
|
84
|
+
npm run phase:run test-feature tasks
|
|
85
|
+
|
|
86
|
+
# バリデーション
|
|
87
|
+
npm run validate:phase test-feature requirements
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**利点**:
|
|
91
|
+
- 実際のAPI動作を確認できる
|
|
92
|
+
- モックの複雑さを回避
|
|
93
|
+
- メンテナンスコストが低い
|
|
94
|
+
|
|
95
|
+
## カバレッジ目標
|
|
96
|
+
|
|
97
|
+
- **validate-phase.ts**: 80%以上(単体テストでカバー)
|
|
98
|
+
- **その他のスクリプト**: 統合テスト(E2E)でカバー
|
|
99
|
+
|
|
100
|
+
**方針**: 過剰テストを避け、実用的なテストに絞る
|
|
101
|
+
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* validate-phase.ts の単体テスト
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
6
|
+
import { existsSync, readFileSync } from 'fs';
|
|
7
|
+
import { validatePhase } from '../validate-phase.js';
|
|
8
|
+
|
|
9
|
+
// fsモジュールのモック
|
|
10
|
+
vi.mock('fs', () => ({
|
|
11
|
+
existsSync: vi.fn(),
|
|
12
|
+
readFileSync: vi.fn()
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
// project-metaのモック
|
|
16
|
+
vi.mock('../utils/project-meta.js', () => ({
|
|
17
|
+
loadProjectMeta: vi.fn(() => ({
|
|
18
|
+
projectId: 'test-project',
|
|
19
|
+
projectName: 'テストプロジェクト',
|
|
20
|
+
jiraProjectKey: 'TEST'
|
|
21
|
+
}))
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
describe('validatePhase', () => {
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
vi.clearAllMocks();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe('基本動作', () => {
|
|
30
|
+
it('すべての必須項目が揃っている場合、validationが成功する', () => {
|
|
31
|
+
// Arrange: requirementsフェーズを代表例としてテスト
|
|
32
|
+
vi.mocked(existsSync).mockReturnValue(true);
|
|
33
|
+
vi.mocked(readFileSync).mockReturnValue(JSON.stringify({
|
|
34
|
+
confluence: {
|
|
35
|
+
spaceKey: 'TEST',
|
|
36
|
+
requirementsPageId: '12345'
|
|
37
|
+
},
|
|
38
|
+
milestones: {
|
|
39
|
+
requirements: {
|
|
40
|
+
completed: true
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}));
|
|
44
|
+
|
|
45
|
+
// Act
|
|
46
|
+
const result = validatePhase('test-feature', 'requirements');
|
|
47
|
+
|
|
48
|
+
// Assert
|
|
49
|
+
expect(result.valid).toBe(true);
|
|
50
|
+
expect(result.errors).toHaveLength(0);
|
|
51
|
+
expect(result.phase).toBe('requirements');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('Confluenceページが作成されていない場合、エラーを返す', () => {
|
|
55
|
+
// Arrange
|
|
56
|
+
vi.mocked(existsSync).mockReturnValue(true);
|
|
57
|
+
vi.mocked(readFileSync).mockReturnValue(JSON.stringify({
|
|
58
|
+
confluence: {
|
|
59
|
+
spaceKey: 'TEST'
|
|
60
|
+
// requirementsPageId が存在しない
|
|
61
|
+
}
|
|
62
|
+
}));
|
|
63
|
+
|
|
64
|
+
// Act
|
|
65
|
+
const result = validatePhase('test-feature', 'requirements');
|
|
66
|
+
|
|
67
|
+
// Assert
|
|
68
|
+
expect(result.valid).toBe(false);
|
|
69
|
+
expect(result.errors).toContain('❌ Confluenceページ(要件定義)が作成されていません');
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
describe('フェーズ固有の検証', () => {
|
|
74
|
+
it('designフェーズ: 前提条件(requirements完了)をチェックする', () => {
|
|
75
|
+
// Arrange
|
|
76
|
+
vi.mocked(existsSync).mockReturnValue(true);
|
|
77
|
+
vi.mocked(readFileSync).mockReturnValue(JSON.stringify({
|
|
78
|
+
confluence: {
|
|
79
|
+
designPageId: '67890'
|
|
80
|
+
},
|
|
81
|
+
milestones: {
|
|
82
|
+
requirements: {
|
|
83
|
+
completed: false // 前提条件が満たされていない
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}));
|
|
87
|
+
|
|
88
|
+
// Act
|
|
89
|
+
const result = validatePhase('test-feature', 'design');
|
|
90
|
+
|
|
91
|
+
// Assert
|
|
92
|
+
expect(result.valid).toBe(false);
|
|
93
|
+
expect(result.errors).toContain('❌ 要件定義が完了していません(前提条件)');
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('tasksフェーズ: JIRA Epic/Story作成をチェックする', () => {
|
|
97
|
+
// Arrange
|
|
98
|
+
vi.mocked(existsSync).mockReturnValue(true);
|
|
99
|
+
vi.mocked(readFileSync).mockImplementation((path) => {
|
|
100
|
+
if (String(path).includes('tasks.md')) {
|
|
101
|
+
return '11/06(木)Day 1';
|
|
102
|
+
}
|
|
103
|
+
return JSON.stringify({
|
|
104
|
+
milestones: {
|
|
105
|
+
design: {
|
|
106
|
+
completed: true
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
jira: {
|
|
110
|
+
// epicKey が存在しない(重要なチェック)
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// Act
|
|
116
|
+
const result = validatePhase('test-feature', 'tasks');
|
|
117
|
+
|
|
118
|
+
// Assert
|
|
119
|
+
expect(result.valid).toBe(false);
|
|
120
|
+
expect(result.errors).toContain('❌ JIRA Epicが作成されていません');
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('tasksフェーズ: 営業日表記をチェックする(重要な独自機能)', () => {
|
|
124
|
+
// Arrange
|
|
125
|
+
vi.mocked(existsSync).mockReturnValue(true);
|
|
126
|
+
vi.mocked(readFileSync).mockImplementation((path) => {
|
|
127
|
+
if (String(path).includes('tasks.md')) {
|
|
128
|
+
return 'タスク一覧(曜日表記なし)'; // 営業日表記がない
|
|
129
|
+
}
|
|
130
|
+
return JSON.stringify({
|
|
131
|
+
milestones: {
|
|
132
|
+
design: {
|
|
133
|
+
completed: true
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
jira: {
|
|
137
|
+
epicKey: 'TEST-1',
|
|
138
|
+
stories: {
|
|
139
|
+
created: 5,
|
|
140
|
+
total: 5
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// Act
|
|
147
|
+
const result = validatePhase('test-feature', 'tasks');
|
|
148
|
+
|
|
149
|
+
// Assert
|
|
150
|
+
expect(result.valid).toBe(true); // 警告だけなのでvalid
|
|
151
|
+
expect(result.warnings).toContain('⚠️ tasks.mdに曜日表記(月、火、水...)が含まれていません');
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
describe('エッジケース', () => {
|
|
156
|
+
it('spec.jsonが存在しない場合、エラーを返す', () => {
|
|
157
|
+
// Arrange
|
|
158
|
+
// requirements.mdは存在するが、spec.jsonは存在しない
|
|
159
|
+
vi.mocked(existsSync).mockImplementation((path: string | Buffer | URL) => {
|
|
160
|
+
const pathStr = typeof path === 'string' ? path : path instanceof URL ? path.pathname : path.toString();
|
|
161
|
+
if (pathStr.includes('requirements.md')) {
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
if (pathStr.includes('spec.json')) {
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
return false;
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
// Act
|
|
171
|
+
const result = validatePhase('test-feature', 'requirements');
|
|
172
|
+
|
|
173
|
+
// Assert: エラーをスローせず、errors配列にエラーを含めて返す
|
|
174
|
+
expect(result.valid).toBe(false);
|
|
175
|
+
expect(result.errors).toContainEqual(expect.stringContaining('spec.json読み込みエラー'));
|
|
176
|
+
expect(result.errors).toContainEqual(expect.stringContaining('spec.json not found'));
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('不正なフェーズ名の場合、エラーをスローする', () => {
|
|
180
|
+
// Act & Assert
|
|
181
|
+
expect(() => validatePhase('test-feature', 'invalid' as any)).toThrow('Unknown phase: invalid');
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 設定スキーマ定義
|
|
3
|
+
* Zodを使用して設定ファイルの型安全性を保証
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Confluence階層構造のモード
|
|
10
|
+
*/
|
|
11
|
+
export const ConfluenceHierarchyModeSchema = z.enum(['simple', 'nested']);
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Confluenceページ作成粒度
|
|
15
|
+
*/
|
|
16
|
+
export const ConfluencePageCreationGranularitySchema = z.enum([
|
|
17
|
+
'single',
|
|
18
|
+
'by-section',
|
|
19
|
+
'by-hierarchy',
|
|
20
|
+
'manual'
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Confluence階層構造設定(by-hierarchyまたはmanualの場合)
|
|
25
|
+
*/
|
|
26
|
+
export const ConfluenceHierarchyStructureSchema = z.object({
|
|
27
|
+
mode: ConfluenceHierarchyModeSchema.optional(),
|
|
28
|
+
parentPageTitle: z.string().optional(),
|
|
29
|
+
createDocTypeParents: z.boolean().optional(),
|
|
30
|
+
structure: z.record(
|
|
31
|
+
z.string(),
|
|
32
|
+
z.object({
|
|
33
|
+
parent: z.string().optional(),
|
|
34
|
+
title: z.string().optional(),
|
|
35
|
+
children: z.array(
|
|
36
|
+
z.object({
|
|
37
|
+
section: z.string(),
|
|
38
|
+
title: z.string()
|
|
39
|
+
})
|
|
40
|
+
).optional(),
|
|
41
|
+
pages: z.array(
|
|
42
|
+
z.object({
|
|
43
|
+
title: z.string(),
|
|
44
|
+
sections: z.array(z.string()),
|
|
45
|
+
labels: z.array(z.string()).optional()
|
|
46
|
+
})
|
|
47
|
+
).optional()
|
|
48
|
+
})
|
|
49
|
+
).optional()
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Confluence設定スキーマ
|
|
54
|
+
*/
|
|
55
|
+
export const ConfluenceConfigSchema = z.object({
|
|
56
|
+
pageCreationGranularity: ConfluencePageCreationGranularitySchema.default('single'),
|
|
57
|
+
pageTitleFormat: z.string().default('[{projectName}] {featureName} {docTypeLabel}'),
|
|
58
|
+
autoLabels: z.array(z.string()).default(['{projectLabel}', '{docType}', '{featureName}', 'github-sync']),
|
|
59
|
+
spaces: z.object({
|
|
60
|
+
requirements: z.string().optional(),
|
|
61
|
+
design: z.string().optional(),
|
|
62
|
+
tasks: z.string().optional()
|
|
63
|
+
}).optional(),
|
|
64
|
+
hierarchy: ConfluenceHierarchyStructureSchema.optional()
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* JIRA Story作成粒度
|
|
69
|
+
*/
|
|
70
|
+
export const JiraStoryCreationGranularitySchema = z.enum([
|
|
71
|
+
'all',
|
|
72
|
+
'by-phase',
|
|
73
|
+
'selected-phases'
|
|
74
|
+
]);
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* JIRA Story Points設定
|
|
78
|
+
*/
|
|
79
|
+
export const JiraStoryPointsSchema = z.enum(['auto', 'manual', 'disabled']);
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* JIRA設定スキーマ
|
|
83
|
+
*/
|
|
84
|
+
export const JiraConfigSchema = z.object({
|
|
85
|
+
storyCreationGranularity: JiraStoryCreationGranularitySchema.default('all'),
|
|
86
|
+
createEpic: z.boolean().default(true),
|
|
87
|
+
storyPoints: JiraStoryPointsSchema.default('auto'),
|
|
88
|
+
autoLabels: z.array(z.string()).default(['{projectLabel}', '{featureName}', '{phaseLabel}']),
|
|
89
|
+
issueTypes: z.object({
|
|
90
|
+
epic: z.string().default('Epic'),
|
|
91
|
+
story: z.string().nullish().default(null), // null | undefined | string
|
|
92
|
+
subtask: z.string().nullish().default(null) // null | undefined | string
|
|
93
|
+
}).optional(),
|
|
94
|
+
selectedPhases: z.array(z.string()).optional()
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* ワークフロー設定スキーマ
|
|
99
|
+
*/
|
|
100
|
+
export const WorkflowConfigSchema = z.object({
|
|
101
|
+
enabledPhases: z.array(z.string()).default(['requirements', 'design', 'tasks']),
|
|
102
|
+
approvalGates: z.object({
|
|
103
|
+
requirements: z.array(z.string()).optional(),
|
|
104
|
+
design: z.array(z.string()).optional(),
|
|
105
|
+
release: z.array(z.string()).optional()
|
|
106
|
+
}).optional()
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* 全体設定スキーマ
|
|
111
|
+
*/
|
|
112
|
+
export const AppConfigSchema = z.object({
|
|
113
|
+
confluence: ConfluenceConfigSchema.optional(),
|
|
114
|
+
jira: JiraConfigSchema.optional(),
|
|
115
|
+
workflow: WorkflowConfigSchema.optional()
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* 設定の型定義
|
|
120
|
+
*/
|
|
121
|
+
export type ConfluenceHierarchyMode = z.infer<typeof ConfluenceHierarchyModeSchema>;
|
|
122
|
+
export type ConfluencePageCreationGranularity = z.infer<typeof ConfluencePageCreationGranularitySchema>;
|
|
123
|
+
export type ConfluenceHierarchyStructure = z.infer<typeof ConfluenceHierarchyStructureSchema>;
|
|
124
|
+
export type ConfluenceConfig = z.infer<typeof ConfluenceConfigSchema>;
|
|
125
|
+
export type JiraStoryCreationGranularity = z.infer<typeof JiraStoryCreationGranularitySchema>;
|
|
126
|
+
export type JiraStoryPoints = z.infer<typeof JiraStoryPointsSchema>;
|
|
127
|
+
export type JiraConfig = z.infer<typeof JiraConfigSchema>;
|
|
128
|
+
export type WorkflowConfig = z.infer<typeof WorkflowConfigSchema>;
|
|
129
|
+
export type AppConfig = z.infer<typeof AppConfigSchema>;
|
|
130
|
+
|