Files
Agent-System/internal/app/instance_duty_adapter_test.go
weichenandCursor 67b3b5e070 test: cover duty lock, heal/revoke skip, and instance duty adapter
Unit evidence for AE5a/AE5b filters; optional PG cases when
TEST_DATABASE_URL is set.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-17 09:59:38 +00:00

97 lines
3.2 KiB
Go

package app
import (
"context"
"sync"
"testing"
"chat-agent-system/internal/projectinstance"
"github.com/jackc/pgx/v5"
)
type stubAuthorOnlyPolicy struct {
allowed bool
revision int64
}
func (s *stubAuthorOnlyPolicy) GetAuthorOnlyApprovalPolicy(_ context.Context) (projectinstance.AuthorOnlyPolicySnapshot, error) {
return projectinstance.AuthorOnlyPolicySnapshot{Allowed: s.allowed, Revision: s.revision}, nil
}
func (s *stubAuthorOnlyPolicy) LockAuthorOnlyApprovalPolicy(ctx context.Context, _ pgx.Tx) (projectinstance.AuthorOnlyPolicySnapshot, error) {
return s.GetAuthorOnlyApprovalPolicy(ctx)
}
type stubMemberLookup struct {
mu sync.Mutex
ids map[int64]struct{}
}
func (s *stubMemberLookup) ActiveMemberIDs(_ context.Context, _ int64) (map[int64]struct{}, error) {
s.mu.Lock()
defer s.mu.Unlock()
out := make(map[int64]struct{}, len(s.ids))
for id := range s.ids {
out[id] = struct{}{}
}
return out, nil
}
func TestProjectInstanceDutyAdapter_AuthorOnlyLive(t *testing.T) {
ctx := context.Background()
store := projectinstance.NewMemoryStore()
_ = projectinstance.SeedProjectMeta(store, 501, projectinstance.LifecyclePlanning, "", 1, 0)
svc := projectinstance.NewService(store, projectinstance.TestResolver(), nil, nil)
svc.SetAuthorOnlyPolicySource(&stubAuthorOnlyPolicy{allowed: true, revision: 1})
svc.SetActiveMemberLookup(&stubMemberLookup{ids: map[int64]struct{}{7: {}}})
plan := projectinstance.ValidTestPlan()
plan.Stages[0].DocTasks[0].DutyPolicy = projectinstance.DutyPolicyAuthorOnly
plan.Stages[0].DocTasks[0].Duties = projectinstance.DocDuty{AuthorUserID: 7}
if _, err := svc.SaveWorkflowPlan(ctx, 501, 1, true, projectinstance.SavePlanInput{
Plan: plan, ExpectedPlanRevision: 0, ExpectedSelectionVersion: 1,
}); err != nil {
t.Fatalf("save: %v", err)
}
preview, err := svc.ActivationPreview(ctx, 501, 1, true)
if err != nil {
t.Fatalf("preview: %v", err)
}
if _, err := svc.Activate(ctx, 501, 1, true, projectinstance.ActivateInput{
ConfirmationToken: preview.ConfirmationToken,
ExpectedProjectVersion: preview.ExpectedProjectVersion,
ExpectedPlanRevision: preview.ExpectedPlanRevision,
}); err != nil {
t.Fatalf("activate: %v", err)
}
adapter := &projectInstanceDutyAdapter{inst: svc}
author, reviewer, approver, policy, governed, err := adapter.ResolveGovernedDuty(ctx, 501, "intake")
if err != nil {
t.Fatalf("resolve: %v", err)
}
if !governed || policy != projectinstance.DutyPolicyAuthorOnly {
t.Fatalf("governed=%v policy=%q", governed, policy)
}
if author != 7 || reviewer != 7 || approver != 7 {
t.Fatalf("want 7/7/7 got %d/%d/%d", author, reviewer, approver)
}
}
func TestProjectInstanceDutyAdapter_NoLiveInstance(t *testing.T) {
ctx := context.Background()
store := projectinstance.NewMemoryStore()
_ = projectinstance.SeedProjectMeta(store, 502, projectinstance.LifecyclePlanning, "", 1, 0)
svc := projectinstance.NewService(store, projectinstance.TestResolver(), nil, nil)
adapter := &projectInstanceDutyAdapter{inst: svc}
_, _, _, policy, governed, err := adapter.ResolveGovernedDuty(ctx, 502, "intake")
if err != nil {
t.Fatalf("resolve: %v", err)
}
if governed || policy != "" {
t.Fatalf("want ungoverned, got governed=%v policy=%q", governed, policy)
}
}