package event_test import ( "context" "encoding/json" "errors" "os" "path/filepath" "testing" "yovision/bell/contracts" "yovision/bell/internal/event" ) const fixedEventID = "evt_01J8XQ2K7M3P5R9T0V4W6Y8Z2B" type fixedIDs struct{ id string } func (f fixedIDs) NewEventID() (string, error) { return f.id, nil } type privacy struct { allowed bool err error calls int } func (p *privacy) VideoAllowed(context.Context, int64, int64, int64) (bool, error) { p.calls++ return p.allowed, p.err } func contractPath(name string) string { return filepath.Join("..", "..", "..", "docs", "raw", "contracts", name) } func candidate(t *testing.T, name string) []byte { t.Helper() raw, err := os.ReadFile(contractPath(name)) if err != nil { t.Fatal(err) } var object map[string]any if err := json.Unmarshal(raw, &object); err != nil { t.Fatal(err) } delete(object, "id") encoded, err := json.Marshal(object) if err != nil { t.Fatal(err) } return encoded } func mutate(t *testing.T, raw []byte, fn func(map[string]any)) []byte { t.Helper() var object map[string]any if err := json.Unmarshal(raw, &object); err != nil { t.Fatal(err) } fn(object) encoded, err := json.Marshal(object) if err != nil { t.Fatal(err) } return encoded } func factory(t *testing.T, policy *privacy) *event.Factory { t.Helper() guard, err := event.NewEvidenceGuard("private-customer") if err != nil { t.Fatal(err) } value, err := event.NewFactory(contracts.EventV01Schema, fixedIDs{id: fixedEventID}, policy, guard) if err != nil { t.Fatal(err) } return value } func assertCode(t *testing.T, err error, code event.ErrorCode) { t.Helper() var validation *event.ValidationError if !errors.As(err, &validation) || validation.Code != code { t.Fatalf("expected %s, got %v", code, err) } } func TestFrozenContractCopyIsExact(t *testing.T) { raw, err := os.ReadFile(contractPath("event-v0.1.schema.json")) if err != nil { t.Fatal(err) } if string(raw) != string(contracts.EventV01Schema) { t.Fatal("Bell contract copy drifted from the frozen source") } } func TestFactoryAcceptsAllFrozenExamples(t *testing.T) { for _, name := range []string{ "event-v0.1.example-current.json", "event-v0.1.example-target.json", "event-v0.1.example-radar.json", } { t.Run(name, func(t *testing.T) { policy := &privacy{allowed: true} created, err := factory(t, policy).Create(context.Background(), candidate(t, name)) if err != nil { t.Fatal(err) } if created.ID() != fixedEventID || len(created.JSON()) == 0 { t.Fatal("Bell did not assemble the final event") } if name == "event-v0.1.example-radar.json" && policy.calls != 0 { t.Fatal("non-video event unexpectedly consulted video policy") } }) } } func TestFactoryRejectsUpstreamIDAndUnknownField(t *testing.T) { policy := &privacy{allowed: true} base := candidate(t, "event-v0.1.example-current.json") withID := mutate(t, base, func(object map[string]any) { object["id"] = fixedEventID }) _, err := factory(t, policy).Create(context.Background(), withID) assertCode(t, err, event.CodeUpstreamID) unknown := mutate(t, base, func(object map[string]any) { object["surprise"] = true }) _, err = factory(t, policy).Create(context.Background(), unknown) assertCode(t, err, event.CodeSchema) } func TestFactoryEnforcesCrossFieldAssertions(t *testing.T) { base := candidate(t, "event-v0.1.example-current.json") tests := []struct { name string code event.ErrorCode edit func(map[string]any) }{ {"time-order", event.CodeTimeOrder, func(v map[string]any) { v["occurred_at"] = "2026-08-03T10:31:23.000Z" }}, {"latency", event.CodeLatency, func(v map[string]any) { v["latency_seconds"] = 9.0 }}, {"confidence", event.CodeConfidence, func(v map[string]any) { v["confidence"] = 0.9 }}, {"primary", event.CodePrimarySensor, func(v map[string]any) { v["sensors"] = []any{ map[string]any{"device_id": float64(5012), "modality": "video", "role": "primary"}, map[string]any{"device_id": float64(5013), "modality": "radar", "role": "primary"}, } }}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { _, err := factory(t, &privacy{allowed: true}).Create(context.Background(), mutate(t, base, test.edit)) assertCode(t, err, test.code) }) } } func TestFactoryFailsClosedForPrivacyAndEvidence(t *testing.T) { base := candidate(t, "event-v0.1.example-current.json") _, err := factory(t, &privacy{err: errors.New("mapping unavailable")}).Create(context.Background(), base) assertCode(t, err, event.CodePrivacyUnavailable) _, err = factory(t, &privacy{allowed: false}).Create(context.Background(), base) assertCode(t, err, event.CodePrivacyDenied) unsafe := mutate(t, base, func(v map[string]any) { evidence := v["evidence"].(map[string]any) evidence["snapshot_uris"] = []any{"rtsp://user:password@10.0.0.1:554/private-customer.png"} }) _, err = factory(t, &privacy{allowed: true}).Create(context.Background(), unsafe) assertCode(t, err, event.CodeEvidence) } func TestFactoryRequiresFailClosedPoliciesAndPayloadLimit(t *testing.T) { guard, err := event.NewEvidenceGuard() if err != nil { t.Fatal(err) } if _, err := event.NewFactory(contracts.EventV01Schema, fixedIDs{id: fixedEventID}, nil, guard); err == nil { t.Fatal("nil privacy policy unexpectedly accepted") } _, err = factory(t, &privacy{allowed: true}).Create(context.Background(), make([]byte, event.MaxPayloadBytes+1)) assertCode(t, err, event.CodePayloadTooLarge) }