You've already forked agentic-coding-workflow
103 lines
3.2 KiB
Go
103 lines
3.2 KiB
Go
package chat
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/yan1h/agent-coding-workflow/internal/infra/errs"
|
|
)
|
|
|
|
// tplFakeRepo embeds fakeRepo and overrides prompt template methods.
|
|
type tplFakeRepo struct {
|
|
*fakeRepo
|
|
templates map[uuid.UUID]*PromptTemplate
|
|
}
|
|
|
|
func newTplFakeRepo() *tplFakeRepo {
|
|
return &tplFakeRepo{fakeRepo: newFakeRepo(), templates: map[uuid.UUID]*PromptTemplate{}}
|
|
}
|
|
|
|
func (r *tplFakeRepo) GetPromptTemplate(_ context.Context, id uuid.UUID) (*PromptTemplate, error) {
|
|
return r.templates[id], nil // 未命中返回 (nil, nil),与真实 repo 语义一致
|
|
}
|
|
func (r *tplFakeRepo) UpdatePromptTemplate(_ context.Context, id uuid.UUID, name, content string) error {
|
|
t, ok := r.templates[id]
|
|
if !ok {
|
|
return errs.New(errs.CodeChatTemplateNotFound, "template not found")
|
|
}
|
|
t.Name, t.Content = name, content
|
|
return nil
|
|
}
|
|
func (r *tplFakeRepo) DeletePromptTemplate(_ context.Context, id uuid.UUID) error {
|
|
delete(r.templates, id)
|
|
return nil
|
|
}
|
|
|
|
func newTplSvc(repo *tplFakeRepo) TemplateService {
|
|
return NewTemplateService(repo, noopAuditor{}, discardLogger())
|
|
}
|
|
|
|
func addTplFixture(repo *tplFakeRepo, scope TemplateScope, owner *uuid.UUID) *PromptTemplate {
|
|
t := &PromptTemplate{ID: uuid.New(), Name: "tpl", Content: "c", Scope: scope, OwnerID: owner}
|
|
repo.templates[t.ID] = t
|
|
return t
|
|
}
|
|
|
|
func requireForbidden(t *testing.T, err error) {
|
|
t.Helper()
|
|
ae, ok := errs.As(err)
|
|
require.True(t, ok)
|
|
assert.Equal(t, errs.CodeForbidden, ae.Code)
|
|
}
|
|
|
|
func TestTemplateUpdate_SystemScopeRequiresAdmin(t *testing.T) {
|
|
repo := newTplFakeRepo()
|
|
svc := newTplSvc(repo)
|
|
sys := addTplFixture(repo, TemplateScopeSystem, nil)
|
|
uid := uuid.New()
|
|
|
|
// 非 admin → 拒绝
|
|
err := svc.Update(context.Background(), uid, false, sys.ID, "x", "y")
|
|
requireForbidden(t, err)
|
|
assert.Equal(t, "tpl", repo.templates[sys.ID].Name)
|
|
|
|
// admin → 允许
|
|
require.NoError(t, svc.Update(context.Background(), uid, true, sys.ID, "x", "y"))
|
|
assert.Equal(t, "x", repo.templates[sys.ID].Name)
|
|
}
|
|
|
|
func TestTemplateDelete_SystemScopeRequiresAdmin(t *testing.T) {
|
|
repo := newTplFakeRepo()
|
|
svc := newTplSvc(repo)
|
|
sys := addTplFixture(repo, TemplateScopeSystem, nil)
|
|
uid := uuid.New()
|
|
|
|
err := svc.Delete(context.Background(), uid, false, sys.ID)
|
|
requireForbidden(t, err)
|
|
assert.Contains(t, repo.templates, sys.ID)
|
|
|
|
require.NoError(t, svc.Delete(context.Background(), uid, true, sys.ID))
|
|
assert.NotContains(t, repo.templates, sys.ID)
|
|
}
|
|
|
|
func TestTemplateUpdateDelete_UserScopeOwnerOnly(t *testing.T) {
|
|
repo := newTplFakeRepo()
|
|
svc := newTplSvc(repo)
|
|
owner := uuid.New()
|
|
tpl := addTplFixture(repo, TemplateScopeUser, &owner)
|
|
stranger := uuid.New()
|
|
|
|
// 非 owner(即使 admin)→ 拒绝,保持既有 owner-only 语义
|
|
requireForbidden(t, svc.Update(context.Background(), stranger, true, tpl.ID, "x", "y"))
|
|
requireForbidden(t, svc.Delete(context.Background(), stranger, true, tpl.ID))
|
|
|
|
// owner → 允许
|
|
require.NoError(t, svc.Update(context.Background(), owner, false, tpl.ID, "x", "y"))
|
|
require.NoError(t, svc.Delete(context.Background(), owner, false, tpl.ID))
|
|
assert.NotContains(t, repo.templates, tpl.ID)
|
|
}
|