Files
agentic-coding-workflow/internal/mcp/tools_pm_decomposition_test.go
2026-06-22 08:55:57 +08:00

154 lines
4.8 KiB
Go

package mcp
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestCreateSubtaskTool(t *testing.T) {
deps := testDeps()
// parent
_, err := callTool(ctxWithAuth(Scope{}), deps, "create_issue", map[string]any{
"project_slug": "test-project", "title": "Parent",
})
require.NoError(t, err)
result, err := callTool(ctxWithAuth(Scope{}), deps, "create_subtask", map[string]any{
"project_slug": "test-project",
"parent_number": float64(1),
"title": "Child",
"priority": float64(2),
})
require.NoError(t, err)
assert.False(t, result.IsError)
var out issueCreateOut
unmarshalStructured(t, result, &out)
assert.True(t, out.OK)
assert.Equal(t, "Child", out.Issue.Title)
}
func TestAddListRemoveDependencyTool(t *testing.T) {
deps := testDeps()
for _, title := range []string{"A", "B"} {
_, err := callTool(ctxWithAuth(Scope{}), deps, "create_issue", map[string]any{
"project_slug": "test-project", "title": title,
})
require.NoError(t, err)
}
// A(#1) blocked-by B(#2)
result, err := callTool(ctxWithAuth(Scope{}), deps, "add_dependency", map[string]any{
"project_slug": "test-project",
"blocked_number": float64(1),
"blocker_number": float64(2),
})
require.NoError(t, err)
assert.False(t, result.IsError)
var addOut dependencyOut
unmarshalStructured(t, result, &addOut)
assert.True(t, addOut.OK)
// list_dependencies on A → blocked_by contains B
result, err = callTool(ctxWithAuth(Scope{}), deps, "list_dependencies", map[string]any{
"project_slug": "test-project", "number": float64(1),
})
require.NoError(t, err)
var listOut listDependenciesOut
unmarshalStructured(t, result, &listOut)
require.Len(t, listOut.BlockedBy, 1)
assert.Equal(t, 2, listOut.BlockedBy[0].Number)
// remove
result, err = callTool(ctxWithAuth(Scope{}), deps, "remove_dependency", map[string]any{
"project_slug": "test-project",
"blocked_number": float64(1),
"blocker_number": float64(2),
})
require.NoError(t, err)
assert.False(t, result.IsError)
result, err = callTool(ctxWithAuth(Scope{}), deps, "list_dependencies", map[string]any{
"project_slug": "test-project", "number": float64(1),
})
require.NoError(t, err)
unmarshalStructured(t, result, &listOut)
assert.Empty(t, listOut.BlockedBy)
}
func TestAddDependencyTool_RejectsSelf(t *testing.T) {
deps := testDeps()
_, err := callTool(ctxWithAuth(Scope{}), deps, "create_issue", map[string]any{
"project_slug": "test-project", "title": "A",
})
require.NoError(t, err)
result, err := callTool(ctxWithAuth(Scope{}), deps, "add_dependency", map[string]any{
"project_slug": "test-project",
"blocked_number": float64(1),
"blocker_number": float64(1),
})
require.NoError(t, err)
assert.True(t, result.IsError, "自依赖应被工具拒绝")
}
func TestSubmitDecompositionTool(t *testing.T) {
deps := testDeps()
// anchor parent
_, err := callTool(ctxWithAuth(Scope{}), deps, "create_issue", map[string]any{
"project_slug": "test-project", "title": "Anchor",
})
require.NoError(t, err)
result, err := callTool(ctxWithAuth(Scope{}), deps, "submit_decomposition", map[string]any{
"project_slug": "test-project",
"parent_number": float64(1),
"subtasks": []any{
map[string]any{"ref": "a", "title": "Task A", "priority": float64(2)},
map[string]any{"ref": "b", "title": "Task B"},
},
"edges": []any{
map[string]any{"blocked_ref": "a", "blocker_ref": "b"},
},
})
require.NoError(t, err)
assert.False(t, result.IsError)
var out submitDecompositionOut
unmarshalStructured(t, result, &out)
assert.True(t, out.OK)
require.Contains(t, out.RefToNumber, "a")
require.Contains(t, out.RefToNumber, "b")
}
func TestSubmitDecompositionTool_RejectsCycle(t *testing.T) {
deps := testDeps()
result, err := callTool(ctxWithAuth(Scope{}), deps, "submit_decomposition", map[string]any{
"project_slug": "test-project",
"subtasks": []any{
map[string]any{"ref": "a", "title": "A"},
map[string]any{"ref": "b", "title": "B"},
},
"edges": []any{
map[string]any{"blocked_ref": "a", "blocker_ref": "b"},
map[string]any{"blocked_ref": "b", "blocker_ref": "a"},
},
})
require.NoError(t, err)
assert.True(t, result.IsError, "环应被拒绝")
}
func TestDecompositionToolsInSystemScope(t *testing.T) {
// 新工具必须在 system token 默认白名单内,规划阶段 agent 才能调用。
set := map[string]bool{}
for _, name := range defaultSystemTools {
set[name] = true
}
for _, name := range []string{
"create_subtask", "add_dependency", "remove_dependency",
"list_dependencies", "submit_decomposition",
} {
assert.True(t, set[name], "%s 应在 defaultSystemTools", name)
}
// create_acp_session 仍对 system token 禁用(不应在白名单)。
assert.False(t, set["create_acp_session"], "create_acp_session 不应在 system 白名单")
}