Activated projects can update duties, approval policy, and document enablement. Changing the workflow definition stays rejected, and project 143 remains frozen. Co-authored-by: Cursor <cursoragent@cursor.com>
116 lines
3.5 KiB
Go
116 lines
3.5 KiB
Go
package projectinstance
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
|
|
"chat-agent-system/internal/workflowdef"
|
|
)
|
|
|
|
type countingMaterializer struct {
|
|
calls int
|
|
projectID int64
|
|
}
|
|
|
|
func (c *countingMaterializer) MaterializeTemplateDocs(_ context.Context, projectID int64, _ WorkflowPlan) error {
|
|
c.calls++
|
|
c.projectID = projectID
|
|
return nil
|
|
}
|
|
|
|
func TestUpdateLivePlanKeepsWorkflowAndMaterializes(t *testing.T) {
|
|
ctx := context.Background()
|
|
store := newMemStore()
|
|
_ = SeedProjectMeta(store, 42, LifecyclePlanning, "", 1, 0)
|
|
mat := &countingMaterializer{}
|
|
svc := NewService(store, testResolver(), nil, nil)
|
|
svc.SetTemplateDocMaterializer(mat)
|
|
|
|
if _, err := svc.SaveWorkflowPlan(ctx, 42, 1, true, SavePlanInput{
|
|
Plan: validPlan(), ExpectedPlanRevision: 0, ExpectedSelectionVersion: 1,
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
preview, err := svc.ActivationPreview(ctx, 42, 1, true)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := svc.Activate(ctx, 42, 1, true, ActivateInput{
|
|
ConfirmationToken: preview.ConfirmationToken,
|
|
ExpectedProjectVersion: preview.ExpectedProjectVersion,
|
|
ExpectedPlanRevision: preview.ExpectedPlanRevision,
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if mat.calls != 1 || mat.projectID != 42 {
|
|
t.Fatalf("activate materialize calls=%d project=%d", mat.calls, mat.projectID)
|
|
}
|
|
|
|
settings, err := svc.GetSettings(ctx, 42, 1, true)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if settings.PlanEditMode != PlanEditLive {
|
|
t.Fatalf("plan_edit_mode=%s", settings.PlanEditMode)
|
|
}
|
|
plan := *settings.Plan
|
|
plan.Stages[0].DocTasks[0].Duties.ApproverUserID = 4
|
|
updated, err := svc.UpdateLivePlan(ctx, 42, 1, true, SavePlanInput{
|
|
Plan: plan, ExpectedPlanRevision: settings.WorkflowPlanRevision, LiveEdit: true,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if updated.Plan.Stages[0].DocTasks[0].Duties.ApproverUserID != 4 {
|
|
t.Fatalf("duty not updated: %+v", updated.Plan.Stages[0].DocTasks[0].Duties)
|
|
}
|
|
if updated.Plan.WorkflowDefinitionKey != plan.WorkflowDefinitionKey {
|
|
t.Fatal("workflow key changed")
|
|
}
|
|
if mat.calls != 2 {
|
|
t.Fatalf("live update materialize calls=%d", mat.calls)
|
|
}
|
|
|
|
switched := plan
|
|
switched.WorkflowDefinitionKey = "other"
|
|
_, err = svc.UpdateLivePlan(ctx, 42, 1, true, SavePlanInput{
|
|
Plan: switched, ExpectedPlanRevision: updated.WorkflowPlanRevision,
|
|
})
|
|
if !errors.Is(err, ErrWorkflowSwitchFrozen) {
|
|
t.Fatalf("expected switch frozen, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestUpdateLivePlanRejectsReadonlyProject(t *testing.T) {
|
|
svc := NewService(newMemStore(), testResolver(), nil, nil)
|
|
_, err := svc.UpdateLivePlan(context.Background(), readonlyLivePlanProjectID, 1, true, SavePlanInput{
|
|
Plan: validPlan(), ExpectedPlanRevision: 1, LiveEdit: true,
|
|
})
|
|
if !errors.Is(err, ErrPlanFrozen) {
|
|
t.Fatalf("expected frozen, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestPlanEditModeFrozenForReadonlyLiveProject(t *testing.T) {
|
|
ctx := context.Background()
|
|
store := newMemStore()
|
|
_ = SeedProjectMeta(store, readonlyLivePlanProjectID, LifecycleActive, workflowdef.OfficialKeyMain, 2, 1)
|
|
store.live[readonlyLivePlanProjectID] = &WorkflowInstance{
|
|
ID: 89,
|
|
ProjectID: readonlyLivePlanProjectID,
|
|
WorkflowVersionID: 1,
|
|
Status: InstanceActive,
|
|
ConfigRevision: 3,
|
|
ConfigSnapshot: validPlan(),
|
|
}
|
|
svc := NewService(store, testResolver(), nil, nil)
|
|
settings, err := svc.GetSettings(ctx, readonlyLivePlanProjectID, 1, true)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if settings.PlanEditMode != PlanEditFrozen {
|
|
t.Fatalf("plan_edit_mode=%s", settings.PlanEditMode)
|
|
}
|
|
}
|