package project import ( "context" "testing" "github.com/google/uuid" "github.com/stretchr/testify/require" "github.com/yan1h/agent-coding-workflow/internal/infra/errs" ) func newReqSvc(repo *fakeRepo) (RequirementService, ProjectService, *spyAudit) { spy := &spyAudit{} return NewRequirementService(repo, spy), NewProjectService(repo, spy), spy } func TestRequirementService_Create_HappyAndAutoIncrement(t *testing.T) { repo := newFakeRepo() owner := uuid.New() seedProject(t, repo, owner) svc, _, spy := newReqSvc(repo) caller := Caller{UserID: owner} r1, err := svc.Create(context.Background(), caller, "demo", CreateRequirementInput{Title: "First"}) require.NoError(t, err) require.Equal(t, 1, r1.Number) r2, err := svc.Create(context.Background(), caller, "demo", CreateRequirementInput{Title: "Second"}) require.NoError(t, err) require.Equal(t, 2, r2.Number) require.Contains(t, spy.actions(), "requirement.create") } func TestRequirementService_PhaseChange_FollowsEnum(t *testing.T) { repo := newFakeRepo() owner := uuid.New() seedProject(t, repo, owner) svc, _, _ := newReqSvc(repo) caller := Caller{UserID: owner} r, err := svc.Create(context.Background(), caller, "demo", CreateRequirementInput{Title: "T"}) require.NoError(t, err) // 任意跳跃合法:planning → reviewing out, err := svc.ChangePhase(context.Background(), caller, "demo", r.Number, PhaseReviewing) require.NoError(t, err) require.Equal(t, PhaseReviewing, out.Phase) // 非枚举字符串 → invalid _, err = svc.ChangePhase(context.Background(), caller, "demo", r.Number, "bogus") ae, ok := errs.As(err) require.True(t, ok) require.Equal(t, errs.CodeInvalidInput, ae.Code) } func TestRequirementService_PhaseChange_BlockedWhenClosed(t *testing.T) { repo := newFakeRepo() owner := uuid.New() seedProject(t, repo, owner) svc, _, _ := newReqSvc(repo) caller := Caller{UserID: owner} r, err := svc.Create(context.Background(), caller, "demo", CreateRequirementInput{Title: "T"}) require.NoError(t, err) require.NoError(t, svc.Close(context.Background(), caller, "demo", r.Number)) _, err = svc.ChangePhase(context.Background(), caller, "demo", r.Number, PhaseAuditing) ae, ok := errs.As(err) require.True(t, ok) require.Equal(t, errs.CodeRequirementClosed, ae.Code) } func TestRequirementService_Write_BlockedWhenProjectArchived(t *testing.T) { repo := newFakeRepo() owner := uuid.New() seedProject(t, repo, owner) reqSvc, projSvc, _ := newReqSvc(repo) caller := Caller{UserID: owner} require.NoError(t, projSvc.Archive(context.Background(), caller, "demo")) _, err := reqSvc.Create(context.Background(), caller, "demo", CreateRequirementInput{Title: "X"}) ae, ok := errs.As(err) require.True(t, ok) require.Equal(t, errs.CodeProjectArchived, ae.Code) } func TestRequirementService_Reopen_AllowsPhaseAgain(t *testing.T) { repo := newFakeRepo() owner := uuid.New() seedProject(t, repo, owner) svc, _, _ := newReqSvc(repo) caller := Caller{UserID: owner} r, err := svc.Create(context.Background(), caller, "demo", CreateRequirementInput{Title: "T"}) require.NoError(t, err) require.NoError(t, svc.Close(context.Background(), caller, "demo", r.Number)) require.NoError(t, svc.Reopen(context.Background(), caller, "demo", r.Number)) out, err := svc.ChangePhase(context.Background(), caller, "demo", r.Number, PhaseDone) require.NoError(t, err) require.Equal(t, PhaseDone, out.Phase) }