package domain import ( "errors" "testing" ) func TestIsNil(t *testing.T) { cases := []struct { name string u UUID want bool }{ {"empty string", UUID(""), true}, {"single char", UUID("x"), false}, {"uuid string", UUID("550e8400-e29b-41d4-a716-446655440000"), false}, {"nil literal", UUID(""), true}, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { got := c.u.IsNil() if got != c.want { t.Errorf("UUID(%q).IsNil() = %v, want %v", c.u, got, c.want) } }) } } func TestCanTransition(t *testing.T) { type tc struct { name string from string to string want bool } var cases []tc for from, targets := range ValidSignalTransitions { for _, to := range targets { cases = append(cases, tc{from + "->" + to, from, to, true}) } } disallowed := []tc{ {"raised->raised", SignalRaised, SignalRaised, false}, {"resolved->raised", SignalResolved, SignalRaised, false}, {"failed->raised", SignalFailed, SignalRaised, false}, {"acknowledged->raised", SignalAcknowledged, SignalRaised, false}, {"muted->resolved", SignalMuted, SignalResolved, false}, {"acting->acknowledged", SignalActing, SignalAcknowledged, false}, } cases = append(cases, disallowed...) cases = append(cases, tc{"unknown source", "nonexistent", SignalRaised, false}, tc{"unknown target", SignalRaised, "nonexistent", false}, ) for _, c := range cases { t.Run(c.name, func(t *testing.T) { s := &Signal{State: c.from} got := s.CanTransition(c.to) if got != c.want { t.Errorf("CanTransition(%q -> %q) = %v, want %v", c.from, c.to, got, c.want) } }) } } func TestSentinelErrors(t *testing.T) { cases := []struct { name string err error msg string }{ {"ErrNotFound", ErrNotFound, "entity not found"}, {"ErrInvalidTransition", ErrInvalidTransition, "invalid lifecycle transition"}, {"ErrApprovalRequired", ErrApprovalRequired, "operator approval required"}, {"ErrAutonomyBlocked", ErrAutonomyBlocked, "autonomy policy blocks this action"}, {"ErrConflict", ErrConflict, "concurrent modification conflict"}, {"ErrCircuitOpen", ErrCircuitOpen, "circuit breaker open for target"}, {"ErrAbstractType", ErrAbstractType, "cannot instantiate abstract entity type"}, {"ErrInvalidEdge", ErrInvalidEdge, "relationship endpoint type mismatch"}, {"ErrCardinality", ErrCardinality, "relationship cardinality violation"}, {"ErrSeedHashMismatch", ErrSeedHashMismatch, "seed content hash mismatch"}, {"ErrAlreadyExists", ErrAlreadyExists, "entity already exists"}, {"ErrQuarantined", ErrQuarantined, "pattern is quarantined"}, {"ErrSkillDeprecated", ErrSkillDeprecated, "skill is deprecated"}, {"ErrInvalidInput", ErrInvalidInput, "invalid input"}, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { if c.err == nil { t.Fatal("sentinel error is nil") } if !errors.Is(c.err, c.err) { t.Errorf("errors.Is failed for %s", c.name) } if c.err.Error() != c.msg { t.Errorf("Error() = %q, want %q", c.err.Error(), c.msg) } }) } } func TestSignalTransitionsComplete(t *testing.T) { // Non-terminal states must be keys in ValidSignalTransitions. // SignalResolved is a terminal state (no outgoing transitions) and is // intentionally absent from the map. nonTerminal := []string{ SignalRaised, SignalAcknowledged, SignalActing, SignalMuted, SignalFailed, } for _, state := range nonTerminal { targets, ok := ValidSignalTransitions[state] if !ok { t.Errorf("non-terminal state %q missing from ValidSignalTransitions", state) continue } if len(targets) == 0 { t.Errorf("state %q maps to empty transition list", state) } } // Resolved is terminal: it should not appear as a source key. if _, ok := ValidSignalTransitions[SignalResolved]; ok { t.Errorf("terminal state %q should not have outgoing transitions", SignalResolved) } // No state anywhere in the map may map to nil/empty. for state, targets := range ValidSignalTransitions { if len(targets) == 0 { t.Errorf("state %q maps to empty/nil transition list", state) } } }