package agenttask import ( "context" "errors" "testing" ) func TestStateMachineLegalAndIllegalTransitions(t *testing.T) { record := WorkRecord{State: WorkStateObserved} for _, next := range []WorkState{ WorkStateReady, WorkStatePreparing, WorkStateDispatching, WorkStateSubmitted, WorkStateReviewing, WorkStatePendingIntegration, WorkStateIntegrating, WorkStateCompleted, } { if err := transitionWork(&record, next); err != nil { t.Fatalf("transition to %s: %v", next, err) } } if err := transitionWork(&record, WorkStateReady); err == nil { t.Fatal("completed -> ready unexpectedly allowed") } } func TestStateMachineDuplicateCommandIdempotency(t *testing.T) { harness := newHarness(t, map[ProjectID]ProjectWorkflowSnapshot{}, 1) request := StartRequest{ CommandID: "command", ProjectID: "project", WorkspaceID: "workspace", MilestoneID: "milestone", WorkflowRevision: "workflow-r1", ConfigRevision: "config-r1", GrantRevision: "grant-r1", } if err := harness.manager.StartProject(context.Background(), request); err != nil { t.Fatalf("first StartProject: %v", err) } if err := harness.manager.StartProject(context.Background(), request); err != nil { t.Fatalf("idempotent StartProject: %v", err) } request.ConfigRevision = "config-r2" if err := harness.manager.StartProject(context.Background(), request); err == nil { t.Fatal("same command with different immutable input accepted") } } func TestStateMachineCorruptIdentityBlocker(t *testing.T) { unit := testUnit("same", WriteSetUnknown) snapshot := testSnapshot("project", "workspace", unit, unit) harness := newHarness(t, map[ProjectID]ProjectWorkflowSnapshot{"project": snapshot}, 1) harness.start("project", "workspace", nil) if err := harness.manager.Reconcile(context.Background()); err != nil { t.Fatalf("Reconcile: %v", err) } project := harness.store.snapshot().Projects["project"] if project.Status != ProjectStatusBlocked || project.Blocker == nil || project.Blocker.Code != BlockerInvalidIdentity { t.Fatalf("project = %#v, want corrupt identity blocker", project) } if harness.invoker.callCount() != 0 { t.Fatalf("corrupt workflow invoked provider %d times", harness.invoker.callCount()) } } func TestNewManagerRequiresStrictExecutionPorts(t *testing.T) { _, err := NewManager( ManagerConfig{OwnerID: "manager"}, nil, nil, nil, nil, nil, nil, nil, nil, nil, ) if err == nil { t.Fatal("NewManager accepted missing strict ports") } var identityErr *IdentityError if _, err = NewManager( ManagerConfig{}, nil, nil, nil, nil, nil, nil, nil, nil, nil, ); !errors.As(err, &identityErr) { t.Fatalf("empty owner error = %v, want IdentityError", err) } } func TestDurableIdentityEncodingIsInjective(t *testing.T) { id1 := durableIdentity("dispatch-v1", "p1/w", "2", "1") id2 := durableIdentity("dispatch-v1", "p1", "w/2", "1") if id1 == id2 { t.Fatalf("durable identity collision: %q == %q", id1, id2) } dk1 := dispatchKey("p1/w", "2", 1) dk2 := dispatchKey("p1", "w/2", 1) if dk1 == dk2 { t.Fatalf("dispatchKey collision: %q == %q", dk1, dk2) } rk1 := reviewKey("p1", "w", 1, "art#1") rk2 := reviewKey("p1", "w#1", 1, "art") if rk1 == rk2 { t.Fatalf("reviewKey collision: %q == %q", rk1, rk2) } } func TestEventIdentityDistinguishesCommandsAndReplays(t *testing.T) { e1 := Event{ Type: EventManualStart, ProjectID: "p1", WorkspaceID: "w1", CommandID: "cmd-1", WorkflowRevision: "rev-1", } e2 := Event{ Type: EventManualStart, ProjectID: "p1", WorkspaceID: "w1", CommandID: "cmd-2", WorkflowRevision: "rev-1", } m := &Manager{clock: systemClock{}, events: &recordingEvents{}} m.emit(context.Background(), e1) m.emit(context.Background(), e2) var id1, id2, id1Replay string m.events = &testSink{onEmit: func(e Event) { id1 = e.EventID }} m.emit(context.Background(), e1) m.events = &testSink{onEmit: func(e Event) { id2 = e.EventID }} m.emit(context.Background(), e2) m.events = &testSink{onEmit: func(e Event) { id1Replay = e.EventID }} m.emit(context.Background(), e1) if id1 == id2 { t.Fatalf("different commands produced identical EventID: %q", id1) } if id1 != id1Replay { t.Fatalf("exact replay produced different EventID: %q vs %q", id1, id1Replay) } } type testSink struct { onEmit func(Event) } func (s *testSink) Emit(_ context.Context, e Event) error { if s.onEmit != nil { s.onEmit(e) } return nil } func TestInterruptedResumeEmitsStableEvent(t *testing.T) { snapshot := testSnapshot("project", "workspace", testUnit("work", WriteSetUnknown)) harness := newHarness(t, map[ProjectID]ProjectWorkflowSnapshot{"project": snapshot}, 1) harness.start("project", "workspace", nil) harness.store.edit(func(state *ManagerState) { project := state.Projects["project"] project.Status = ProjectStatusRunning state.Projects["project"] = project }) if err := harness.manager.Reconcile(context.Background()); err != nil { t.Fatalf("Reconcile: %v", err) } eventsEmitted := harness.events.snapshot() var hasAutoResume bool for _, e := range eventsEmitted { if e.Type == EventAutoResume { hasAutoResume = true if e.CommandID == "" || e.WorkflowRevision == "" { t.Fatalf("EventAutoResume missing logical discriminator: %#v", e) } } } if !hasAutoResume { t.Fatalf("auto resume did not emit EventAutoResume event") } }