package runtime_test import ( "context" "testing" "chat-agent-system/internal/projectinstance" "chat-agent-system/internal/workflow/runtime" "chat-agent-system/internal/workflowdef" ) type liveInstanceAdapter struct { svc *projectinstance.Service } func (a liveInstanceAdapter) Available() bool { return a.svc != nil && a.svc.Available() } func (a liveInstanceAdapter) GetLiveInstanceDetail(ctx context.Context, projectID int64) (*runtime.LiveInstanceView, *runtime.VersionView, error) { detail, version, err := a.svc.GetLiveInstanceDetail(ctx, projectID) if err != nil { return nil, nil, err } return mapLiveInstance(detail), mapVersionView(version), nil } func mapLiveInstance(detail *projectinstance.InstanceDetail) *runtime.LiveInstanceView { if detail == nil { return nil } view := &runtime.LiveInstanceView{ InstanceID: detail.Instance.ID, ConfigSnapshot: runtime.WorkflowPlanView{ Stages: mapStagePlans(detail.Instance.ConfigSnapshot.Stages), }, Stages: mapStageInstances(detail.Stages), } return view } func mapStagePlans(stages []projectinstance.StagePlan) []runtime.StagePlanView { out := make([]runtime.StagePlanView, 0, len(stages)) for _, st := range stages { sp := runtime.StagePlanView{StageKey: st.StageKey} for _, dt := range st.DocTasks { sp.DocTasks = append(sp.DocTasks, runtime.DocTaskPlanView{ TaskKey: dt.TaskKey, Duties: runtime.DutyView{ AuthorUserID: dt.Duties.AuthorUserID, ReviewerUserID: dt.Duties.ReviewerUserID, ApproverUserID: dt.Duties.ApproverUserID, }, }) } out = append(out, sp) } return out } func mapStageInstances(stages []projectinstance.StageInstance) []runtime.StageInstanceView { out := make([]runtime.StageInstanceView, 0, len(stages)) for _, st := range stages { sv := runtime.StageInstanceView{StageKey: st.StageKey} for _, dt := range st.DocTasks { sv.DocTasks = append(sv.DocTasks, runtime.DocTaskInstanceView{ ID: dt.ID, TaskKey: dt.TaskKey, }) } out = append(out, sv) } return out } func mapVersionView(version *workflowdef.Version) *runtime.VersionView { if version == nil { return nil } view := &runtime.VersionView{} for _, st := range version.Definition.Stages { sv := runtime.StageDefView{Key: st.Key} for _, dt := range st.DocTasks { dv := runtime.DocTaskDefView{ Key: dt.Key, ExecutorKey: dt.ExecutorKey, QualityGateKey: dt.QualityGateKey, TemplateAssetKey: dt.TemplateAssetKey, TemplateVersion: dt.TemplateVersion, } if dt.Template != nil { dv.Template = &runtime.TemplateRefView{ AssetKey: dt.Template.AssetKey, Version: dt.Template.Version, } } sv.DocTasks = append(sv.DocTasks, dv) } view.Definition.Stages = append(view.Definition.Stages, sv) } return view } func TestResolveDocTask_NoneDutiesStayZero(t *testing.T) { ctx := context.Background() store := projectinstance.NewMemoryStore() if err := projectinstance.SeedProjectMeta(store, 500, projectinstance.LifecyclePlanning, "", 1, 0); err != nil { t.Fatal(err) } svc := projectinstance.NewService(store, projectinstance.OptionalDutyResolver(), nil, nil) if _, err := svc.SaveWorkflowPlan(ctx, 500, 1, true, projectinstance.SavePlanInput{ Plan: projectinstance.OptionalDutyPlan( projectinstance.DocDuty{AuthorUserID: 1, ReviewerUserID: 2, ApproverUserID: 3}, projectinstance.DocDuty{}, ), ExpectedPlanRevision: 0, ExpectedSelectionVersion: 1, }); err != nil { t.Fatalf("save: %v", err) } preview, err := svc.ActivationPreview(ctx, 500, 1, true) if err != nil { t.Fatalf("preview: %v", err) } if _, err := svc.Activate(ctx, 500, 1, true, projectinstance.ActivateInput{ ConfirmationToken: preview.ConfirmationToken, ExpectedProjectVersion: preview.ExpectedProjectVersion, ExpectedPlanRevision: preview.ExpectedPlanRevision, }); err != nil { t.Fatalf("activate: %v", err) } detail, _, err := svc.GetLiveInstanceDetail(ctx, 500) if err != nil { t.Fatal(err) } var researchID int64 for _, st := range detail.Stages { for _, dt := range st.DocTasks { if dt.TaskKey == "research" { researchID = dt.ID } } } if researchID == 0 { t.Fatal("expected research doc task instance id") } res, err := runtime.NewOrchestrator(liveInstanceAdapter{svc: svc}).ResolveDocTask(ctx, 500, researchID) if err != nil { t.Fatalf("ResolveDocTask: %v", err) } if res.AuthorUserID != 0 || res.ReviewerUserID != 0 || res.ApproverUserID != 0 { t.Fatalf("none duties must stay zero without creator fallback, got %+v", res) } }