@@ -6,60 +6,61 @@ import (
66 "github.com/stretchr/testify/assert"
77)
88
9- // TestExecutorHasIsGeneric exercises the generic Has API across all
10- // configured event kinds to ensure the eventTable is wired correctly. It
11- // guards against regressions where adding a new event to the Config
12- // struct silently fails to surface in Has/Dispatch.
9+ // trueHook is a minimal hook reused as the body of every per-event
10+ // fixture below — the hook's identity doesn't matter to Has, only its
11+ // presence does.
12+ var trueHook = []Hook {{Type : HookTypeCommand , Command : "true" }}
13+
14+ // matcherWildcard wraps trueHook in the structure tool-scoped events
15+ // (PreToolUse, PostToolUse) require.
16+ var matcherWildcard = []MatcherConfig {{Matcher : "*" , Hooks : trueHook }}
17+
18+ // onlyHooks maps each known event to a Config that lights up exactly
19+ // that event. The cross-product test below iterates this map (once for
20+ // the empty check, once for the per-event check), so adding a new
21+ // event is a one-line update here — the same one-line update
22+ // compileEvents needs.
23+ var onlyHooks = map [EventType ]* Config {
24+ EventPreToolUse : {PreToolUse : matcherWildcard },
25+ EventPostToolUse : {PostToolUse : matcherWildcard },
26+ EventSessionStart : {SessionStart : trueHook },
27+ EventTurnStart : {TurnStart : trueHook },
28+ EventBeforeLLMCall : {BeforeLLMCall : trueHook },
29+ EventAfterLLMCall : {AfterLLMCall : trueHook },
30+ EventSessionEnd : {SessionEnd : trueHook },
31+ EventOnUserInput : {OnUserInput : trueHook },
32+ EventStop : {Stop : trueHook },
33+ EventNotification : {Notification : trueHook },
34+ EventOnError : {OnError : trueHook },
35+ EventOnMaxIterations : {OnMaxIterations : trueHook },
36+ }
37+
38+ // TestExecutorHasIsGeneric exercises the generic Has API across every
39+ // event kind to ensure compileEvents is wired correctly. It guards
40+ // against regressions where adding a new event to the Config struct
41+ // silently fails to surface in Has/Dispatch.
1342func TestExecutorHasIsGeneric (t * testing.T ) {
1443 t .Parallel ()
1544
1645 // Empty config: no event has hooks.
1746 empty := NewExecutor (nil , "/tmp" , nil )
18- for _ , ev := range []EventType {
19- EventPreToolUse , EventPostToolUse , EventSessionStart , EventTurnStart ,
20- EventBeforeLLMCall , EventAfterLLMCall ,
21- EventSessionEnd , EventOnUserInput , EventStop , EventNotification ,
22- EventOnError , EventOnMaxIterations ,
23- } {
47+ for ev := range onlyHooks {
2448 assert .Falsef (t , empty .Has (ev ), "empty executor must not report Has(%s)" , ev )
2549 }
2650
27- // Each event populated in turn — the cross-product test ensures that
28- // configuring event X never makes Has(Y) true for Y != X.
29- cases := []struct {
30- event EventType
31- config * Config
32- }{
33- {EventPreToolUse , & Config {PreToolUse : []MatcherConfig {{Matcher : "*" , Hooks : []Hook {{Type : HookTypeCommand , Command : "true" }}}}}},
34- {EventPostToolUse , & Config {PostToolUse : []MatcherConfig {{Matcher : "*" , Hooks : []Hook {{Type : HookTypeCommand , Command : "true" }}}}}},
35- {EventSessionStart , & Config {SessionStart : []Hook {{Type : HookTypeCommand , Command : "true" }}}},
36- {EventTurnStart , & Config {TurnStart : []Hook {{Type : HookTypeCommand , Command : "true" }}}},
37- {EventBeforeLLMCall , & Config {BeforeLLMCall : []Hook {{Type : HookTypeCommand , Command : "true" }}}},
38- {EventAfterLLMCall , & Config {AfterLLMCall : []Hook {{Type : HookTypeCommand , Command : "true" }}}},
39- {EventSessionEnd , & Config {SessionEnd : []Hook {{Type : HookTypeCommand , Command : "true" }}}},
40- {EventOnUserInput , & Config {OnUserInput : []Hook {{Type : HookTypeCommand , Command : "true" }}}},
41- {EventStop , & Config {Stop : []Hook {{Type : HookTypeCommand , Command : "true" }}}},
42- {EventNotification , & Config {Notification : []Hook {{Type : HookTypeCommand , Command : "true" }}}},
43- {EventOnError , & Config {OnError : []Hook {{Type : HookTypeCommand , Command : "true" }}}},
44- {EventOnMaxIterations , & Config {OnMaxIterations : []Hook {{Type : HookTypeCommand , Command : "true" }}}},
45- }
46-
47- for _ , tc := range cases {
48- exec := NewExecutor (tc .config , "/tmp" , nil )
49- for _ , other := range []EventType {
50- EventPreToolUse , EventPostToolUse , EventSessionStart , EventTurnStart ,
51- EventBeforeLLMCall , EventAfterLLMCall ,
52- EventSessionEnd , EventOnUserInput , EventStop , EventNotification ,
53- EventOnError , EventOnMaxIterations ,
54- } {
55- if other == tc .event {
56- assert .Truef (t , exec .Has (other ), "configuring %s must light up Has(%s)" , tc .event , other )
51+ // Cross-product: configuring event ev must light up Has(ev) and
52+ // only Has(ev).
53+ for ev , cfg := range onlyHooks {
54+ exec := NewExecutor (cfg , "/tmp" , nil )
55+ for other := range onlyHooks {
56+ if other == ev {
57+ assert .Truef (t , exec .Has (other ), "configuring %s must light up Has(%s)" , ev , other )
5758 } else {
58- assert .Falsef (t , exec .Has (other ), "configuring %s must NOT light up Has(%s)" , tc . event , other )
59+ assert .Falsef (t , exec .Has (other ), "configuring %s must NOT light up Has(%s)" , ev , other )
5960 }
6061 }
6162 }
6263
6364 // Unknown events fall through cleanly rather than panicking.
64- assert .False (t , NewExecutor ( nil , "/tmp" , nil ) .Has (EventType ("does-not-exist" )))
65+ assert .False (t , empty .Has (EventType ("does-not-exist" )))
6566}
0 commit comments