package docflow import ( "context" "errors" "testing" "chat-agent-system/internal/docgov" "chat-agent-system/internal/events" ) type recSink struct { envs []events.Envelope cons [][]string err error } func (s *recSink) Append(_ context.Context, env events.Envelope, consumers []string) error { if s.err != nil { return s.err } if err := events.ValidateEnvelope(env); err != nil { return err } if err := events.AssertLiveConsumers(env.EventName, consumers); err != nil { return err } s.envs = append(s.envs, env) s.cons = append(s.cons, consumers) return nil } func TestEmitFailClosedMissingProject(t *testing.T) { sink := &recSink{} cmds := &Commands{Events: sink, Environment: "dev"} err := cmds.emit(context.Background(), &docgov.DocumentRecord{DocID: "d", ProjectID: 0}, events.EventApproved, "e1", "k", "document_approval", "d", 1, Principal{UserID: 1}, []byte(`{"doc_id":"d"}`)) if !errors.Is(err, events.ErrInvalidScope) { t.Fatalf("missing project: %v", err) } if len(sink.envs) != 0 { t.Fatal("must not append") } if err := cmds.emit(context.Background(), &docgov.DocumentRecord{DocID: "d", ProjectID: 9}, events.EventApproved, "e1", "k", "document_approval", "d", 1, Principal{UserID: 1}, []byte(`{"doc_id":"d","project_id":9,"task_key":"intake"}`)); err != nil { t.Fatal(err) } if len(sink.envs) != 1 || sink.envs[0].ProjectID != 9 || sink.envs[0].Environment != "dev" { t.Fatalf("%+v", sink.envs) } for _, c := range sink.cons[0] { if c == events.ConsumerFAV { t.Fatal("fav consumer") } } } func TestEmitRejectsFAVConsumers(t *testing.T) { sink := &recSink{} cmds := &Commands{Events: sink, Environment: "dev"} if err := cmds.Events.Append(context.Background(), events.Envelope{ EventID: "e", EventName: events.EventVersionCommitted, SchemaVersion: events.EnvelopeSchema, Environment: "dev", ProjectID: 9, AggregateID: "d", PayloadDigest: events.Digest([]byte(`{"doc_id":"d"}`)), Producer: "docflow", Payload: []byte(`{"doc_id":"d"}`), }, []string{events.ConsumerFAV}); err == nil { t.Fatal("fav must fail") } }