You've already forked agentic-coding-workflow
199 lines
6.7 KiB
Go
199 lines
6.7 KiB
Go
package mcp
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/yan1h/agent-coding-workflow/internal/infra/errs"
|
|
"github.com/yan1h/agent-coding-workflow/internal/run"
|
|
"github.com/yan1h/agent-coding-workflow/internal/workspace"
|
|
)
|
|
|
|
// ===== fakes =====
|
|
|
|
// fakeRunSvc implements RunService.
|
|
type fakeRunSvc struct {
|
|
profiles []run.RunProfileResp
|
|
lastOp string
|
|
logs []run.LogLineResp
|
|
}
|
|
|
|
func (f *fakeRunSvc) List(_ context.Context, _ workspace.Caller, wsID uuid.UUID) ([]run.RunProfileResp, error) {
|
|
out := make([]run.RunProfileResp, 0, len(f.profiles))
|
|
for _, p := range f.profiles {
|
|
if p.WorkspaceID == wsID.String() {
|
|
out = append(out, p)
|
|
}
|
|
}
|
|
return out, nil
|
|
}
|
|
func (f *fakeRunSvc) Create(_ context.Context, _ workspace.Caller, wsID uuid.UUID, req run.CreateRunProfileReq) (run.RunProfileResp, error) {
|
|
p := run.RunProfileResp{
|
|
ID: uuid.NewString(), WorkspaceID: wsID.String(),
|
|
Slug: req.Slug, Name: req.Name, Command: req.Command,
|
|
Status: string(run.StatusStopped),
|
|
}
|
|
f.profiles = append(f.profiles, p)
|
|
return p, nil
|
|
}
|
|
func (f *fakeRunSvc) Update(_ context.Context, _ workspace.Caller, profileID uuid.UUID, req run.UpdateRunProfileReq) (run.RunProfileResp, error) {
|
|
for i := range f.profiles {
|
|
if f.profiles[i].ID == profileID.String() {
|
|
if req.Name != nil {
|
|
f.profiles[i].Name = *req.Name
|
|
}
|
|
return f.profiles[i], nil
|
|
}
|
|
}
|
|
return run.RunProfileResp{}, errs.New(errs.CodeNotFound, "profile")
|
|
}
|
|
func (f *fakeRunSvc) Delete(_ context.Context, _ workspace.Caller, profileID uuid.UUID) error {
|
|
kept := f.profiles[:0]
|
|
for _, p := range f.profiles {
|
|
if p.ID != profileID.String() {
|
|
kept = append(kept, p)
|
|
}
|
|
}
|
|
f.profiles = kept
|
|
return nil
|
|
}
|
|
func (f *fakeRunSvc) Start(_ context.Context, _ workspace.Caller, _ uuid.UUID) (run.RunStatusResp, error) {
|
|
f.lastOp = "start"
|
|
return run.RunStatusResp{Status: string(run.StatusRunning)}, nil
|
|
}
|
|
func (f *fakeRunSvc) Stop(_ context.Context, _ workspace.Caller, _ uuid.UUID) (run.RunStatusResp, error) {
|
|
f.lastOp = "stop"
|
|
return run.RunStatusResp{Status: string(run.StatusStopped)}, nil
|
|
}
|
|
func (f *fakeRunSvc) Restart(_ context.Context, _ workspace.Caller, _ uuid.UUID) (run.RunStatusResp, error) {
|
|
f.lastOp = "restart"
|
|
return run.RunStatusResp{Status: string(run.StatusRunning)}, nil
|
|
}
|
|
func (f *fakeRunSvc) Status(_ context.Context, _ workspace.Caller, _ uuid.UUID) (run.RunStatusResp, error) {
|
|
f.lastOp = "status"
|
|
return run.RunStatusResp{Status: string(run.StatusRunning)}, nil
|
|
}
|
|
func (f *fakeRunSvc) Logs(_ context.Context, _ workspace.Caller, _ uuid.UUID, _ int64, limit int) ([]run.LogLineResp, error) {
|
|
if len(f.logs) > limit {
|
|
return f.logs[:limit], nil
|
|
}
|
|
return f.logs, nil
|
|
}
|
|
|
|
func runTestDeps() (ServerDeps, *fakeRunSvc) {
|
|
deps := testDeps()
|
|
deps.Workspaces = &fakeWorkspaceSvc{items: []*workspace.Workspace{{
|
|
ID: testWSID, ProjectID: testPID, Slug: "ws-1", Name: "Workspace 1",
|
|
DefaultBranch: "main", SyncStatus: workspace.SyncStatusIdle, CreatedAt: time.Now(),
|
|
}}}
|
|
runSvc := &fakeRunSvc{}
|
|
deps.Runs = runSvc
|
|
return deps, runSvc
|
|
}
|
|
|
|
// ===== tests =====
|
|
|
|
func TestRunProfileCRUD(t *testing.T) {
|
|
deps, runSvc := runTestDeps()
|
|
|
|
result, err := callTool(ctxWithAuth(Scope{}), deps, "create_run_profile", map[string]any{
|
|
"workspace_id": testWSID.String(), "slug": "dev", "name": "Dev", "command": "pnpm dev",
|
|
})
|
|
require.NoError(t, err)
|
|
require.False(t, result.IsError)
|
|
var createOut runProfileOut
|
|
unmarshalStructured(t, result, &createOut)
|
|
assert.True(t, createOut.OK)
|
|
assert.Equal(t, "dev", createOut.Profile.Slug)
|
|
pid := createOut.Profile.ID
|
|
|
|
result, err = callTool(ctxWithAuth(Scope{}), deps, "list_run_profiles", map[string]any{
|
|
"workspace_id": testWSID.String(),
|
|
})
|
|
require.NoError(t, err)
|
|
var listOut listRunProfilesOut
|
|
unmarshalStructured(t, result, &listOut)
|
|
require.Len(t, listOut.Profiles, 1)
|
|
|
|
result, err = callTool(ctxWithAuth(Scope{}), deps, "update_run_profile", map[string]any{
|
|
"workspace_id": testWSID.String(), "profile_id": pid, "name": "Dev2",
|
|
})
|
|
require.NoError(t, err)
|
|
unmarshalStructured(t, result, &createOut)
|
|
assert.Equal(t, "Dev2", createOut.Profile.Name)
|
|
|
|
result, err = callTool(ctxWithAuth(Scope{}), deps, "delete_run_profile", map[string]any{
|
|
"workspace_id": testWSID.String(), "profile_id": pid,
|
|
})
|
|
require.NoError(t, err)
|
|
require.False(t, result.IsError)
|
|
assert.Empty(t, runSvc.profiles)
|
|
}
|
|
|
|
func TestRunProfileLifecycleOps(t *testing.T) {
|
|
deps, runSvc := runTestDeps()
|
|
p, err := runSvc.Create(context.Background(), workspace.Caller{}, testWSID, run.CreateRunProfileReq{
|
|
Slug: "dev", Name: "Dev", Command: "pnpm dev",
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
for _, tc := range []struct{ tool, op, wantStatus string }{
|
|
{"start_run_profile", "start", string(run.StatusRunning)},
|
|
{"stop_run_profile", "stop", string(run.StatusStopped)},
|
|
{"restart_run_profile", "restart", string(run.StatusRunning)},
|
|
{"get_run_status", "status", string(run.StatusRunning)},
|
|
} {
|
|
result, err := callTool(ctxWithAuth(Scope{}), deps, tc.tool, map[string]any{
|
|
"workspace_id": testWSID.String(), "profile_id": p.ID,
|
|
})
|
|
require.NoError(t, err, tc.tool)
|
|
require.False(t, result.IsError, tc.tool)
|
|
var out runStatusOut
|
|
unmarshalStructured(t, result, &out)
|
|
assert.Equal(t, tc.op, runSvc.lastOp, tc.tool)
|
|
assert.Equal(t, tc.wantStatus, out.Status.Status, tc.tool)
|
|
}
|
|
}
|
|
|
|
func TestRunProfileMembershipEnforced(t *testing.T) {
|
|
deps, runSvc := runTestDeps()
|
|
// profile 属于其他 workspace → 不可经本 workspace 操作
|
|
runSvc.profiles = append(runSvc.profiles, run.RunProfileResp{
|
|
ID: uuid.NewString(), WorkspaceID: uuid.NewString(), Slug: "alien", Name: "Alien",
|
|
})
|
|
|
|
result, err := callTool(ctxWithAuth(Scope{}), deps, "start_run_profile", map[string]any{
|
|
"workspace_id": testWSID.String(), "profile_id": runSvc.profiles[0].ID,
|
|
})
|
|
require.NoError(t, err)
|
|
assert.True(t, result.IsError)
|
|
assert.Empty(t, runSvc.lastOp)
|
|
}
|
|
|
|
func TestGetRunLogs(t *testing.T) {
|
|
deps, runSvc := runTestDeps()
|
|
p, err := runSvc.Create(context.Background(), workspace.Caller{}, testWSID, run.CreateRunProfileReq{
|
|
Slug: "dev", Name: "Dev", Command: "pnpm dev",
|
|
})
|
|
require.NoError(t, err)
|
|
runSvc.logs = []run.LogLineResp{
|
|
{ID: 1, Level: "out", Text: "listening on :3000", TS: "2026-06-10T00:00:00Z"},
|
|
{ID: 2, Level: "err", Text: "warn: deprecated", TS: "2026-06-10T00:00:01Z"},
|
|
}
|
|
|
|
result, err := callTool(ctxWithAuth(Scope{}), deps, "get_run_logs", map[string]any{
|
|
"workspace_id": testWSID.String(), "profile_id": p.ID,
|
|
})
|
|
require.NoError(t, err)
|
|
require.False(t, result.IsError)
|
|
var out getRunLogsOut
|
|
unmarshalStructured(t, result, &out)
|
|
require.Len(t, out.Lines, 2)
|
|
assert.Equal(t, "listening on :3000", out.Lines[0].Text)
|
|
}
|