@einja/dev-cli 0.1.9 → 0.1.10
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 +30 -2
- package/dist/cli.js +3 -6
- package/dist/cli.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +11 -6
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/list.js +1 -1
- package/dist/commands/list.js.map +1 -1
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +69 -7
- package/dist/commands/sync.js.map +1 -1
- package/dist/commands/sync.test.js +11 -25
- package/dist/commands/sync.test.js.map +1 -1
- package/dist/commands/task-loop/index.d.ts.map +1 -1
- package/dist/commands/task-loop/index.js +5 -2
- package/dist/commands/task-loop/index.js.map +1 -1
- package/dist/commands/task-loop/lib/__mocks__/child-process.mock.d.ts +227 -0
- package/dist/commands/task-loop/lib/__mocks__/child-process.mock.d.ts.map +1 -0
- package/dist/commands/task-loop/lib/__mocks__/child-process.mock.js +351 -0
- package/dist/commands/task-loop/lib/__mocks__/child-process.mock.js.map +1 -0
- package/dist/commands/task-loop/lib/__mocks__/sample-issues.d.ts +46 -0
- package/dist/commands/task-loop/lib/__mocks__/sample-issues.d.ts.map +1 -0
- package/dist/commands/task-loop/lib/__mocks__/sample-issues.js +224 -0
- package/dist/commands/task-loop/lib/__mocks__/sample-issues.js.map +1 -0
- package/dist/commands/task-loop/lib/branch-manager.d.ts.map +1 -1
- package/dist/commands/task-loop/lib/branch-manager.js +14 -8
- package/dist/commands/task-loop/lib/branch-manager.js.map +1 -1
- package/dist/commands/task-loop/lib/branch-manager.test.d.ts +2 -0
- package/dist/commands/task-loop/lib/branch-manager.test.d.ts.map +1 -0
- package/dist/commands/task-loop/lib/branch-manager.test.js +539 -0
- package/dist/commands/task-loop/lib/branch-manager.test.js.map +1 -0
- package/dist/commands/task-loop/lib/conflict-handler.js +1 -1
- package/dist/commands/task-loop/lib/conflict-handler.js.map +1 -1
- package/dist/commands/task-loop/lib/dependency-resolver.test.d.ts +2 -0
- package/dist/commands/task-loop/lib/dependency-resolver.test.d.ts.map +1 -0
- package/dist/commands/task-loop/lib/dependency-resolver.test.js +1129 -0
- package/dist/commands/task-loop/lib/dependency-resolver.test.js.map +1 -0
- package/dist/commands/task-loop/lib/gh-setup.d.ts.map +1 -1
- package/dist/commands/task-loop/lib/gh-setup.js.map +1 -1
- package/dist/commands/task-loop/lib/github-client.d.ts.map +1 -1
- package/dist/commands/task-loop/lib/github-client.js +3 -3
- package/dist/commands/task-loop/lib/github-client.js.map +1 -1
- package/dist/commands/task-loop/lib/github-client.test.d.ts +2 -0
- package/dist/commands/task-loop/lib/github-client.test.d.ts.map +1 -0
- package/dist/commands/task-loop/lib/github-client.test.js +377 -0
- package/dist/commands/task-loop/lib/github-client.test.js.map +1 -0
- package/dist/commands/task-loop/lib/issue-parser.js +4 -4
- package/dist/commands/task-loop/lib/issue-parser.js.map +1 -1
- package/dist/commands/task-loop/lib/issue-parser.test.d.ts +2 -0
- package/dist/commands/task-loop/lib/issue-parser.test.d.ts.map +1 -0
- package/dist/commands/task-loop/lib/issue-parser.test.js +854 -0
- package/dist/commands/task-loop/lib/issue-parser.test.js.map +1 -0
- package/dist/commands/task-loop/lib/pull-request-manager.d.ts +35 -0
- package/dist/commands/task-loop/lib/pull-request-manager.d.ts.map +1 -0
- package/dist/commands/task-loop/lib/pull-request-manager.js +150 -0
- package/dist/commands/task-loop/lib/pull-request-manager.js.map +1 -0
- package/dist/commands/task-loop/lib/task-number-utils.d.ts +10 -4
- package/dist/commands/task-loop/lib/task-number-utils.d.ts.map +1 -1
- package/dist/commands/task-loop/lib/task-number-utils.js +19 -10
- package/dist/commands/task-loop/lib/task-number-utils.js.map +1 -1
- package/dist/commands/task-loop/lib/task-number-utils.test.d.ts +2 -0
- package/dist/commands/task-loop/lib/task-number-utils.test.d.ts.map +1 -0
- package/dist/commands/task-loop/lib/task-number-utils.test.js +379 -0
- package/dist/commands/task-loop/lib/task-number-utils.test.js.map +1 -0
- package/dist/commands/task-loop/lib/task-state-manager.d.ts.map +1 -1
- package/dist/commands/task-loop/lib/task-state-manager.js +1 -1
- package/dist/commands/task-loop/lib/task-state-manager.js.map +1 -1
- package/dist/commands/task-loop/lib/task-state-manager.test.d.ts +2 -0
- package/dist/commands/task-loop/lib/task-state-manager.test.d.ts.map +1 -0
- package/dist/commands/task-loop/lib/task-state-manager.test.js +541 -0
- package/dist/commands/task-loop/lib/task-state-manager.test.js.map +1 -0
- package/dist/lib/mcp-config.d.ts.map +1 -1
- package/dist/lib/mcp-config.js +8 -4
- package/dist/lib/mcp-config.js.map +1 -1
- package/dist/lib/mcp-config.test.js +2 -2
- package/dist/lib/mcp-config.test.js.map +1 -1
- package/dist/lib/merger.d.ts.map +1 -1
- package/dist/lib/merger.js.map +1 -1
- package/dist/lib/preset-update/cli-repo-detector.test.js.map +1 -1
- package/dist/lib/preset-update/file-copier.d.ts +2 -0
- package/dist/lib/preset-update/file-copier.d.ts.map +1 -1
- package/dist/lib/preset-update/file-copier.js +12 -8
- package/dist/lib/preset-update/file-copier.js.map +1 -1
- package/dist/lib/preset-update/file-copier.test.js +36 -5
- package/dist/lib/preset-update/file-copier.test.js.map +1 -1
- package/dist/lib/preset-update/preset-finder.d.ts +1 -1
- package/dist/lib/preset-update/preset-finder.d.ts.map +1 -1
- package/dist/lib/preset-update/preset-finder.js +1 -1
- package/dist/lib/preset-update/preset-finder.js.map +1 -1
- package/dist/lib/preset-update/preset-finder.test.js +11 -11
- package/dist/lib/preset-update/preset-finder.test.js.map +1 -1
- package/dist/lib/preset.js +3 -3
- package/dist/lib/preset.js.map +1 -1
- package/dist/lib/sync/backup-manager.d.ts.map +1 -1
- package/dist/lib/sync/backup-manager.js +1 -1
- package/dist/lib/sync/backup-manager.js.map +1 -1
- package/dist/lib/sync/backup-manager.test.js +2 -2
- package/dist/lib/sync/backup-manager.test.js.map +1 -1
- package/dist/lib/sync/batch-processor.d.ts.map +1 -1
- package/dist/lib/sync/batch-processor.js.map +1 -1
- package/dist/lib/sync/batch-processor.test.js.map +1 -1
- package/dist/lib/sync/category-validator.d.ts.map +1 -1
- package/dist/lib/sync/category-validator.js.map +1 -1
- package/dist/lib/sync/category-validator.test.js +2 -11
- package/dist/lib/sync/category-validator.test.js.map +1 -1
- package/dist/lib/sync/conflict-reporter.d.ts.map +1 -1
- package/dist/lib/sync/conflict-reporter.js +1 -2
- package/dist/lib/sync/conflict-reporter.js.map +1 -1
- package/dist/lib/sync/conflict-reporter.test.js +2 -7
- package/dist/lib/sync/conflict-reporter.test.js.map +1 -1
- package/dist/lib/sync/diff-engine.d.ts.map +1 -1
- package/dist/lib/sync/diff-engine.js +2 -4
- package/dist/lib/sync/diff-engine.js.map +1 -1
- package/dist/lib/sync/diff-engine.test.js.map +1 -1
- package/dist/lib/sync/file-filter.d.ts.map +1 -1
- package/dist/lib/sync/file-filter.js +26 -3
- package/dist/lib/sync/file-filter.js.map +1 -1
- package/dist/lib/sync/file-filter.test.js +26 -2
- package/dist/lib/sync/file-filter.test.js.map +1 -1
- package/dist/lib/sync/hash-cache.d.ts.map +1 -1
- package/dist/lib/sync/hash-cache.js.map +1 -1
- package/dist/lib/sync/hash-cache.test.js +2 -2
- package/dist/lib/sync/hash-cache.test.js.map +1 -1
- package/dist/lib/sync/integration.test.js +289 -2
- package/dist/lib/sync/integration.test.js.map +1 -1
- package/dist/lib/sync/marker-processor.d.ts +34 -10
- package/dist/lib/sync/marker-processor.d.ts.map +1 -1
- package/dist/lib/sync/marker-processor.js +142 -41
- package/dist/lib/sync/marker-processor.js.map +1 -1
- package/dist/lib/sync/marker-processor.test.js +134 -1
- package/dist/lib/sync/marker-processor.test.js.map +1 -1
- package/dist/lib/sync/metadata-manager.d.ts.map +1 -1
- package/dist/lib/sync/metadata-manager.js.map +1 -1
- package/dist/lib/sync/metadata-manager.test.js +4 -6
- package/dist/lib/sync/metadata-manager.test.js.map +1 -1
- package/dist/lib/sync/performance.test.js +2 -2
- package/dist/lib/sync/performance.test.js.map +1 -1
- package/dist/lib/sync/seed-synchronizer.d.ts +27 -0
- package/dist/lib/sync/seed-synchronizer.d.ts.map +1 -0
- package/dist/lib/sync/seed-synchronizer.js +72 -0
- package/dist/lib/sync/seed-synchronizer.js.map +1 -0
- package/dist/lib/sync/seed-synchronizer.test.d.ts +2 -0
- package/dist/lib/sync/seed-synchronizer.test.d.ts.map +1 -0
- package/dist/lib/sync/seed-synchronizer.test.js +147 -0
- package/dist/lib/sync/seed-synchronizer.test.js.map +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/preset-update.d.ts +1 -1
- package/dist/types/sync.d.ts +4 -2
- package/dist/types/sync.d.ts.map +1 -1
- package/dist/types/sync.js.map +1 -1
- package/package.json +1 -1
- package/presets/default/.claude/agents/einja/backend-architect.md +1131 -0
- package/presets/{minimal/.claude/agents/einja/frontend → default/.claude/agents/einja}/design-engineer.md +1 -1
- package/presets/{minimal/.claude/agents/einja/frontend → default/.claude/agents/einja}/frontend-architect.md +1 -1
- package/presets/{minimal/.claude/agents/einja/frontend → default/.claude/agents/einja}/frontend-coder.md +1 -37
- package/presets/{minimal → default}/.claude/agents/einja/task/task-committer.md +12 -6
- package/presets/{minimal → default}/.claude/agents/einja/task/task-executer.md +9 -9
- package/presets/{minimal → default}/.claude/commands/einja/frontend-implement.md +1 -1
- package/presets/{minimal → default}/.claude/commands/einja/update-docs-by-task-specs.md +6 -6
- package/presets/{minimal/.claude/skills/einja/api-development → default/.claude/skills/einja-api-development}/SKILL.md +5 -5
- package/presets/{minimal/.claude/skills/einja/backend-architecture → default/.claude/skills/einja-backend-architecture}/SKILL.md +5 -5
- package/presets/{minimal/.claude/skills/einja/coding-standards → default/.claude/skills/einja-coding-standards}/SKILL.md +6 -6
- package/presets/{minimal/.claude/skills/einja/component-design → default/.claude/skills/einja-component-design}/SKILL.md +6 -6
- package/presets/{minimal/.claude/skills/einja/frontend-development → default/.claude/skills/einja-frontend-development}/SKILL.md +5 -5
- package/presets/{minimal/.claude/skills/einja/output-format → default/.claude/skills/einja-output-format}/SKILL.md +54 -5
- package/presets/{minimal → default}/preset.yaml +1 -1
- package/presets/{minimal → default}/symlinks.json +10 -10
- package/scaffolds/CLAUDE.md.template +4 -4
- package/scaffolds/steering/README.md +42 -0
- package/scaffolds/steering/acceptance-criteria-and-qa-guide.md +11 -0
- package/scaffolds/steering/architecture.md +11 -0
- package/scaffolds/steering/branch-strategy.md +11 -0
- package/scaffolds/steering/commit-rules.md +12 -1
- package/scaffolds/steering/db-schema-design.md +11 -0
- package/scaffolds/steering/development/api-development.md +15 -4
- package/scaffolds/steering/development/backend-architecture.md +11 -0
- package/scaffolds/steering/development/frontend-development.md +11 -0
- package/scaffolds/steering/development/review-guidelines.md +11 -0
- package/scaffolds/steering/development/testing-strategy.md +85 -0
- package/scaffolds/steering/development-workflow.md +11 -0
- package/scaffolds/steering/infrastructure/deployment.md +11 -0
- package/scaffolds/steering/infrastructure/environment-variables.md +11 -0
- package/scaffolds/steering/product.md +11 -0
- package/scaffolds/steering/task-management.md +11 -0
- /package/presets/{minimal → default}/.claude/agents/einja/docs/docs-updater.md +0 -0
- /package/presets/{minimal → default}/.claude/agents/einja/git/conflict-resolver.md +0 -0
- /package/presets/{minimal → default}/.claude/agents/einja/specs/spec-design-generator.md +0 -0
- /package/presets/{minimal → default}/.claude/agents/einja/specs/spec-qa-generator.md +0 -0
- /package/presets/{minimal → default}/.claude/agents/einja/specs/spec-requirements-generator.md +0 -0
- /package/presets/{minimal → default}/.claude/agents/einja/specs/spec-tasks-generator.md +0 -0
- /package/presets/{minimal → default}/.claude/agents/einja/task/task-modification-analyzer.md +0 -0
- /package/presets/{minimal → default}/.claude/agents/einja/task/task-qa.md +0 -0
- /package/presets/{minimal → default}/.claude/agents/einja/task/task-reviewer.md +0 -0
- /package/presets/{minimal → default}/.claude/commands/einja/spec-create.md +0 -0
- /package/presets/{minimal → default}/.claude/commands/einja/start-dev.md +0 -0
- /package/presets/{minimal → default}/.claude/commands/einja/sync-cursor-commands.md +0 -0
- /package/presets/{minimal → default}/.claude/commands/einja/task-exec.md +0 -0
- /package/presets/{minimal → default}/.claude/hooks/einja/biome-format.sh +0 -0
- /package/presets/{minimal → default}/.claude/hooks/einja/design-doc-check.sh +0 -0
- /package/presets/{minimal → default}/.claude/hooks/einja/detect-secrets.sh +0 -0
- /package/presets/{minimal → default}/.claude/hooks/einja/large-file-warning.sh +0 -0
- /package/presets/{minimal → default}/.claude/hooks/einja/playwright-resize.sh +0 -0
- /package/presets/{minimal → default}/.claude/hooks/einja/typecheck.sh +0 -0
- /package/presets/{minimal → default}/.claude/hooks/einja/unset-volta-recursion.sh +0 -0
- /package/presets/{minimal → default}/.claude/hooks/einja/validate-git-commit.sh +0 -0
- /package/presets/{minimal → default}/.claude/hooks/einja/warn-index-ts.sh +0 -0
- /package/presets/{minimal → default}/.claude/hooks/einja/warn-relative-import.sh +0 -0
- /package/presets/{minimal → default}/.claude/settings.json +0 -0
- /package/presets/{minimal/.claude/skills/einja/coding-standards → default/.claude/skills/einja-coding-standards}/reference/naming-conventions.md +0 -0
- /package/presets/{minimal/.claude/skills/einja/coding-standards → default/.claude/skills/einja-coding-standards}/reference/prohibited-patterns.md +0 -0
- /package/presets/{minimal/.claude/skills/einja/coding-standards → default/.claude/skills/einja-coding-standards}/reference/typescript-rules.md +0 -0
- /package/presets/{minimal/.claude/skills/einja/component-design → default/.claude/skills/einja-component-design}/reference/directory-structure.md +0 -0
- /package/presets/{minimal/.claude/skills/einja/component-design → default/.claude/skills/einja-component-design}/reference/props-patterns.md +0 -0
- /package/presets/{minimal/.claude/skills/einja/component-design → default/.claude/skills/einja-component-design}/reference/styling-guide.md +0 -0
- /package/presets/{minimal/.claude/skills/einja/conflict-resolver → default/.claude/skills/einja-conflict-resolver}/SKILL.md +0 -0
- /package/presets/{minimal/.claude/skills/einja/general-context-loader → default/.claude/skills/einja-general-context-loader}/SKILL.md +0 -0
- /package/presets/{minimal/.claude/skills/einja/spec-context-loader → default/.claude/skills/einja-spec-context-loader}/SKILL.md +0 -0
- /package/presets/{minimal/.claude/skills/einja/task-commit → default/.claude/skills/einja-task-commit}/SKILL.md +0 -0
- /package/presets/{minimal/.claude/skills/einja/task-qa → default/.claude/skills/einja-task-qa}/SKILL.md +0 -0
- /package/presets/{minimal/.claude/skills/einja/task-qa → default/.claude/skills/einja-task-qa}/reference/failure-patterns.md +0 -0
- /package/presets/{minimal/.claude/skills/einja/task-qa → default/.claude/skills/einja-task-qa}/reference/troubleshooting.md +0 -0
- /package/presets/{minimal/.claude/skills/einja/task-qa → default/.claude/skills/einja-task-qa}/reference/usage-patterns.md +0 -0
- /package/presets/{minimal/.claude/skills/einja/task-qa → default/.claude/skills/einja-task-qa}/templates/qa-test-template.md +0 -0
|
@@ -0,0 +1,1129 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { checkExternalIssueDependencies, detectCircularDependencies, getCompletedPhaseNumbers, isAllTasksCompleted, isDependencySatisfied, isPhaseCompleted, selectExecutableTaskGroups, selectNextTaskGroup, } from "./dependency-resolver.js";
|
|
3
|
+
import { parseIssueBody } from "./issue-parser.js";
|
|
4
|
+
// github-client.js のモック
|
|
5
|
+
vi.mock("./github-client.js", () => ({
|
|
6
|
+
isIssueClosed: vi.fn(),
|
|
7
|
+
}));
|
|
8
|
+
import { isIssueClosed } from "./github-client.js";
|
|
9
|
+
describe("dependency-resolver", () => {
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
// 各テスト前にモックをリセット
|
|
12
|
+
vi.clearAllMocks();
|
|
13
|
+
});
|
|
14
|
+
describe("isDependencySatisfied", () => {
|
|
15
|
+
it("依存なしの場合、trueを返す", () => {
|
|
16
|
+
// Given: 依存関係を持たないタスクグループ
|
|
17
|
+
const taskGroup = {
|
|
18
|
+
id: "1.1",
|
|
19
|
+
name: "タスク1",
|
|
20
|
+
phaseNumber: 1,
|
|
21
|
+
status: "pending",
|
|
22
|
+
dependencies: [],
|
|
23
|
+
completionCriteria: "完了",
|
|
24
|
+
tasks: [],
|
|
25
|
+
};
|
|
26
|
+
const completedGroups = new Set();
|
|
27
|
+
const phases = [];
|
|
28
|
+
// When: 依存関係チェック
|
|
29
|
+
const result = isDependencySatisfied(taskGroup, completedGroups, phases);
|
|
30
|
+
// Then: trueが返る
|
|
31
|
+
expect(result).toBe(true);
|
|
32
|
+
});
|
|
33
|
+
it("タスク依存'1.1'が完了済みの場合、trueを返す", () => {
|
|
34
|
+
// Given: タスク1.1に依存するタスク1.2
|
|
35
|
+
const taskGroup = {
|
|
36
|
+
id: "1.2",
|
|
37
|
+
name: "タスク2",
|
|
38
|
+
phaseNumber: 1,
|
|
39
|
+
status: "pending",
|
|
40
|
+
dependencies: ["1.1"],
|
|
41
|
+
completionCriteria: "完了",
|
|
42
|
+
tasks: [],
|
|
43
|
+
};
|
|
44
|
+
const completedGroups = new Set(["1.1"]);
|
|
45
|
+
const phases = [];
|
|
46
|
+
// When: 依存関係チェック
|
|
47
|
+
const result = isDependencySatisfied(taskGroup, completedGroups, phases);
|
|
48
|
+
// Then: trueが返る
|
|
49
|
+
expect(result).toBe(true);
|
|
50
|
+
});
|
|
51
|
+
it("タスク依存'1.1'が未完了の場合、falseを返す", () => {
|
|
52
|
+
// Given: タスク1.1に依存するタスク1.2(1.1は未完了)
|
|
53
|
+
const taskGroup = {
|
|
54
|
+
id: "1.2",
|
|
55
|
+
name: "タスク2",
|
|
56
|
+
phaseNumber: 1,
|
|
57
|
+
status: "pending",
|
|
58
|
+
dependencies: ["1.1"],
|
|
59
|
+
completionCriteria: "完了",
|
|
60
|
+
tasks: [],
|
|
61
|
+
};
|
|
62
|
+
const completedGroups = new Set();
|
|
63
|
+
const phases = [];
|
|
64
|
+
// When: 依存関係チェック
|
|
65
|
+
const result = isDependencySatisfied(taskGroup, completedGroups, phases);
|
|
66
|
+
// Then: falseが返る
|
|
67
|
+
expect(result).toBe(false);
|
|
68
|
+
});
|
|
69
|
+
it("Phase依存が満たされている場合、trueを返す", () => {
|
|
70
|
+
// Given: Phase 1完了に依存するタスク2.1
|
|
71
|
+
const taskGroup = {
|
|
72
|
+
id: "2.1",
|
|
73
|
+
name: "タスク2.1",
|
|
74
|
+
phaseNumber: 2,
|
|
75
|
+
status: "pending",
|
|
76
|
+
dependencies: ["Phase 1完了"],
|
|
77
|
+
completionCriteria: "完了",
|
|
78
|
+
tasks: [],
|
|
79
|
+
};
|
|
80
|
+
const completedGroups = new Set(["1.1", "1.2"]);
|
|
81
|
+
const phases = [
|
|
82
|
+
{
|
|
83
|
+
number: 1,
|
|
84
|
+
name: "Phase 1",
|
|
85
|
+
taskGroups: [
|
|
86
|
+
{
|
|
87
|
+
id: "1.1",
|
|
88
|
+
name: "タスク1.1",
|
|
89
|
+
phaseNumber: 1,
|
|
90
|
+
status: "completed",
|
|
91
|
+
dependencies: [],
|
|
92
|
+
completionCriteria: "完了",
|
|
93
|
+
tasks: [],
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
id: "1.2",
|
|
97
|
+
name: "タスク1.2",
|
|
98
|
+
phaseNumber: 1,
|
|
99
|
+
status: "completed",
|
|
100
|
+
dependencies: [],
|
|
101
|
+
completionCriteria: "完了",
|
|
102
|
+
tasks: [],
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
},
|
|
106
|
+
];
|
|
107
|
+
// When: 依存関係チェック
|
|
108
|
+
const result = isDependencySatisfied(taskGroup, completedGroups, phases);
|
|
109
|
+
// Then: trueが返る
|
|
110
|
+
expect(result).toBe(true);
|
|
111
|
+
});
|
|
112
|
+
it("Phase依存が未満たしの場合、falseを返す", () => {
|
|
113
|
+
// Given: Phase 1完了に依存するタスク2.1(Phase 1は未完了)
|
|
114
|
+
const taskGroup = {
|
|
115
|
+
id: "2.1",
|
|
116
|
+
name: "タスク2.1",
|
|
117
|
+
phaseNumber: 2,
|
|
118
|
+
status: "pending",
|
|
119
|
+
dependencies: ["Phase 1完了"],
|
|
120
|
+
completionCriteria: "完了",
|
|
121
|
+
tasks: [],
|
|
122
|
+
};
|
|
123
|
+
const completedGroups = new Set(["1.1"]);
|
|
124
|
+
const phases = [
|
|
125
|
+
{
|
|
126
|
+
number: 1,
|
|
127
|
+
name: "Phase 1",
|
|
128
|
+
taskGroups: [
|
|
129
|
+
{
|
|
130
|
+
id: "1.1",
|
|
131
|
+
name: "タスク1.1",
|
|
132
|
+
phaseNumber: 1,
|
|
133
|
+
status: "completed",
|
|
134
|
+
dependencies: [],
|
|
135
|
+
completionCriteria: "完了",
|
|
136
|
+
tasks: [],
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
id: "1.2",
|
|
140
|
+
name: "タスク1.2",
|
|
141
|
+
phaseNumber: 1,
|
|
142
|
+
status: "pending",
|
|
143
|
+
dependencies: [],
|
|
144
|
+
completionCriteria: "完了",
|
|
145
|
+
tasks: [],
|
|
146
|
+
},
|
|
147
|
+
],
|
|
148
|
+
},
|
|
149
|
+
];
|
|
150
|
+
// When: 依存関係チェック
|
|
151
|
+
const result = isDependencySatisfied(taskGroup, completedGroups, phases);
|
|
152
|
+
// Then: falseが返る
|
|
153
|
+
expect(result).toBe(false);
|
|
154
|
+
});
|
|
155
|
+
it("Issue依存'#123'の場合、同期チェックではtrueを返す", () => {
|
|
156
|
+
// Given: Issue #123に依存するタスク
|
|
157
|
+
const taskGroup = {
|
|
158
|
+
id: "1.1",
|
|
159
|
+
name: "タスク1",
|
|
160
|
+
phaseNumber: 1,
|
|
161
|
+
status: "pending",
|
|
162
|
+
dependencies: ["#123"],
|
|
163
|
+
completionCriteria: "完了",
|
|
164
|
+
tasks: [],
|
|
165
|
+
};
|
|
166
|
+
const completedGroups = new Set();
|
|
167
|
+
const phases = [];
|
|
168
|
+
// When: 依存関係チェック
|
|
169
|
+
const result = isDependencySatisfied(taskGroup, completedGroups, phases);
|
|
170
|
+
// Then: trueが返る(Issue依存は非同期でチェックされる)
|
|
171
|
+
expect(result).toBe(true);
|
|
172
|
+
});
|
|
173
|
+
it("複数依存が全て満たされている場合、trueを返す", () => {
|
|
174
|
+
// Given: 複数の依存関係を持つタスク
|
|
175
|
+
const taskGroup = {
|
|
176
|
+
id: "1.3",
|
|
177
|
+
name: "タスク3",
|
|
178
|
+
phaseNumber: 1,
|
|
179
|
+
status: "pending",
|
|
180
|
+
dependencies: ["1.1", "1.2"],
|
|
181
|
+
completionCriteria: "完了",
|
|
182
|
+
tasks: [],
|
|
183
|
+
};
|
|
184
|
+
const completedGroups = new Set(["1.1", "1.2"]);
|
|
185
|
+
const phases = [];
|
|
186
|
+
// When: 依存関係チェック
|
|
187
|
+
const result = isDependencySatisfied(taskGroup, completedGroups, phases);
|
|
188
|
+
// Then: trueが返る
|
|
189
|
+
expect(result).toBe(true);
|
|
190
|
+
});
|
|
191
|
+
it("複数依存のうち一部が未満たしの場合、falseを返す", () => {
|
|
192
|
+
// Given: 複数の依存関係を持つタスク(一部未完了)
|
|
193
|
+
const taskGroup = {
|
|
194
|
+
id: "1.3",
|
|
195
|
+
name: "タスク3",
|
|
196
|
+
phaseNumber: 1,
|
|
197
|
+
status: "pending",
|
|
198
|
+
dependencies: ["1.1", "1.2"],
|
|
199
|
+
completionCriteria: "完了",
|
|
200
|
+
tasks: [],
|
|
201
|
+
};
|
|
202
|
+
const completedGroups = new Set(["1.1"]);
|
|
203
|
+
const phases = [];
|
|
204
|
+
// When: 依存関係チェック
|
|
205
|
+
const result = isDependencySatisfied(taskGroup, completedGroups, phases);
|
|
206
|
+
// Then: falseが返る
|
|
207
|
+
expect(result).toBe(false);
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
describe("checkExternalIssueDependencies", () => {
|
|
211
|
+
it("Issue依存'#123'がクローズ済みの場合、trueを返す", async () => {
|
|
212
|
+
// Given: Issue #123に依存するタスク
|
|
213
|
+
const taskGroup = {
|
|
214
|
+
id: "1.1",
|
|
215
|
+
name: "タスク1",
|
|
216
|
+
phaseNumber: 1,
|
|
217
|
+
status: "pending",
|
|
218
|
+
dependencies: ["#123"],
|
|
219
|
+
completionCriteria: "完了",
|
|
220
|
+
tasks: [],
|
|
221
|
+
};
|
|
222
|
+
vi.mocked(isIssueClosed).mockReturnValue(true);
|
|
223
|
+
// When: 外部Issue依存をチェック
|
|
224
|
+
const result = await checkExternalIssueDependencies(taskGroup);
|
|
225
|
+
// Then: trueが返る
|
|
226
|
+
expect(result).toBe(true);
|
|
227
|
+
expect(isIssueClosed).toHaveBeenCalledWith(123);
|
|
228
|
+
});
|
|
229
|
+
it("Issue依存'#123'がオープンの場合、falseを返す", async () => {
|
|
230
|
+
// Given: Issue #123に依存するタスク(#123はオープン)
|
|
231
|
+
const taskGroup = {
|
|
232
|
+
id: "1.1",
|
|
233
|
+
name: "タスク1",
|
|
234
|
+
phaseNumber: 1,
|
|
235
|
+
status: "pending",
|
|
236
|
+
dependencies: ["#123"],
|
|
237
|
+
completionCriteria: "完了",
|
|
238
|
+
tasks: [],
|
|
239
|
+
};
|
|
240
|
+
vi.mocked(isIssueClosed).mockReturnValue(false);
|
|
241
|
+
// When: 外部Issue依存をチェック
|
|
242
|
+
const result = await checkExternalIssueDependencies(taskGroup);
|
|
243
|
+
// Then: falseが返る
|
|
244
|
+
expect(result).toBe(false);
|
|
245
|
+
expect(isIssueClosed).toHaveBeenCalledWith(123);
|
|
246
|
+
});
|
|
247
|
+
it("複数Issue依存が全てクローズ済みの場合、trueを返す", async () => {
|
|
248
|
+
// Given: 複数のIssue依存を持つタスク
|
|
249
|
+
const taskGroup = {
|
|
250
|
+
id: "1.1",
|
|
251
|
+
name: "タスク1",
|
|
252
|
+
phaseNumber: 1,
|
|
253
|
+
status: "pending",
|
|
254
|
+
dependencies: ["#123", "#456"],
|
|
255
|
+
completionCriteria: "完了",
|
|
256
|
+
tasks: [],
|
|
257
|
+
};
|
|
258
|
+
vi.mocked(isIssueClosed).mockReturnValue(true);
|
|
259
|
+
// When: 外部Issue依存をチェック
|
|
260
|
+
const result = await checkExternalIssueDependencies(taskGroup);
|
|
261
|
+
// Then: trueが返る
|
|
262
|
+
expect(result).toBe(true);
|
|
263
|
+
expect(isIssueClosed).toHaveBeenCalledWith(123);
|
|
264
|
+
expect(isIssueClosed).toHaveBeenCalledWith(456);
|
|
265
|
+
});
|
|
266
|
+
it("複数Issue依存のうち一部がオープンの場合、falseを返す", async () => {
|
|
267
|
+
// Given: 複数のIssue依存を持つタスク(一部オープン)
|
|
268
|
+
const taskGroup = {
|
|
269
|
+
id: "1.1",
|
|
270
|
+
name: "タスク1",
|
|
271
|
+
phaseNumber: 1,
|
|
272
|
+
status: "pending",
|
|
273
|
+
dependencies: ["#123", "#456"],
|
|
274
|
+
completionCriteria: "完了",
|
|
275
|
+
tasks: [],
|
|
276
|
+
};
|
|
277
|
+
vi.mocked(isIssueClosed).mockReturnValueOnce(true).mockReturnValueOnce(false);
|
|
278
|
+
// When: 外部Issue依存をチェック
|
|
279
|
+
const result = await checkExternalIssueDependencies(taskGroup);
|
|
280
|
+
// Then: falseが返る
|
|
281
|
+
expect(result).toBe(false);
|
|
282
|
+
});
|
|
283
|
+
it("Issue依存が存在しない場合、trueを返す", async () => {
|
|
284
|
+
// Given: Issue依存を持たないタスク
|
|
285
|
+
const taskGroup = {
|
|
286
|
+
id: "1.1",
|
|
287
|
+
name: "タスク1",
|
|
288
|
+
phaseNumber: 1,
|
|
289
|
+
status: "pending",
|
|
290
|
+
dependencies: ["1.2"],
|
|
291
|
+
completionCriteria: "完了",
|
|
292
|
+
tasks: [],
|
|
293
|
+
};
|
|
294
|
+
// When: 外部Issue依存をチェック
|
|
295
|
+
const result = await checkExternalIssueDependencies(taskGroup);
|
|
296
|
+
// Then: trueが返る(チェック対象なし)
|
|
297
|
+
expect(result).toBe(true);
|
|
298
|
+
expect(isIssueClosed).not.toHaveBeenCalled();
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
describe("isPhaseCompleted", () => {
|
|
302
|
+
it("Phase内の全タスクグループが完了済みの場合、trueを返す", () => {
|
|
303
|
+
// Given: Phase 1の全タスクグループが完了済み
|
|
304
|
+
const phases = [
|
|
305
|
+
{
|
|
306
|
+
number: 1,
|
|
307
|
+
name: "Phase 1",
|
|
308
|
+
taskGroups: [
|
|
309
|
+
{
|
|
310
|
+
id: "1.1",
|
|
311
|
+
name: "タスク1.1",
|
|
312
|
+
phaseNumber: 1,
|
|
313
|
+
status: "completed",
|
|
314
|
+
dependencies: [],
|
|
315
|
+
completionCriteria: "完了",
|
|
316
|
+
tasks: [],
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
id: "1.2",
|
|
320
|
+
name: "タスク1.2",
|
|
321
|
+
phaseNumber: 1,
|
|
322
|
+
status: "completed",
|
|
323
|
+
dependencies: [],
|
|
324
|
+
completionCriteria: "完了",
|
|
325
|
+
tasks: [],
|
|
326
|
+
},
|
|
327
|
+
],
|
|
328
|
+
},
|
|
329
|
+
];
|
|
330
|
+
const completedGroups = new Set(["1.1", "1.2"]);
|
|
331
|
+
// When: Phase完了チェック
|
|
332
|
+
const result = isPhaseCompleted(phases, 1, completedGroups);
|
|
333
|
+
// Then: trueが返る
|
|
334
|
+
expect(result).toBe(true);
|
|
335
|
+
});
|
|
336
|
+
it("Phase内に未完了タスクグループが存在する場合、falseを返す", () => {
|
|
337
|
+
// Given: Phase 1に未完了タスクグループが存在
|
|
338
|
+
const phases = [
|
|
339
|
+
{
|
|
340
|
+
number: 1,
|
|
341
|
+
name: "Phase 1",
|
|
342
|
+
taskGroups: [
|
|
343
|
+
{
|
|
344
|
+
id: "1.1",
|
|
345
|
+
name: "タスク1.1",
|
|
346
|
+
phaseNumber: 1,
|
|
347
|
+
status: "completed",
|
|
348
|
+
dependencies: [],
|
|
349
|
+
completionCriteria: "完了",
|
|
350
|
+
tasks: [],
|
|
351
|
+
},
|
|
352
|
+
{
|
|
353
|
+
id: "1.2",
|
|
354
|
+
name: "タスク1.2",
|
|
355
|
+
phaseNumber: 1,
|
|
356
|
+
status: "pending",
|
|
357
|
+
dependencies: [],
|
|
358
|
+
completionCriteria: "完了",
|
|
359
|
+
tasks: [],
|
|
360
|
+
},
|
|
361
|
+
],
|
|
362
|
+
},
|
|
363
|
+
];
|
|
364
|
+
const completedGroups = new Set(["1.1"]);
|
|
365
|
+
// When: Phase完了チェック
|
|
366
|
+
const result = isPhaseCompleted(phases, 1, completedGroups);
|
|
367
|
+
// Then: falseが返る
|
|
368
|
+
expect(result).toBe(false);
|
|
369
|
+
});
|
|
370
|
+
it("存在しないPhase番号を指定した場合、falseを返す", () => {
|
|
371
|
+
// Given: Phase 1のみ存在
|
|
372
|
+
const phases = [
|
|
373
|
+
{
|
|
374
|
+
number: 1,
|
|
375
|
+
name: "Phase 1",
|
|
376
|
+
taskGroups: [
|
|
377
|
+
{
|
|
378
|
+
id: "1.1",
|
|
379
|
+
name: "タスク1.1",
|
|
380
|
+
phaseNumber: 1,
|
|
381
|
+
status: "completed",
|
|
382
|
+
dependencies: [],
|
|
383
|
+
completionCriteria: "完了",
|
|
384
|
+
tasks: [],
|
|
385
|
+
},
|
|
386
|
+
],
|
|
387
|
+
},
|
|
388
|
+
];
|
|
389
|
+
const completedGroups = new Set(["1.1"]);
|
|
390
|
+
// When: 存在しないPhase 2をチェック
|
|
391
|
+
const result = isPhaseCompleted(phases, 2, completedGroups);
|
|
392
|
+
// Then: falseが返る
|
|
393
|
+
expect(result).toBe(false);
|
|
394
|
+
});
|
|
395
|
+
it("Phaseにタスクグループが存在しない場合、trueを返す", () => {
|
|
396
|
+
// Given: タスクグループが空のPhase
|
|
397
|
+
const phases = [
|
|
398
|
+
{
|
|
399
|
+
number: 1,
|
|
400
|
+
name: "Phase 1",
|
|
401
|
+
taskGroups: [],
|
|
402
|
+
},
|
|
403
|
+
];
|
|
404
|
+
const completedGroups = new Set();
|
|
405
|
+
// When: Phase完了チェック
|
|
406
|
+
const result = isPhaseCompleted(phases, 1, completedGroups);
|
|
407
|
+
// Then: trueが返る(タスクグループが空=全て完了と見なす)
|
|
408
|
+
expect(result).toBe(true);
|
|
409
|
+
});
|
|
410
|
+
});
|
|
411
|
+
describe("getCompletedPhaseNumbers", () => {
|
|
412
|
+
it("完了済みPhaseの番号配列を返す", () => {
|
|
413
|
+
// Given: Phase 1が完了、Phase 2が未完了のIssue
|
|
414
|
+
const issue = {
|
|
415
|
+
number: 1,
|
|
416
|
+
title: "Test",
|
|
417
|
+
state: "open",
|
|
418
|
+
body: `### Phase 1: フェーズ1
|
|
419
|
+
|
|
420
|
+
- [x] **1.1 タスク1.1**
|
|
421
|
+
**依存関係**: なし
|
|
422
|
+
**完了条件**: 完了
|
|
423
|
+
|
|
424
|
+
- [x] **1.2 タスク1.2**
|
|
425
|
+
**依存関係**: 1.1
|
|
426
|
+
**完了条件**: 完了
|
|
427
|
+
|
|
428
|
+
### Phase 2: フェーズ2
|
|
429
|
+
|
|
430
|
+
- [ ] **2.1 タスク2.1**
|
|
431
|
+
**依存関係**: Phase 1完了
|
|
432
|
+
**完了条件**: 完了
|
|
433
|
+
`,
|
|
434
|
+
};
|
|
435
|
+
const parsedIssue = parseIssueBody(issue);
|
|
436
|
+
// When: 完了済みPhase番号を取得
|
|
437
|
+
const result = getCompletedPhaseNumbers(parsedIssue);
|
|
438
|
+
// Then: Phase 1のみ返る
|
|
439
|
+
expect(result).toEqual([1]);
|
|
440
|
+
});
|
|
441
|
+
it("全Phase未完了の場合、空配列を返す", () => {
|
|
442
|
+
// Given: 全Phase未完了のIssue
|
|
443
|
+
const issue = {
|
|
444
|
+
number: 1,
|
|
445
|
+
title: "Test",
|
|
446
|
+
state: "open",
|
|
447
|
+
body: `### Phase 1: フェーズ1
|
|
448
|
+
|
|
449
|
+
- [ ] **1.1 タスク1.1**
|
|
450
|
+
**依存関係**: なし
|
|
451
|
+
**完了条件**: 完了
|
|
452
|
+
`,
|
|
453
|
+
};
|
|
454
|
+
const parsedIssue = parseIssueBody(issue);
|
|
455
|
+
// When: 完了済みPhase番号を取得
|
|
456
|
+
const result = getCompletedPhaseNumbers(parsedIssue);
|
|
457
|
+
// Then: 空配列が返る
|
|
458
|
+
expect(result).toEqual([]);
|
|
459
|
+
});
|
|
460
|
+
it("全Phase完了の場合、全Phase番号を返す", () => {
|
|
461
|
+
// Given: 全Phase完了のIssue
|
|
462
|
+
const issue = {
|
|
463
|
+
number: 1,
|
|
464
|
+
title: "Test",
|
|
465
|
+
state: "open",
|
|
466
|
+
body: `### Phase 1: フェーズ1
|
|
467
|
+
|
|
468
|
+
- [x] **1.1 タスク1.1**
|
|
469
|
+
**依存関係**: なし
|
|
470
|
+
**完了条件**: 完了
|
|
471
|
+
|
|
472
|
+
### Phase 2: フェーズ2
|
|
473
|
+
|
|
474
|
+
- [x] **2.1 タスク2.1**
|
|
475
|
+
**依存関係**: Phase 1完了
|
|
476
|
+
**完了条件**: 完了
|
|
477
|
+
`,
|
|
478
|
+
};
|
|
479
|
+
const parsedIssue = parseIssueBody(issue);
|
|
480
|
+
// When: 完了済みPhase番号を取得
|
|
481
|
+
const result = getCompletedPhaseNumbers(parsedIssue);
|
|
482
|
+
// Then: 全Phase番号が返る
|
|
483
|
+
expect(result).toEqual([1, 2]);
|
|
484
|
+
});
|
|
485
|
+
});
|
|
486
|
+
describe("selectNextTaskGroup", () => {
|
|
487
|
+
it("実行可能なタスクがある場合、最初のタスクグループを返す", async () => {
|
|
488
|
+
// Given: 依存を満たす複数のタスクグループ
|
|
489
|
+
const issue = {
|
|
490
|
+
number: 1,
|
|
491
|
+
title: "Test",
|
|
492
|
+
state: "open",
|
|
493
|
+
body: `### Phase 1: フェーズ1
|
|
494
|
+
|
|
495
|
+
- [x] **1.1 タスク1.1**
|
|
496
|
+
**依存関係**: なし
|
|
497
|
+
**完了条件**: 完了
|
|
498
|
+
|
|
499
|
+
- [ ] **1.2 タスク1.2**
|
|
500
|
+
**依存関係**: 1.1
|
|
501
|
+
**完了条件**: 完了
|
|
502
|
+
|
|
503
|
+
- [ ] **1.3 タスク1.3**
|
|
504
|
+
**依存関係**: 1.1
|
|
505
|
+
**完了条件**: 完了
|
|
506
|
+
`,
|
|
507
|
+
};
|
|
508
|
+
const parsedIssue = parseIssueBody(issue);
|
|
509
|
+
// When: 次のタスクグループを選定
|
|
510
|
+
const result = await selectNextTaskGroup(parsedIssue);
|
|
511
|
+
// Then: 最初の実行可能タスクグループが返る
|
|
512
|
+
expect(result).not.toBeNull();
|
|
513
|
+
expect(result === null || result === void 0 ? void 0 : result.id).toBe("1.2");
|
|
514
|
+
});
|
|
515
|
+
it("maxTaskNumberで範囲外のタスクは除外される", async () => {
|
|
516
|
+
// Given: 複数のタスクグループ
|
|
517
|
+
const issue = {
|
|
518
|
+
number: 1,
|
|
519
|
+
title: "Test",
|
|
520
|
+
state: "open",
|
|
521
|
+
body: `### Phase 1: フェーズ1
|
|
522
|
+
|
|
523
|
+
- [ ] **1.1 タスク1.1**
|
|
524
|
+
**依存関係**: なし
|
|
525
|
+
**完了条件**: 完了
|
|
526
|
+
|
|
527
|
+
- [ ] **1.2 タスク1.2**
|
|
528
|
+
**依存関係**: なし
|
|
529
|
+
**完了条件**: 完了
|
|
530
|
+
|
|
531
|
+
- [ ] **1.3 タスク1.3**
|
|
532
|
+
**依存関係**: なし
|
|
533
|
+
**完了条件**: 完了
|
|
534
|
+
`,
|
|
535
|
+
};
|
|
536
|
+
const parsedIssue = parseIssueBody(issue);
|
|
537
|
+
// When: maxTaskNumberを指定して次のタスクグループを選定
|
|
538
|
+
const result = await selectNextTaskGroup(parsedIssue, "1.2");
|
|
539
|
+
// Then: 1.2以下の最初のタスクグループが返る
|
|
540
|
+
expect(result).not.toBeNull();
|
|
541
|
+
expect(result === null || result === void 0 ? void 0 : result.id).toBe("1.1");
|
|
542
|
+
});
|
|
543
|
+
it("依存関係が未解決のタスクは除外される", async () => {
|
|
544
|
+
// Given: 依存関係が未解決のタスク
|
|
545
|
+
const issue = {
|
|
546
|
+
number: 1,
|
|
547
|
+
title: "Test",
|
|
548
|
+
state: "open",
|
|
549
|
+
body: `### Phase 1: フェーズ1
|
|
550
|
+
|
|
551
|
+
- [ ] **1.1 タスク1.1**
|
|
552
|
+
**依存関係**: なし
|
|
553
|
+
**完了条件**: 完了
|
|
554
|
+
|
|
555
|
+
- [ ] **1.2 タスク1.2**
|
|
556
|
+
**依存関係**: 1.1
|
|
557
|
+
**完了条件**: 完了
|
|
558
|
+
`,
|
|
559
|
+
};
|
|
560
|
+
const parsedIssue = parseIssueBody(issue);
|
|
561
|
+
// When: 次のタスクグループを選定
|
|
562
|
+
const result = await selectNextTaskGroup(parsedIssue);
|
|
563
|
+
// Then: 依存のないタスクのみ返る(1.2は除外される)
|
|
564
|
+
expect(result).not.toBeNull();
|
|
565
|
+
expect(result === null || result === void 0 ? void 0 : result.id).toBe("1.1");
|
|
566
|
+
});
|
|
567
|
+
it("全て完了済みの場合、nullを返す", async () => {
|
|
568
|
+
// Given: 全て完了済みのIssue
|
|
569
|
+
const issue = {
|
|
570
|
+
number: 1,
|
|
571
|
+
title: "Test",
|
|
572
|
+
state: "open",
|
|
573
|
+
body: `### Phase 1: フェーズ1
|
|
574
|
+
|
|
575
|
+
- [x] **1.1 タスク1.1**
|
|
576
|
+
**依存関係**: なし
|
|
577
|
+
**完了条件**: 完了
|
|
578
|
+
|
|
579
|
+
- [x] **1.2 タスク1.2**
|
|
580
|
+
**依存関係**: 1.1
|
|
581
|
+
**完了条件**: 完了
|
|
582
|
+
`,
|
|
583
|
+
};
|
|
584
|
+
const parsedIssue = parseIssueBody(issue);
|
|
585
|
+
// When: 次のタスクグループを選定
|
|
586
|
+
const result = await selectNextTaskGroup(parsedIssue);
|
|
587
|
+
// Then: nullが返る
|
|
588
|
+
expect(result).toBeNull();
|
|
589
|
+
});
|
|
590
|
+
});
|
|
591
|
+
describe("selectExecutableTaskGroups", () => {
|
|
592
|
+
it("依存を満たす複数タスクの場合、実行可能配列を返す", async () => {
|
|
593
|
+
// Given: 依存を満たす複数のタスクグループ
|
|
594
|
+
const issue = {
|
|
595
|
+
number: 1,
|
|
596
|
+
title: "Test",
|
|
597
|
+
state: "open",
|
|
598
|
+
body: `### Phase 1: フェーズ1
|
|
599
|
+
|
|
600
|
+
- [x] **1.1 タスク1.1**
|
|
601
|
+
**依存関係**: なし
|
|
602
|
+
**完了条件**: 完了
|
|
603
|
+
|
|
604
|
+
- [ ] **1.2 タスク1.2**
|
|
605
|
+
**依存関係**: 1.1
|
|
606
|
+
**完了条件**: 完了
|
|
607
|
+
|
|
608
|
+
- [ ] **1.3 タスク1.3**
|
|
609
|
+
**依存関係**: 1.1
|
|
610
|
+
**完了条件**: 完了
|
|
611
|
+
`,
|
|
612
|
+
};
|
|
613
|
+
const parsedIssue = parseIssueBody(issue);
|
|
614
|
+
// When: 実行可能タスクグループを選定
|
|
615
|
+
const result = await selectExecutableTaskGroups(parsedIssue);
|
|
616
|
+
// Then: 依存を満たすタスクグループが返る
|
|
617
|
+
expect(result).toHaveLength(2);
|
|
618
|
+
expect(result[0].id).toBe("1.2");
|
|
619
|
+
expect(result[1].id).toBe("1.3");
|
|
620
|
+
});
|
|
621
|
+
it("maxTaskNumber='1.2'の場合、1.2以下のみ返す", async () => {
|
|
622
|
+
// Given: 複数のタスクグループ
|
|
623
|
+
const issue = {
|
|
624
|
+
number: 1,
|
|
625
|
+
title: "Test",
|
|
626
|
+
state: "open",
|
|
627
|
+
body: `### Phase 1: フェーズ1
|
|
628
|
+
|
|
629
|
+
- [ ] **1.1 タスク1.1**
|
|
630
|
+
**依存関係**: なし
|
|
631
|
+
**完了条件**: 完了
|
|
632
|
+
|
|
633
|
+
- [ ] **1.2 タスク1.2**
|
|
634
|
+
**依存関係**: なし
|
|
635
|
+
**完了条件**: 完了
|
|
636
|
+
|
|
637
|
+
- [ ] **1.3 タスク1.3**
|
|
638
|
+
**依存関係**: なし
|
|
639
|
+
**完了条件**: 完了
|
|
640
|
+
`,
|
|
641
|
+
};
|
|
642
|
+
const parsedIssue = parseIssueBody(issue);
|
|
643
|
+
// When: maxTaskNumberを指定して実行可能タスクグループを選定
|
|
644
|
+
const result = await selectExecutableTaskGroups(parsedIssue, "1.2");
|
|
645
|
+
// Then: 1.2以下のタスクグループのみ返る
|
|
646
|
+
expect(result).toHaveLength(2);
|
|
647
|
+
expect(result[0].id).toBe("1.1");
|
|
648
|
+
expect(result[1].id).toBe("1.2");
|
|
649
|
+
});
|
|
650
|
+
it("全て完了済みの場合、空配列を返す", async () => {
|
|
651
|
+
// Given: 全て完了済みのIssue
|
|
652
|
+
const issue = {
|
|
653
|
+
number: 1,
|
|
654
|
+
title: "Test",
|
|
655
|
+
state: "open",
|
|
656
|
+
body: `### Phase 1: フェーズ1
|
|
657
|
+
|
|
658
|
+
- [x] **1.1 タスク1.1**
|
|
659
|
+
**依存関係**: なし
|
|
660
|
+
**完了条件**: 完了
|
|
661
|
+
|
|
662
|
+
- [x] **1.2 タスク1.2**
|
|
663
|
+
**依存関係**: 1.1
|
|
664
|
+
**完了条件**: 完了
|
|
665
|
+
`,
|
|
666
|
+
};
|
|
667
|
+
const parsedIssue = parseIssueBody(issue);
|
|
668
|
+
// When: 実行可能タスクグループを選定
|
|
669
|
+
const result = await selectExecutableTaskGroups(parsedIssue);
|
|
670
|
+
// Then: 空配列が返る
|
|
671
|
+
expect(result).toHaveLength(0);
|
|
672
|
+
});
|
|
673
|
+
it("依存が満たされていないタスクは除外される", async () => {
|
|
674
|
+
// Given: 依存が満たされていないタスク
|
|
675
|
+
const issue = {
|
|
676
|
+
number: 1,
|
|
677
|
+
title: "Test",
|
|
678
|
+
state: "open",
|
|
679
|
+
body: `### Phase 1: フェーズ1
|
|
680
|
+
|
|
681
|
+
- [ ] **1.1 タスク1.1**
|
|
682
|
+
**依存関係**: なし
|
|
683
|
+
**完了条件**: 完了
|
|
684
|
+
|
|
685
|
+
- [ ] **1.2 タスク1.2**
|
|
686
|
+
**依存関係**: 1.1
|
|
687
|
+
**完了条件**: 完了
|
|
688
|
+
`,
|
|
689
|
+
};
|
|
690
|
+
const parsedIssue = parseIssueBody(issue);
|
|
691
|
+
// When: 実行可能タスクグループを選定
|
|
692
|
+
const result = await selectExecutableTaskGroups(parsedIssue);
|
|
693
|
+
// Then: 依存のないタスクのみ返る
|
|
694
|
+
expect(result).toHaveLength(1);
|
|
695
|
+
expect(result[0].id).toBe("1.1");
|
|
696
|
+
});
|
|
697
|
+
it("外部Issue依存がクローズ済みの場合、実行可能に含まれる", async () => {
|
|
698
|
+
// Given: 外部Issue依存を持つタスク
|
|
699
|
+
const issue = {
|
|
700
|
+
number: 1,
|
|
701
|
+
title: "Test",
|
|
702
|
+
state: "open",
|
|
703
|
+
body: `### Phase 1: フェーズ1
|
|
704
|
+
|
|
705
|
+
- [ ] **1.1 タスク1.1**
|
|
706
|
+
**依存関係**: #123
|
|
707
|
+
**完了条件**: 完了
|
|
708
|
+
`,
|
|
709
|
+
};
|
|
710
|
+
const parsedIssue = parseIssueBody(issue);
|
|
711
|
+
vi.mocked(isIssueClosed).mockReturnValue(true);
|
|
712
|
+
// When: 実行可能タスクグループを選定
|
|
713
|
+
const result = await selectExecutableTaskGroups(parsedIssue);
|
|
714
|
+
// Then: 実行可能に含まれる
|
|
715
|
+
expect(result).toHaveLength(1);
|
|
716
|
+
expect(result[0].id).toBe("1.1");
|
|
717
|
+
expect(isIssueClosed).toHaveBeenCalledWith(123);
|
|
718
|
+
});
|
|
719
|
+
it("外部Issue依存がオープンの場合、実行可能から除外される", async () => {
|
|
720
|
+
// Given: 外部Issue依存を持つタスク(Issue はオープン)
|
|
721
|
+
const issue = {
|
|
722
|
+
number: 1,
|
|
723
|
+
title: "Test",
|
|
724
|
+
state: "open",
|
|
725
|
+
body: `### Phase 1: フェーズ1
|
|
726
|
+
|
|
727
|
+
- [ ] **1.1 タスク1.1**
|
|
728
|
+
**依存関係**: #123
|
|
729
|
+
**完了条件**: 完了
|
|
730
|
+
`,
|
|
731
|
+
};
|
|
732
|
+
const parsedIssue = parseIssueBody(issue);
|
|
733
|
+
vi.mocked(isIssueClosed).mockReturnValue(false);
|
|
734
|
+
// When: 実行可能タスクグループを選定
|
|
735
|
+
const result = await selectExecutableTaskGroups(parsedIssue);
|
|
736
|
+
// Then: 実行可能から除外される
|
|
737
|
+
expect(result).toHaveLength(0);
|
|
738
|
+
expect(isIssueClosed).toHaveBeenCalledWith(123);
|
|
739
|
+
});
|
|
740
|
+
it("in-progressステータスのタスクも候補に含まれる", async () => {
|
|
741
|
+
// Given: in-progressステータスのタスク
|
|
742
|
+
const parsedIssue = {
|
|
743
|
+
issueNumber: 1,
|
|
744
|
+
title: "Test",
|
|
745
|
+
body: "",
|
|
746
|
+
phases: [
|
|
747
|
+
{
|
|
748
|
+
number: 1,
|
|
749
|
+
name: "Phase 1",
|
|
750
|
+
taskGroups: [
|
|
751
|
+
{
|
|
752
|
+
id: "1.1",
|
|
753
|
+
name: "タスク1.1",
|
|
754
|
+
phaseNumber: 1,
|
|
755
|
+
status: "in-progress",
|
|
756
|
+
dependencies: [],
|
|
757
|
+
completionCriteria: "完了",
|
|
758
|
+
tasks: [],
|
|
759
|
+
},
|
|
760
|
+
],
|
|
761
|
+
},
|
|
762
|
+
],
|
|
763
|
+
};
|
|
764
|
+
// When: 実行可能タスクグループを選定
|
|
765
|
+
const result = await selectExecutableTaskGroups(parsedIssue);
|
|
766
|
+
// Then: in-progressタスクが含まれる
|
|
767
|
+
expect(result).toHaveLength(1);
|
|
768
|
+
expect(result[0].id).toBe("1.1");
|
|
769
|
+
});
|
|
770
|
+
it("タスク番号順にソートされて返される", async () => {
|
|
771
|
+
// Given: タスク番号がバラバラのタスクグループ
|
|
772
|
+
const issue = {
|
|
773
|
+
number: 1,
|
|
774
|
+
title: "Test",
|
|
775
|
+
state: "open",
|
|
776
|
+
body: `### Phase 1: フェーズ1
|
|
777
|
+
|
|
778
|
+
- [ ] **1.3 タスク1.3**
|
|
779
|
+
**依存関係**: なし
|
|
780
|
+
**完了条件**: 完了
|
|
781
|
+
|
|
782
|
+
- [ ] **1.1 タスク1.1**
|
|
783
|
+
**依存関係**: なし
|
|
784
|
+
**完了条件**: 完了
|
|
785
|
+
|
|
786
|
+
- [ ] **1.2 タスク1.2**
|
|
787
|
+
**依存関係**: なし
|
|
788
|
+
**完了条件**: 完了
|
|
789
|
+
`,
|
|
790
|
+
};
|
|
791
|
+
const parsedIssue = parseIssueBody(issue);
|
|
792
|
+
// When: 実行可能タスクグループを選定
|
|
793
|
+
const result = await selectExecutableTaskGroups(parsedIssue);
|
|
794
|
+
// Then: タスク番号順にソートされて返る
|
|
795
|
+
expect(result).toHaveLength(3);
|
|
796
|
+
expect(result[0].id).toBe("1.1");
|
|
797
|
+
expect(result[1].id).toBe("1.2");
|
|
798
|
+
expect(result[2].id).toBe("1.3");
|
|
799
|
+
});
|
|
800
|
+
});
|
|
801
|
+
describe("isAllTasksCompleted", () => {
|
|
802
|
+
it("全タスクが完了済みの場合、trueを返す", () => {
|
|
803
|
+
// Given: 全タスクが完了済みのIssue
|
|
804
|
+
const issue = {
|
|
805
|
+
number: 1,
|
|
806
|
+
title: "Test",
|
|
807
|
+
state: "open",
|
|
808
|
+
body: `### Phase 1: フェーズ1
|
|
809
|
+
|
|
810
|
+
- [x] **1.1 タスク1.1**
|
|
811
|
+
**依存関係**: なし
|
|
812
|
+
**完了条件**: 完了
|
|
813
|
+
|
|
814
|
+
- [x] **1.2 タスク1.2**
|
|
815
|
+
**依存関係**: 1.1
|
|
816
|
+
**完了条件**: 完了
|
|
817
|
+
|
|
818
|
+
### Phase 2: フェーズ2
|
|
819
|
+
|
|
820
|
+
- [x] **2.1 タスク2.1**
|
|
821
|
+
**依存関係**: Phase 1完了
|
|
822
|
+
**完了条件**: 完了
|
|
823
|
+
`,
|
|
824
|
+
};
|
|
825
|
+
const parsedIssue = parseIssueBody(issue);
|
|
826
|
+
// When: 全タスク完了チェック
|
|
827
|
+
const result = isAllTasksCompleted(parsedIssue);
|
|
828
|
+
// Then: trueが返る
|
|
829
|
+
expect(result).toBe(true);
|
|
830
|
+
});
|
|
831
|
+
it("未完了タスクがある場合、falseを返す", () => {
|
|
832
|
+
// Given: 未完了タスクがあるIssue
|
|
833
|
+
const issue = {
|
|
834
|
+
number: 1,
|
|
835
|
+
title: "Test",
|
|
836
|
+
state: "open",
|
|
837
|
+
body: `### Phase 1: フェーズ1
|
|
838
|
+
|
|
839
|
+
- [x] **1.1 タスク1.1**
|
|
840
|
+
**依存関係**: なし
|
|
841
|
+
**完了条件**: 完了
|
|
842
|
+
|
|
843
|
+
- [ ] **1.2 タスク1.2**
|
|
844
|
+
**依存関係**: 1.1
|
|
845
|
+
**完了条件**: 完了
|
|
846
|
+
`,
|
|
847
|
+
};
|
|
848
|
+
const parsedIssue = parseIssueBody(issue);
|
|
849
|
+
// When: 全タスク完了チェック
|
|
850
|
+
const result = isAllTasksCompleted(parsedIssue);
|
|
851
|
+
// Then: falseが返る
|
|
852
|
+
expect(result).toBe(false);
|
|
853
|
+
});
|
|
854
|
+
it("maxTaskNumber範囲内の全タスクが完了の場合、trueを返す", () => {
|
|
855
|
+
// Given: 1.2以下が完了、1.3が未完了のIssue
|
|
856
|
+
const issue = {
|
|
857
|
+
number: 1,
|
|
858
|
+
title: "Test",
|
|
859
|
+
state: "open",
|
|
860
|
+
body: `### Phase 1: フェーズ1
|
|
861
|
+
|
|
862
|
+
- [x] **1.1 タスク1.1**
|
|
863
|
+
**依存関係**: なし
|
|
864
|
+
**完了条件**: 完了
|
|
865
|
+
|
|
866
|
+
- [x] **1.2 タスク1.2**
|
|
867
|
+
**依存関係**: 1.1
|
|
868
|
+
**完了条件**: 完了
|
|
869
|
+
|
|
870
|
+
- [ ] **1.3 タスク1.3**
|
|
871
|
+
**依存関係**: 1.2
|
|
872
|
+
**完了条件**: 完了
|
|
873
|
+
`,
|
|
874
|
+
};
|
|
875
|
+
const parsedIssue = parseIssueBody(issue);
|
|
876
|
+
// When: maxTaskNumber=1.2で全タスク完了チェック
|
|
877
|
+
const result = isAllTasksCompleted(parsedIssue, "1.2");
|
|
878
|
+
// Then: trueが返る(1.3は範囲外なので無視される)
|
|
879
|
+
expect(result).toBe(true);
|
|
880
|
+
});
|
|
881
|
+
it("maxTaskNumber範囲内に未完了タスクがある場合、falseを返す", () => {
|
|
882
|
+
// Given: 1.2が未完了、1.3以降は完了のIssue
|
|
883
|
+
const issue = {
|
|
884
|
+
number: 1,
|
|
885
|
+
title: "Test",
|
|
886
|
+
state: "open",
|
|
887
|
+
body: `### Phase 1: フェーズ1
|
|
888
|
+
|
|
889
|
+
- [x] **1.1 タスク1.1**
|
|
890
|
+
**依存関係**: なし
|
|
891
|
+
**完了条件**: 完了
|
|
892
|
+
|
|
893
|
+
- [ ] **1.2 タスク1.2**
|
|
894
|
+
**依存関係**: 1.1
|
|
895
|
+
**完了条件**: 完了
|
|
896
|
+
|
|
897
|
+
- [x] **1.3 タスク1.3**
|
|
898
|
+
**依存関係**: 1.2
|
|
899
|
+
**完了条件**: 完了
|
|
900
|
+
`,
|
|
901
|
+
};
|
|
902
|
+
const parsedIssue = parseIssueBody(issue);
|
|
903
|
+
// When: maxTaskNumber=1.2で全タスク完了チェック
|
|
904
|
+
const result = isAllTasksCompleted(parsedIssue, "1.2");
|
|
905
|
+
// Then: falseが返る(1.2が未完了)
|
|
906
|
+
expect(result).toBe(false);
|
|
907
|
+
});
|
|
908
|
+
it("タスクグループが存在しない場合、trueを返す", () => {
|
|
909
|
+
// Given: タスクグループが空のIssue
|
|
910
|
+
const parsedIssue = {
|
|
911
|
+
issueNumber: 1,
|
|
912
|
+
title: "Test",
|
|
913
|
+
body: "",
|
|
914
|
+
phases: [],
|
|
915
|
+
};
|
|
916
|
+
// When: 全タスク完了チェック
|
|
917
|
+
const result = isAllTasksCompleted(parsedIssue);
|
|
918
|
+
// Then: trueが返る(タスクが存在しない=全て完了と見なす)
|
|
919
|
+
expect(result).toBe(true);
|
|
920
|
+
});
|
|
921
|
+
});
|
|
922
|
+
describe("detectCircularDependencies", () => {
|
|
923
|
+
it("循環なしの場合、nullを返す", () => {
|
|
924
|
+
// Given: 循環のない依存関係
|
|
925
|
+
const issue = {
|
|
926
|
+
number: 1,
|
|
927
|
+
title: "Test",
|
|
928
|
+
state: "open",
|
|
929
|
+
body: `### Phase 1: フェーズ1
|
|
930
|
+
|
|
931
|
+
- [ ] **1.1 タスク1.1**
|
|
932
|
+
**依存関係**: なし
|
|
933
|
+
**完了条件**: 完了
|
|
934
|
+
|
|
935
|
+
- [ ] **1.2 タスク1.2**
|
|
936
|
+
**依存関係**: 1.1
|
|
937
|
+
**完了条件**: 完了
|
|
938
|
+
|
|
939
|
+
- [ ] **1.3 タスク1.3**
|
|
940
|
+
**依存関係**: 1.2
|
|
941
|
+
**完了条件**: 完了
|
|
942
|
+
`,
|
|
943
|
+
};
|
|
944
|
+
const parsedIssue = parseIssueBody(issue);
|
|
945
|
+
// When: 循環依存をチェック
|
|
946
|
+
const result = detectCircularDependencies(parsedIssue);
|
|
947
|
+
// Then: nullが返る
|
|
948
|
+
expect(result).toBeNull();
|
|
949
|
+
});
|
|
950
|
+
it("A→B→Aの2ノード循環の場合、循環パスを返す", () => {
|
|
951
|
+
// Given: 2ノード循環依存
|
|
952
|
+
const issue = {
|
|
953
|
+
number: 1,
|
|
954
|
+
title: "Test",
|
|
955
|
+
state: "open",
|
|
956
|
+
body: `### Phase 1: フェーズ1
|
|
957
|
+
|
|
958
|
+
- [ ] **1.1 タスクA**
|
|
959
|
+
**依存関係**: 1.2
|
|
960
|
+
**完了条件**: 完了
|
|
961
|
+
|
|
962
|
+
- [ ] **1.2 タスクB**
|
|
963
|
+
**依存関係**: 1.1
|
|
964
|
+
**完了条件**: 完了
|
|
965
|
+
`,
|
|
966
|
+
};
|
|
967
|
+
const parsedIssue = parseIssueBody(issue);
|
|
968
|
+
// When: 循環依存をチェック
|
|
969
|
+
const result = detectCircularDependencies(parsedIssue);
|
|
970
|
+
// Then: 循環パスが返る
|
|
971
|
+
expect(result).not.toBeNull();
|
|
972
|
+
expect(result).toContain("1.1");
|
|
973
|
+
expect(result).toContain("1.2");
|
|
974
|
+
});
|
|
975
|
+
it("A→B→C→Aの3ノード循環の場合、循環パスを返す", () => {
|
|
976
|
+
// Given: 3ノード循環依存
|
|
977
|
+
const issue = {
|
|
978
|
+
number: 1,
|
|
979
|
+
title: "Test",
|
|
980
|
+
state: "open",
|
|
981
|
+
body: `### Phase 1: フェーズ1
|
|
982
|
+
|
|
983
|
+
- [ ] **1.1 タスクA**
|
|
984
|
+
**依存関係**: 1.2
|
|
985
|
+
**完了条件**: 完了
|
|
986
|
+
|
|
987
|
+
- [ ] **1.2 タスクB**
|
|
988
|
+
**依存関係**: 1.3
|
|
989
|
+
**完了条件**: 完了
|
|
990
|
+
|
|
991
|
+
- [ ] **1.3 タスクC**
|
|
992
|
+
**依存関係**: 1.1
|
|
993
|
+
**完了条件**: 完了
|
|
994
|
+
`,
|
|
995
|
+
};
|
|
996
|
+
const parsedIssue = parseIssueBody(issue);
|
|
997
|
+
// When: 循環依存をチェック
|
|
998
|
+
const result = detectCircularDependencies(parsedIssue);
|
|
999
|
+
// Then: 循環パスが返る
|
|
1000
|
+
expect(result).not.toBeNull();
|
|
1001
|
+
expect(result).toContain("1.1");
|
|
1002
|
+
expect(result).toContain("1.2");
|
|
1003
|
+
expect(result).toContain("1.3");
|
|
1004
|
+
});
|
|
1005
|
+
it("自己参照依存は循環として検出される", () => {
|
|
1006
|
+
// Given: 自己参照依存(1.1 → 1.1)
|
|
1007
|
+
const parsedIssue = {
|
|
1008
|
+
issueNumber: 1,
|
|
1009
|
+
title: "Test",
|
|
1010
|
+
body: "",
|
|
1011
|
+
phases: [
|
|
1012
|
+
{
|
|
1013
|
+
number: 1,
|
|
1014
|
+
name: "Phase 1",
|
|
1015
|
+
taskGroups: [
|
|
1016
|
+
{
|
|
1017
|
+
id: "1.1",
|
|
1018
|
+
name: "タスク1.1",
|
|
1019
|
+
phaseNumber: 1,
|
|
1020
|
+
status: "pending",
|
|
1021
|
+
dependencies: ["1.1"],
|
|
1022
|
+
completionCriteria: "完了",
|
|
1023
|
+
tasks: [],
|
|
1024
|
+
},
|
|
1025
|
+
],
|
|
1026
|
+
},
|
|
1027
|
+
],
|
|
1028
|
+
};
|
|
1029
|
+
// When: 循環依存をチェック
|
|
1030
|
+
const result = detectCircularDependencies(parsedIssue);
|
|
1031
|
+
// Then: 循環パスが返る
|
|
1032
|
+
expect(result).not.toBeNull();
|
|
1033
|
+
expect(result).toContain("1.1");
|
|
1034
|
+
});
|
|
1035
|
+
it("Phase依存やIssue依存は循環検出の対象外", () => {
|
|
1036
|
+
// Given: Phase依存とIssue依存を含むタスク
|
|
1037
|
+
const issue = {
|
|
1038
|
+
number: 1,
|
|
1039
|
+
title: "Test",
|
|
1040
|
+
state: "open",
|
|
1041
|
+
body: `### Phase 1: フェーズ1
|
|
1042
|
+
|
|
1043
|
+
- [ ] **1.1 タスク1.1**
|
|
1044
|
+
**依存関係**: なし
|
|
1045
|
+
**完了条件**: 完了
|
|
1046
|
+
|
|
1047
|
+
### Phase 2: フェーズ2
|
|
1048
|
+
|
|
1049
|
+
- [ ] **2.1 タスク2.1**
|
|
1050
|
+
**依存関係**: Phase 1完了, #123
|
|
1051
|
+
**完了条件**: 完了
|
|
1052
|
+
`,
|
|
1053
|
+
};
|
|
1054
|
+
const parsedIssue = parseIssueBody(issue);
|
|
1055
|
+
// When: 循環依存をチェック
|
|
1056
|
+
const result = detectCircularDependencies(parsedIssue);
|
|
1057
|
+
// Then: nullが返る(タスクグループ依存のみが対象)
|
|
1058
|
+
expect(result).toBeNull();
|
|
1059
|
+
});
|
|
1060
|
+
it("複数の独立した依存チェーンがある場合、循環がなければnullを返す", () => {
|
|
1061
|
+
// Given: 複数の独立した依存チェーン
|
|
1062
|
+
const issue = {
|
|
1063
|
+
number: 1,
|
|
1064
|
+
title: "Test",
|
|
1065
|
+
state: "open",
|
|
1066
|
+
body: `### Phase 1: フェーズ1
|
|
1067
|
+
|
|
1068
|
+
- [ ] **1.1 タスク1.1**
|
|
1069
|
+
**依存関係**: なし
|
|
1070
|
+
**完了条件**: 完了
|
|
1071
|
+
|
|
1072
|
+
- [ ] **1.2 タスク1.2**
|
|
1073
|
+
**依存関係**: 1.1
|
|
1074
|
+
**完了条件**: 完了
|
|
1075
|
+
|
|
1076
|
+
### Phase 2: フェーズ2
|
|
1077
|
+
|
|
1078
|
+
- [ ] **2.1 タスク2.1**
|
|
1079
|
+
**依存関係**: なし
|
|
1080
|
+
**完了条件**: 完了
|
|
1081
|
+
|
|
1082
|
+
- [ ] **2.2 タスク2.2**
|
|
1083
|
+
**依存関係**: 2.1
|
|
1084
|
+
**完了条件**: 完了
|
|
1085
|
+
`,
|
|
1086
|
+
};
|
|
1087
|
+
const parsedIssue = parseIssueBody(issue);
|
|
1088
|
+
// When: 循環依存をチェック
|
|
1089
|
+
const result = detectCircularDependencies(parsedIssue);
|
|
1090
|
+
// Then: nullが返る
|
|
1091
|
+
expect(result).toBeNull();
|
|
1092
|
+
});
|
|
1093
|
+
it("部分的な循環がある場合、循環パスを返す", () => {
|
|
1094
|
+
// Given: 一部のタスクで循環依存が発生
|
|
1095
|
+
const issue = {
|
|
1096
|
+
number: 1,
|
|
1097
|
+
title: "Test",
|
|
1098
|
+
state: "open",
|
|
1099
|
+
body: `### Phase 1: フェーズ1
|
|
1100
|
+
|
|
1101
|
+
- [ ] **1.1 タスク1.1**
|
|
1102
|
+
**依存関係**: なし
|
|
1103
|
+
**完了条件**: 完了
|
|
1104
|
+
|
|
1105
|
+
- [ ] **1.2 タスク1.2**
|
|
1106
|
+
**依存関係**: 1.3
|
|
1107
|
+
**完了条件**: 完了
|
|
1108
|
+
|
|
1109
|
+
- [ ] **1.3 タスク1.3**
|
|
1110
|
+
**依存関係**: 1.4
|
|
1111
|
+
**完了条件**: 完了
|
|
1112
|
+
|
|
1113
|
+
- [ ] **1.4 タスク1.4**
|
|
1114
|
+
**依存関係**: 1.2
|
|
1115
|
+
**完了条件**: 完了
|
|
1116
|
+
`,
|
|
1117
|
+
};
|
|
1118
|
+
const parsedIssue = parseIssueBody(issue);
|
|
1119
|
+
// When: 循環依存をチェック
|
|
1120
|
+
const result = detectCircularDependencies(parsedIssue);
|
|
1121
|
+
// Then: 循環パスが返る(1.2 → 1.3 → 1.4 → 1.2)
|
|
1122
|
+
expect(result).not.toBeNull();
|
|
1123
|
+
expect(result).toContain("1.2");
|
|
1124
|
+
expect(result).toContain("1.3");
|
|
1125
|
+
expect(result).toContain("1.4");
|
|
1126
|
+
});
|
|
1127
|
+
});
|
|
1128
|
+
});
|
|
1129
|
+
//# sourceMappingURL=dependency-resolver.test.js.map
|