package projectinstance import ( "context" "testing" "chat-agent-system/internal/workflowdef" ) func TestCheckDocTaskAccess_DisabledDocumentRejected(t *testing.T) { ctx := context.Background() store := NewMemoryStore() _ = SeedProjectMeta(store, 400, LifecyclePlanning, "", 1, 0) resolver := disabledDocResolver() svc := NewService(store, resolver, nil, nil) version := resolver.versions[workflowdef.OfficialKeyMain] plan := buildDefaultPlan(version) for i := range plan.Stages[0].DocTasks { if plan.Stages[0].DocTasks[i].TaskKey == "research" { plan.Stages[0].DocTasks[i].Enabled = false continue } plan.Stages[0].DocTasks[i].Duties = DocDuty{AuthorUserID: 1, ReviewerUserID: 2, ApproverUserID: 3} } _, _ = svc.SaveWorkflowPlan(ctx, 400, 1, true, SavePlanInput{Plan: plan, ExpectedPlanRevision: 0, ExpectedSelectionVersion: 1}) preview, _ := svc.ActivationPreview(ctx, 400, 1, true) _, _ = svc.Activate(ctx, 400, 1, true, ActivateInput{ ConfirmationToken: preview.ConfirmationToken, ExpectedProjectVersion: preview.ExpectedProjectVersion, ExpectedPlanRevision: preview.ExpectedPlanRevision, }) decision := svc.CheckDocTaskAccess(ctx, 400, 1, "research", DocAccessActionRun, true) if decision.Allowed || decision.Code != AccessCodeDocTaskDisabled { t.Fatalf("expected DOC_TASK_DISABLED, got %+v", decision) } } func TestCheckDocTaskAccess_NonDutyUserRejected(t *testing.T) { ctx := context.Background() store := NewMemoryStore() _ = SeedProjectMeta(store, 401, LifecyclePlanning, "", 1, 0) svc := NewService(store, TestResolver(), nil, nil) _, _ = svc.SaveWorkflowPlan(ctx, 401, 1, true, SavePlanInput{ Plan: ValidTestPlan(), ExpectedPlanRevision: 0, ExpectedSelectionVersion: 1, }) preview, _ := svc.ActivationPreview(ctx, 401, 1, true) _, _ = svc.Activate(ctx, 401, 1, true, ActivateInput{ ConfirmationToken: preview.ConfirmationToken, ExpectedProjectVersion: preview.ExpectedProjectVersion, ExpectedPlanRevision: preview.ExpectedPlanRevision, }) decision := svc.CheckDocTaskAccess(ctx, 401, 99, "intake", DocAccessActionRun, false) if decision.Allowed || decision.Code != AccessCodeForbiddenDuty { t.Fatalf("expected DOC_DUTY_FORBIDDEN, got %+v", decision) } } func TestCheckDocTaskAccess_UnmanagedDutyModes(t *testing.T) { ctx := context.Background() store := NewMemoryStore() _ = SeedProjectMeta(store, 402, LifecyclePlanning, "", 1, 0) svc := NewService(store, optionalDutyResolver(), nil, nil) plan := optionalDutyPlan( DocDuty{AuthorUserID: 1, ReviewerUserID: 2, ApproverUserID: 3}, DocDuty{}, ) if _, err := svc.SaveWorkflowPlan(ctx, 402, 1, true, SavePlanInput{ Plan: plan, ExpectedPlanRevision: 0, ExpectedSelectionVersion: 1, }); err != nil { t.Fatalf("save: %v", err) } preview, err := svc.ActivationPreview(ctx, 402, 1, true) if err != nil { t.Fatalf("preview: %v", err) } if _, err := svc.Activate(ctx, 402, 1, true, ActivateInput{ ConfirmationToken: preview.ConfirmationToken, ExpectedProjectVersion: preview.ExpectedProjectVersion, ExpectedPlanRevision: preview.ExpectedPlanRevision, }); err != nil { t.Fatalf("activate: %v", err) } view := svc.CheckDocTaskAccess(ctx, 402, 99, "research", DocAccessActionView, false) if !view.Allowed { t.Fatalf("member view on none must allow, got %+v", view) } runMember := svc.CheckDocTaskAccess(ctx, 402, 99, "research", DocAccessActionRun, false) if runMember.Allowed || runMember.Code != AccessCodeForbiddenDuty { t.Fatalf("member run on none must deny, got %+v", runMember) } runCreator := svc.CheckDocTaskAccess(ctx, 402, 1, "research", DocAccessActionRun, true) if !runCreator.Allowed { t.Fatalf("creator run on none must allow, got %+v", runCreator) } review := svc.CheckDocTaskAccess(ctx, 402, 1, "research", DocAccessActionReview, true) if review.Allowed || review.Code != AccessCodeDutyNotManaged { t.Fatalf("review on none must be DOC_DUTY_NOT_MANAGED, got %+v", review) } approve := svc.CheckDocTaskAccess(ctx, 402, 1, "research", DocAccessActionApprove, true) if approve.Allowed || approve.Code != AccessCodeDutyNotManaged { t.Fatalf("approve on none must be DOC_DUTY_NOT_MANAGED, got %+v", approve) } }