You've already forked agentic-coding-workflow
ACP / MCP
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
package acp_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/yan1h/agent-coding-workflow/internal/acp"
|
||||
"github.com/yan1h/agent-coding-workflow/internal/infra/errs"
|
||||
)
|
||||
|
||||
func newConfigFileTestSvc(t *testing.T) (acp.AgentKindService, *acp.AgentKind, acp.Caller) {
|
||||
t.Helper()
|
||||
repo := newFakeAgentKindRepo()
|
||||
svc := acp.NewAgentKindService(repo, testEncryptor(t), &agentKindRecordingRecorder{})
|
||||
admin := acp.Caller{UserID: uuid.New(), IsAdmin: true}
|
||||
k, err := svc.Create(context.Background(), admin, acp.CreateAgentKindInput{
|
||||
Name: "claude", DisplayName: "Claude", BinaryPath: "/x",
|
||||
ClientType: acp.ClientClaudeCode, Enabled: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
return svc, k, admin
|
||||
}
|
||||
|
||||
func TestConfigFiles_UpsertListDelete_Roundtrip(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
svc, k, admin := newConfigFileTestSvc(t)
|
||||
|
||||
content := `{"model":"claude-sonnet-4-6"}`
|
||||
out, err := svc.UpsertConfigFile(ctx, admin, k.ID, ".claude/settings.json", content)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, ".claude/settings.json", out.RelPath)
|
||||
assert.Equal(t, content, out.Content)
|
||||
|
||||
list, err := svc.ListConfigFiles(ctx, admin, k.ID)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, list, 1)
|
||||
assert.Equal(t, content, list[0].Content) // 解密往返
|
||||
|
||||
require.NoError(t, svc.DeleteConfigFile(ctx, admin, k.ID, ".claude/settings.json"))
|
||||
list, err = svc.ListConfigFiles(ctx, admin, k.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Empty(t, list)
|
||||
}
|
||||
|
||||
func TestConfigFiles_AdminOnly(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
svc, k, _ := newConfigFileTestSvc(t)
|
||||
user := acp.Caller{UserID: uuid.New(), IsAdmin: false}
|
||||
|
||||
_, err := svc.ListConfigFiles(ctx, user, k.ID)
|
||||
ae, ok := errs.As(err)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, errs.CodeForbidden, ae.Code)
|
||||
|
||||
_, err = svc.UpsertConfigFile(ctx, user, k.ID, "a.json", "{}")
|
||||
ae, ok = errs.As(err)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, errs.CodeForbidden, ae.Code)
|
||||
}
|
||||
|
||||
func TestConfigFiles_RelPathValidation(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
svc, k, admin := newConfigFileTestSvc(t)
|
||||
|
||||
bad := []string{
|
||||
"", "/etc/passwd", "../escape.json", "a/../../b.json",
|
||||
"C:/windows/x.json", "..", ".",
|
||||
}
|
||||
for _, p := range bad {
|
||||
_, err := svc.UpsertConfigFile(ctx, admin, k.ID, p, "{}")
|
||||
ae, ok := errs.As(err)
|
||||
require.True(t, ok, "rel_path %q should be rejected", p)
|
||||
assert.Equal(t, errs.CodeInvalidInput, ae.Code, "rel_path %q", p)
|
||||
}
|
||||
|
||||
// 反斜杠归一为正斜杠;./ 前缀被 Clean 掉
|
||||
out, err := svc.UpsertConfigFile(ctx, admin, k.ID, `.codex\config.toml`, `model = "gpt-5.5"`)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, ".codex/config.toml", out.RelPath)
|
||||
out, err = svc.UpsertConfigFile(ctx, admin, k.ID, "./a/b.json", "{}")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "a/b.json", out.RelPath)
|
||||
}
|
||||
|
||||
func TestConfigFiles_SyntaxValidation(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
svc, k, admin := newConfigFileTestSvc(t)
|
||||
|
||||
_, err := svc.UpsertConfigFile(ctx, admin, k.ID, ".claude/settings.json", "{not json")
|
||||
ae, ok := errs.As(err)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, errs.CodeInvalidInput, ae.Code)
|
||||
|
||||
_, err = svc.UpsertConfigFile(ctx, admin, k.ID, ".codex/config.toml", "= broken toml")
|
||||
ae, ok = errs.As(err)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, errs.CodeInvalidInput, ae.Code)
|
||||
|
||||
// 未识别扩展名不校验
|
||||
_, err = svc.UpsertConfigFile(ctx, admin, k.ID, ".claude/CLAUDE.md", "# 任意文本")
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestConfigFiles_DeleteNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
svc, k, admin := newConfigFileTestSvc(t)
|
||||
|
||||
err := svc.DeleteConfigFile(ctx, admin, k.ID, "missing.json")
|
||||
ae, ok := errs.As(err)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, errs.CodeNotFound, ae.Code)
|
||||
}
|
||||
|
||||
func TestAgentKindService_ClientType(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
repo := newFakeAgentKindRepo()
|
||||
svc := acp.NewAgentKindService(repo, testEncryptor(t), nil)
|
||||
admin := acp.Caller{UserID: uuid.New(), IsAdmin: true}
|
||||
|
||||
// 缺省 → generic
|
||||
k, err := svc.Create(ctx, admin, acp.CreateAgentKindInput{
|
||||
Name: "k1", DisplayName: "K1", BinaryPath: "/x", Enabled: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, acp.ClientGeneric, k.ClientType)
|
||||
|
||||
// 非法值拒绝
|
||||
_, err = svc.Create(ctx, admin, acp.CreateAgentKindInput{
|
||||
Name: "k2", DisplayName: "K2", BinaryPath: "/x", ClientType: "bogus", Enabled: true,
|
||||
})
|
||||
ae, ok := errs.As(err)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, errs.CodeInvalidInput, ae.Code)
|
||||
|
||||
// 更新 client_type
|
||||
ct := acp.ClientCodex
|
||||
updated, err := svc.Update(ctx, admin, k.ID, acp.UpdateAgentKindInput{ClientType: &ct})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, acp.ClientCodex, updated.ClientType)
|
||||
|
||||
bad := acp.ClientType("nope")
|
||||
_, err = svc.Update(ctx, admin, k.ID, acp.UpdateAgentKindInput{ClientType: &bad})
|
||||
ae, ok = errs.As(err)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, errs.CodeInvalidInput, ae.Code)
|
||||
}
|
||||
Reference in New Issue
Block a user