Unit evidence for AE5a/AE5b filters; optional PG cases when TEST_DATABASE_URL is set. Co-authored-by: Cursor <cursoragent@cursor.com>
97 lines
3.2 KiB
Go
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)
|
|
}
|
|
}
|