iop/apps/agent/internal/projectlog/sink_test.go

798 lines
25 KiB
Go

package projectlog
import (
"context"
"errors"
"path/filepath"
"strings"
"testing"
"time"
"iop/packages/go/agentstate"
"iop/packages/go/agenttask"
)
type sinkTestHarness struct {
manager *agentstate.Store
store *Store
sink *Sink
project agenttask.ProjectRecord
}
type replayFailingEvidenceResolver struct {
evidence EventEvidence
calls int
}
func (r *replayFailingEvidenceResolver) ResolveEventEvidence(
_ context.Context,
_ agenttask.Event,
) (EventEvidence, error) {
r.calls++
if r.calls > 1 {
return EventEvidence{}, errors.New("resolver must not be called for stable replay")
}
return r.evidence, nil
}
func newSinkTestHarness(
t *testing.T,
projectID agenttask.ProjectID,
workspaceID agenttask.WorkspaceID,
) *sinkTestHarness {
t.Helper()
root := t.TempDir()
manager, err := agentstate.NewStore(filepath.Join(root, "manager-state.json"))
if err != nil {
t.Fatalf("agentstate.NewStore manager: %v", err)
}
journalState, err := agentstate.NewStore(filepath.Join(root, "projectlog-state.json"))
if err != nil {
t.Fatalf("agentstate.NewStore projectlog: %v", err)
}
store, err := NewStore(journalState, filepath.Join(root, "archives"), projectID, workspaceID)
if err != nil {
t.Fatalf("NewStore: %v", err)
}
resolver, err := NewStateStoreEvidenceResolver(manager)
if err != nil {
t.Fatalf("NewStateStoreEvidenceResolver: %v", err)
}
sink, err := NewSink(store, resolver)
if err != nil {
t.Fatalf("NewSink: %v", err)
}
return &sinkTestHarness{
manager: manager,
store: store,
sink: sink,
project: agenttask.ProjectRecord{
ProjectID: projectID,
WorkspaceID: workspaceID,
Status: agenttask.ProjectStatusRunning,
Intent: &agenttask.StartIntent{
CommandID: "cmd-001",
ProjectID: projectID,
WorkspaceID: workspaceID,
MilestoneID: "milestone-001",
WorkflowRevision: "workflow-rev-1",
ConfigRevision: "config-rev-1",
GrantRevision: "grant-rev-1",
StartedAt: time.Date(2026, 7, 30, 9, 0, 0, 0, time.UTC),
},
Works: make(map[agenttask.WorkUnitID]agenttask.WorkRecord),
},
}
}
func (h *sinkTestHarness) putWork(t *testing.T, work agenttask.WorkRecord) agenttask.StateRevision {
t.Helper()
h.project.Works[work.Unit.ID] = work
state, revision, err := h.manager.Load(context.Background())
if err != nil {
t.Fatalf("manager Load: %v", err)
}
if state.Projects == nil {
state.Projects = make(map[agenttask.ProjectID]agenttask.ProjectRecord)
}
state.Projects[h.project.ProjectID] = h.project
next, err := h.manager.CompareAndSwap(context.Background(), revision, state)
if err != nil {
t.Fatalf("manager CompareAndSwap: %v", err)
}
return next
}
func sinkTestWork(
projectID agenttask.ProjectID,
workspaceID agenttask.WorkspaceID,
workUnitID agenttask.WorkUnitID,
attempt uint32,
state agenttask.WorkState,
) agenttask.WorkRecord {
attemptID := agenttask.AttemptID("attempt-" + string(rune('0'+attempt)))
return agenttask.WorkRecord{
Unit: agenttask.WorkUnit{
ID: workUnitID,
MilestoneID: "milestone-001",
WriteSetKind: agenttask.WriteSetDisjoint,
IsolationMode: "overlay",
},
State: state,
Attempt: attempt,
AttemptID: attemptID,
DispatchOrdinal: 17,
Target: &agenttask.ExecutionTarget{
ProviderID: "provider-exact",
ModelID: "model-exact",
ProfileID: "profile-exact",
ProfileRevision: "profile-rev-exact",
ConfigRevision: "config-rev-exact",
Capacity: 2,
},
Locators: map[agenttask.LocatorKind]agenttask.LocatorRecord{
agenttask.LocatorSession: {
Kind: agenttask.LocatorSession,
Opaque: "session-exact",
Revision: "session-rev-exact",
ProjectID: projectID,
WorkspaceID: workspaceID,
WorkUnitID: workUnitID,
AttemptID: attemptID,
},
agenttask.LocatorProcess: {
Kind: agenttask.LocatorProcess,
Opaque: "process-exact",
Revision: "process-rev-exact",
ProjectID: projectID,
WorkspaceID: workspaceID,
WorkUnitID: workUnitID,
AttemptID: attemptID,
},
},
}
}
func TestSinkMapsEveryEventType(t *testing.T) {
ctx := context.Background()
harness := newSinkTestHarness(t, "proj-sink-1", "ws-sink-1")
work := sinkTestWork(
harness.project.ProjectID,
harness.project.WorkspaceID,
"work-001",
1,
agenttask.WorkStateReviewing,
)
harness.putWork(t, work)
eventTypes := []agenttask.EventType{
agenttask.EventObserved,
agenttask.EventManualStart,
agenttask.EventAutoResume,
agenttask.EventStopped,
agenttask.EventDependencyReady,
agenttask.EventDispatchStarted,
agenttask.EventSubmissionAccepted,
agenttask.EventReviewResult,
agenttask.EventFollowup,
agenttask.EventIntegrationResult,
agenttask.EventBlocked,
agenttask.EventCompleted,
}
now := time.Date(2026, 7, 30, 10, 0, 0, 0, time.UTC)
for index, eventType := range eventTypes {
event := agenttask.Event{
EventID: "event-" + string(rune('a'+index)),
Type: eventType,
ProjectID: harness.project.ProjectID,
WorkspaceID: harness.project.WorkspaceID,
WorkUnitID: work.Unit.ID,
CommandID: harness.project.Intent.CommandID,
Timestamp: now.Add(time.Duration(index) * time.Minute),
}
if err := harness.sink.Emit(ctx, event); err != nil {
t.Fatalf("Emit %s: %v", eventType, err)
}
}
scoped, err := harness.store.ForWorkUnit(work.Unit.ID)
if err != nil {
t.Fatalf("ForWorkUnit: %v", err)
}
records, err := scoped.ReplayRecords(ctx)
if err != nil {
t.Fatalf("ReplayRecords: %v", err)
}
if len(records) != len(eventTypes) {
t.Fatalf("record count = %d, want %d", len(records), len(eventTypes))
}
for index, record := range records {
if record.EventType != eventTypes[index] {
t.Fatalf("record %d type = %s, want %s", index, record.EventType, eventTypes[index])
}
if record.State != agenttask.WorkStateReviewing {
t.Fatalf("record %d inferred state %q", index, record.State)
}
if record.DispatchOrdinal != work.DispatchOrdinal || record.LoopOrdinal != uint64(work.Attempt) {
t.Fatalf("record %d lost exact ordinal evidence: %+v", index, record)
}
}
}
func TestSinkResolvesExactDurableEvidence(t *testing.T) {
ctx := context.Background()
harness := newSinkTestHarness(t, "proj-exact", "ws-exact")
work := sinkTestWork(
harness.project.ProjectID,
harness.project.WorkspaceID,
"work-exact",
3,
agenttask.WorkStateTerminalDeferred,
)
work.ChangeSet = &agenttask.ChangeSetIdentity{
ID: "change-exact",
Revision: "change-rev-exact",
ArtifactID: "artifact-exact",
}
work.IntegrationAttempt = 4
work.Integration = &agenttask.IntegrationResult{
ProjectID: harness.project.ProjectID,
WorkUnitID: work.Unit.ID,
ChangeSet: *work.ChangeSet,
Ordinal: work.DispatchOrdinal,
Attempt: 4,
Outcome: agenttask.IntegrationOutcomeTerminalDeferred,
Retained: true,
BeforeRevision: "base-before",
Blocker: &agenttask.Blocker{
Code: agenttask.BlockerIntegrationFailed,
Message: "safe integration failure",
},
}
work.Blocker = work.Integration.Blocker
wantRevision := harness.putWork(t, work)
event := agenttask.Event{
EventID: "manager-event-exact",
Type: agenttask.EventIntegrationResult,
ProjectID: harness.project.ProjectID,
WorkspaceID: harness.project.WorkspaceID,
WorkUnitID: work.Unit.ID,
AttemptID: work.AttemptID,
Ordinal: work.DispatchOrdinal,
ChangeSetID: work.ChangeSet.ID,
ChangeSetRevision: work.ChangeSet.Revision,
IntegrationAttempt: work.IntegrationAttempt,
State: work.State,
ProviderID: work.Target.ProviderID,
ProfileID: work.Target.ProfileID,
Detail: "terminal integration evidence",
Timestamp: time.Date(2026, 7, 30, 11, 0, 0, 0, time.UTC),
}
if err := harness.sink.Emit(ctx, event); err != nil {
t.Fatalf("Emit: %v", err)
}
scoped, _ := harness.store.ForWorkUnit(work.Unit.ID)
records, err := scoped.ReplayRecords(ctx)
if err != nil || len(records) != 1 {
t.Fatalf("ReplayRecords = %d, %v", len(records), err)
}
record := records[0]
if record.StateRevision != wantRevision ||
record.LoopOrdinal != uint64(work.Attempt) ||
record.AttemptID != work.AttemptID ||
record.DispatchOrdinal != work.DispatchOrdinal {
t.Fatalf("exact state/attempt evidence lost: %+v", record)
}
if record.RouteStatus == nil ||
record.RouteStatus.ProviderID != work.Target.ProviderID ||
record.RouteStatus.ModelID != work.Target.ModelID ||
record.RouteStatus.SelectionRevision != "" ||
record.RouteStatus.ReasonCode != "" {
t.Fatalf("route evidence was fabricated or lost: %+v", record.RouteStatus)
}
if record.ChangeSetID != work.ChangeSet.ID ||
record.ChangeSetRevision != work.ChangeSet.Revision ||
record.IntegrationAttempt != work.IntegrationAttempt ||
record.IntegrationOutcome != work.Integration.Outcome ||
record.BlockerCode != work.Blocker.Code {
t.Fatalf("change-set/integration evidence lost: %+v", record)
}
if len(record.Locators) != 2 ||
record.Locators[0].Kind != agenttask.LocatorProcess ||
record.Locators[1].Kind != agenttask.LocatorSession {
t.Fatalf("locators are not exact and sorted: %+v", record.Locators)
}
}
func TestSinkAllowsProjectEventWithoutWorkEvidence(t *testing.T) {
ctx := context.Background()
harness := newSinkTestHarness(t, "proj-project-event", "ws-project-event")
state, revision, err := harness.manager.Load(ctx)
if err != nil {
t.Fatalf("manager Load: %v", err)
}
state.Projects = map[agenttask.ProjectID]agenttask.ProjectRecord{
harness.project.ProjectID: harness.project,
}
if _, err := harness.manager.CompareAndSwap(ctx, revision, state); err != nil {
t.Fatalf("manager CompareAndSwap: %v", err)
}
event := agenttask.Event{
EventID: "project-event-manual-start",
Type: agenttask.EventManualStart,
ProjectID: harness.project.ProjectID,
CommandID: harness.project.Intent.CommandID,
Detail: "manual project start",
Timestamp: time.Date(2026, 7, 30, 11, 30, 0, 0, time.UTC),
}
if err := harness.sink.Emit(ctx, event); err != nil {
t.Fatalf("Emit project event: %v", err)
}
records, err := harness.store.ReplayRecords(ctx)
if err != nil || len(records) != 1 {
t.Fatalf("ReplayRecords = %d, %v", len(records), err)
}
record := records[0]
if record.WorkUnitID != "" ||
record.AttemptID != "" ||
record.State != "" ||
record.StateRevision != "" ||
record.RouteStatus != nil {
t.Fatalf("project event fabricated work evidence: %+v", record)
}
}
func TestSinkRejectsEvidenceIdentityDrift(t *testing.T) {
harness := newSinkTestHarness(t, "proj-drift", "ws-drift")
work := sinkTestWork(
harness.project.ProjectID,
harness.project.WorkspaceID,
"work-drift",
2,
agenttask.WorkStateDispatching,
)
work.ChangeSet = &agenttask.ChangeSetIdentity{ID: "change-1", Revision: "change-rev-1"}
harness.putWork(t, work)
base := agenttask.Event{
EventID: "event-drift",
Type: agenttask.EventDispatchStarted,
ProjectID: harness.project.ProjectID,
WorkspaceID: harness.project.WorkspaceID,
WorkUnitID: work.Unit.ID,
AttemptID: work.AttemptID,
Ordinal: work.DispatchOrdinal,
State: work.State,
ProviderID: work.Target.ProviderID,
ProfileID: work.Target.ProfileID,
Timestamp: time.Date(2026, 7, 30, 12, 0, 0, 0, time.UTC),
}
tests := []struct {
name string
mutate func(*agenttask.Event)
}{
{"workspace", func(event *agenttask.Event) { event.WorkspaceID = "ws-other" }},
{"attempt", func(event *agenttask.Event) { event.AttemptID = "attempt-other" }},
{"dispatch", func(event *agenttask.Event) { event.Ordinal++ }},
{"state", func(event *agenttask.Event) { event.State = agenttask.WorkStateReviewing }},
{"provider", func(event *agenttask.Event) { event.ProviderID = "provider-other" }},
{"change set", func(event *agenttask.Event) {
event.ChangeSetID = "change-other"
event.ChangeSetRevision = "change-rev-other"
}},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
event := base
test.mutate(&event)
err := harness.sink.Emit(context.Background(), event)
if !errors.Is(err, ErrEvidenceIdentityDrift) {
t.Fatalf("Emit error = %v, want ErrEvidenceIdentityDrift", err)
}
})
}
}
func TestSinkPendingDeliveryUsesExactCommittedEvidence(t *testing.T) {
ctx := context.Background()
harness := newSinkTestHarness(t, "proj-pending", "ws-pending")
pendingWork := sinkTestWork(
harness.project.ProjectID,
harness.project.WorkspaceID,
"work-pending",
1,
agenttask.WorkStateReviewing,
)
pendingWork.Review = &agenttask.ReviewResult{
ProjectID: harness.project.ProjectID,
WorkUnitID: pendingWork.Unit.ID,
AttemptID: pendingWork.AttemptID,
ArtifactID: "artifact-pending",
Verdict: agenttask.ReviewVerdictFail,
Message: "committed review",
Rework: true,
}
pendingProject := harness.project
pendingProject.Works = map[agenttask.WorkUnitID]agenttask.WorkRecord{
pendingWork.Unit.ID: pendingWork,
}
event := agenttask.Event{
EventID: "event-pending-review",
Type: agenttask.EventReviewResult,
ProjectID: pendingProject.ProjectID,
WorkspaceID: pendingProject.WorkspaceID,
WorkUnitID: pendingWork.Unit.ID,
CommandID: pendingProject.Intent.CommandID,
AttemptID: pendingWork.AttemptID,
Ordinal: pendingWork.DispatchOrdinal,
Detail: string(agenttask.ReviewVerdictFail),
Timestamp: time.Date(2026, 7, 30, 12, 30, 0, 0, time.UTC),
}
currentWork := sinkTestWork(
harness.project.ProjectID,
harness.project.WorkspaceID,
pendingWork.Unit.ID,
2,
agenttask.WorkStateReady,
)
currentProject := harness.project
currentProject.Works = map[agenttask.WorkUnitID]agenttask.WorkRecord{
currentWork.Unit.ID: currentWork,
}
state, revision, err := harness.manager.Load(ctx)
if err != nil {
t.Fatalf("manager Load: %v", err)
}
state.Projects = map[agenttask.ProjectID]agenttask.ProjectRecord{
currentProject.ProjectID: currentProject,
}
state.PendingEvents = map[string]agenttask.EventDelivery{
event.EventID: {
Event: event,
EvidenceRevision: "pending-state-revision-17",
Project: &pendingProject,
Work: &pendingWork,
},
}
if _, err := harness.manager.CompareAndSwap(ctx, revision, state); err != nil {
t.Fatalf("manager CompareAndSwap: %v", err)
}
if err := harness.sink.Emit(ctx, event); err != nil {
t.Fatalf("Emit pending event: %v", err)
}
scoped, err := harness.store.ForWorkUnit(pendingWork.Unit.ID)
if err != nil {
t.Fatalf("ForWorkUnit: %v", err)
}
records, err := scoped.ReplayRecords(ctx)
if err != nil || len(records) != 1 {
t.Fatalf("ReplayRecords = %d, %v", len(records), err)
}
record := records[0]
if record.StateRevision != "pending-state-revision-17" ||
record.LoopOrdinal != 1 ||
record.AttemptID != pendingWork.AttemptID ||
record.State != agenttask.WorkStateReviewing ||
record.Result != string(agenttask.ReviewVerdictFail) {
t.Fatalf("record did not use pending delivery evidence: %+v", record)
}
if len(record.Locators) != len(pendingWork.Locators) ||
record.Locators[0].AttemptID != pendingWork.AttemptID {
t.Fatalf("record locators came from advanced work: %+v", record.Locators)
}
}
func TestSinkReplayShortCircuitsEvidenceAfterClockAndStateAdvance(t *testing.T) {
ctx := context.Background()
root := t.TempDir()
journalState, err := agentstate.NewStore(filepath.Join(root, "projectlog-state.json"))
if err != nil {
t.Fatalf("agentstate.NewStore: %v", err)
}
store, err := NewStore(
journalState,
filepath.Join(root, "archives"),
"proj-replay",
"ws-replay",
)
if err != nil {
t.Fatalf("NewStore: %v", err)
}
work := sinkTestWork("proj-replay", "ws-replay", "work-replay", 1, agenttask.WorkStateDispatching)
project := agenttask.ProjectRecord{
ProjectID: "proj-replay",
WorkspaceID: "ws-replay",
Status: agenttask.ProjectStatusRunning,
Intent: &agenttask.StartIntent{
CommandID: "cmd-replay",
ProjectID: "proj-replay",
WorkspaceID: "ws-replay",
MilestoneID: "milestone-replay",
WorkflowRevision: "workflow-replay",
ConfigRevision: "config-replay",
GrantRevision: "grant-replay",
},
Works: map[agenttask.WorkUnitID]agenttask.WorkRecord{
work.Unit.ID: work,
},
}
resolver := &replayFailingEvidenceResolver{
evidence: EventEvidence{
StateRevision: "state-revision-1",
Project: project,
Work: &work,
},
}
sink, err := NewSink(store, resolver)
if err != nil {
t.Fatalf("NewSink: %v", err)
}
event := agenttask.Event{
EventID: "stable-replay-event",
Type: agenttask.EventDispatchStarted,
ProjectID: project.ProjectID,
WorkspaceID: project.WorkspaceID,
WorkUnitID: work.Unit.ID,
CommandID: project.Intent.CommandID,
AttemptID: work.AttemptID,
Ordinal: work.DispatchOrdinal,
State: work.State,
ProviderID: work.Target.ProviderID,
ProfileID: work.Target.ProfileID,
Detail: "stable logical event",
Timestamp: time.Date(2026, 7, 30, 13, 0, 0, 0, time.UTC),
}
if err := sink.Emit(ctx, event); err != nil {
t.Fatalf("first Emit: %v", err)
}
event.Timestamp = event.Timestamp.Add(6 * time.Hour)
resolver.evidence.StateRevision = "state-revision-999"
if err := sink.Emit(ctx, event); err != nil {
t.Fatalf("stable replay Emit: %v", err)
}
if resolver.calls != 1 {
t.Fatalf("evidence resolver calls = %d, want 1", resolver.calls)
}
scoped, _ := store.ForWorkUnit(work.Unit.ID)
records, err := scoped.ReplayRecords(ctx)
if err != nil || len(records) != 1 {
t.Fatalf("ReplayRecords = %d, %v", len(records), err)
}
}
func TestSinkRejectsLogicalEventIDReuseAcrossScopesBeforeEvidenceResolution(t *testing.T) {
ctx := context.Background()
root := t.TempDir()
journalState, err := agentstate.NewStore(filepath.Join(root, "projectlog-state.json"))
if err != nil {
t.Fatalf("agentstate.NewStore: %v", err)
}
store, err := NewStore(
journalState,
filepath.Join(root, "archives"),
"proj-cross-scope",
"ws-cross-scope",
)
if err != nil {
t.Fatalf("NewStore: %v", err)
}
work := sinkTestWork(
"proj-cross-scope",
"ws-cross-scope",
"work-A",
1,
agenttask.WorkStateDispatching,
)
project := agenttask.ProjectRecord{
ProjectID: "proj-cross-scope",
WorkspaceID: "ws-cross-scope",
Status: agenttask.ProjectStatusRunning,
Intent: &agenttask.StartIntent{
CommandID: "cmd-cross-scope",
ProjectID: "proj-cross-scope",
WorkspaceID: "ws-cross-scope",
MilestoneID: "milestone-cross-scope",
WorkflowRevision: "workflow-cross-scope",
ConfigRevision: "config-cross-scope",
GrantRevision: "grant-cross-scope",
},
Works: map[agenttask.WorkUnitID]agenttask.WorkRecord{
work.Unit.ID: work,
},
}
resolver := &replayFailingEvidenceResolver{
evidence: EventEvidence{
StateRevision: "state-revision-cross-scope",
Project: project,
Work: &work,
},
}
sink, err := NewSink(store, resolver)
if err != nil {
t.Fatalf("NewSink: %v", err)
}
event := agenttask.Event{
EventID: "event-cross-scope-conflict",
Type: agenttask.EventDispatchStarted,
ProjectID: project.ProjectID,
WorkspaceID: project.WorkspaceID,
WorkUnitID: work.Unit.ID,
CommandID: project.Intent.CommandID,
AttemptID: work.AttemptID,
Ordinal: work.DispatchOrdinal,
State: work.State,
ProviderID: work.Target.ProviderID,
ProfileID: work.Target.ProfileID,
Detail: "original work A event",
Timestamp: time.Date(2026, 7, 30, 13, 30, 0, 0, time.UTC),
}
if err := sink.Emit(ctx, event); err != nil {
t.Fatalf("first Emit: %v", err)
}
conflicting := event
conflicting.WorkUnitID = "work-B"
conflicting.AttemptID = "attempt-B"
conflicting.Detail = "changed work B event"
if err := sink.Emit(ctx, conflicting); !errors.Is(err, ErrRecordReplayConflict) {
t.Fatalf("cross-scope Emit error = %v", err)
}
if resolver.calls != 1 {
t.Fatalf("evidence resolver calls = %d, want 1", resolver.calls)
}
workA, _ := store.ForWorkUnit("work-A")
workB, _ := store.ForWorkUnit("work-B")
recordsA, err := workA.ReplayRecords(ctx)
if err != nil || len(recordsA) != 1 {
t.Fatalf("work A records = %d, %v", len(recordsA), err)
}
recordsB, err := workB.ReplayRecords(ctx)
if err != nil || len(recordsB) != 0 {
t.Fatalf("work B records = %d, %v", len(recordsB), err)
}
}
func TestSinkUsesOpaqueStableRecordID(t *testing.T) {
ctx := context.Background()
harness := newSinkTestHarness(t, "proj-opaque", "ws-opaque")
work := sinkTestWork(
harness.project.ProjectID,
harness.project.WorkspaceID,
"work-opaque",
1,
agenttask.WorkStateDispatching,
)
harness.putWork(t, work)
event := agenttask.Event{
EventID: "raw-manager-event-with-secret-detail",
Type: agenttask.EventDispatchStarted,
ProjectID: harness.project.ProjectID,
WorkspaceID: harness.project.WorkspaceID,
WorkUnitID: work.Unit.ID,
AttemptID: work.AttemptID,
Ordinal: work.DispatchOrdinal,
Detail: "Authorization: Bearer secret-value",
Timestamp: time.Date(2026, 7, 30, 13, 0, 0, 0, time.UTC),
}
if err := harness.sink.Emit(ctx, event); err != nil {
t.Fatalf("first Emit: %v", err)
}
if err := harness.sink.Emit(ctx, event); err != nil {
t.Fatalf("replayed Emit: %v", err)
}
scoped, _ := harness.store.ForWorkUnit(work.Unit.ID)
records, err := scoped.ReplayRecords(ctx)
if err != nil || len(records) != 1 {
t.Fatalf("ReplayRecords = %d, %v", len(records), err)
}
record := records[0]
if !strings.HasPrefix(record.RecordID, "evt-sha256-") || len(record.RecordID) != len("evt-sha256-")+64 {
t.Fatalf("RecordID is not a bounded SHA-256 identity: %q", record.RecordID)
}
if strings.Contains(record.RecordID, event.EventID) ||
strings.Contains(record.RecordID, "secret") ||
record.Message != "[REDACTED]" {
t.Fatalf("raw event evidence leaked: %+v", record)
}
}
func TestSinkRejectsMissingEventID(t *testing.T) {
harness := newSinkTestHarness(t, "proj-missing", "ws-missing")
work := sinkTestWork(
harness.project.ProjectID,
harness.project.WorkspaceID,
"work-missing",
1,
agenttask.WorkStateDispatching,
)
harness.putWork(t, work)
err := harness.sink.Emit(context.Background(), agenttask.Event{
Type: agenttask.EventDispatchStarted,
ProjectID: harness.project.ProjectID,
WorkspaceID: harness.project.WorkspaceID,
WorkUnitID: work.Unit.ID,
Timestamp: time.Date(2026, 7, 30, 14, 0, 0, 0, time.UTC),
})
if !errors.Is(err, ErrMissingEventID) {
t.Fatalf("Emit error = %v, want ErrMissingEventID", err)
}
}
func TestTimelineProjectionIsStableAndRedacted(t *testing.T) {
ctx := context.Background()
harness := newSinkTestHarness(t, "proj-timeline", "ws-timeline")
work := sinkTestWork(
harness.project.ProjectID,
harness.project.WorkspaceID,
"work-timeline",
1,
agenttask.WorkStateDispatching,
)
harness.putWork(t, work)
now := time.Date(2026, 7, 30, 15, 0, 0, 0, time.UTC)
sensitive := agenttask.Event{
EventID: "timeline-sensitive",
Type: agenttask.EventDispatchStarted,
ProjectID: harness.project.ProjectID,
WorkspaceID: harness.project.WorkspaceID,
WorkUnitID: work.Unit.ID,
AttemptID: work.AttemptID,
Ordinal: work.DispatchOrdinal,
Detail: "Dispatching with token sk-secret12345678",
Timestamp: now,
}
if err := harness.sink.Emit(ctx, sensitive); err != nil {
t.Fatalf("Emit sensitive event: %v", err)
}
work.State = agenttask.WorkStateCompleted
work.Integration = &agenttask.IntegrationResult{
ProjectID: harness.project.ProjectID,
WorkUnitID: work.Unit.ID,
ChangeSet: agenttask.ChangeSetIdentity{
ID: "change-timeline",
Revision: "change-timeline-rev",
},
Ordinal: work.DispatchOrdinal,
Attempt: 1,
Outcome: agenttask.IntegrationOutcomeIntegrated,
}
work.ChangeSet = &work.Integration.ChangeSet
work.IntegrationAttempt = 1
harness.putWork(t, work)
completed := agenttask.Event{
EventID: "timeline-completed",
Type: agenttask.EventCompleted,
ProjectID: harness.project.ProjectID,
WorkspaceID: harness.project.WorkspaceID,
WorkUnitID: work.Unit.ID,
AttemptID: work.AttemptID,
Ordinal: work.DispatchOrdinal,
State: work.State,
Detail: "Task completed successfully",
Timestamp: now.Add(time.Minute),
}
if err := harness.sink.Emit(ctx, completed); err != nil {
t.Fatalf("Emit completed event: %v", err)
}
scoped, _ := harness.store.ForWorkUnit(work.Unit.ID)
records, err := scoped.ReplayRecords(ctx)
if err != nil {
t.Fatalf("ReplayRecords: %v", err)
}
projection := ProjectWorkLog(records)
if len(projection.Entries) != 2 {
t.Fatalf("projection entry count = %d, want 2", len(projection.Entries))
}
if projection.Entries[0].Message != "[REDACTED]" ||
projection.Entries[0].RoleStage != "dispatch" ||
len(projection.Entries[0].Locators) != 2 {
t.Fatalf("first projection entry = %+v", projection.Entries[0])
}
if projection.Entries[1].Message != "Task completed successfully" ||
!projection.Entries[1].Terminal ||
projection.Entries[1].Result != string(agenttask.IntegrationOutcomeIntegrated) {
t.Fatalf("terminal projection entry = %+v", projection.Entries[1])
}
}