@@ -3,6 +3,8 @@ package config
33import (
44 "strings"
55 "testing"
6+
7+ "neo-code/internal/runtime/hooks"
68)
79
810func TestRuntimeHooksConfigApplyDefaultsAndValidate (t * testing.T ) {
@@ -46,31 +48,31 @@ func TestRuntimeHooksConfigValidateUnsupportedFields(t *testing.T) {
4648 tests := []RuntimeHookItemConfig {
4749 {
4850 ID : "bad-scope" ,
49- Point : runtimeHookPointBeforeToolCall ,
51+ Point : string ( hooks . HookPointBeforeToolCall ) ,
5052 Scope : "repo" ,
5153 Kind : runtimeHookKindBuiltIn ,
5254 Mode : runtimeHookModeSync ,
5355 Handler : runtimeHookHandlerWarnOnToolCall ,
5456 },
5557 {
5658 ID : "bad-kind" ,
57- Point : runtimeHookPointBeforeToolCall ,
59+ Point : string ( hooks . HookPointBeforeToolCall ) ,
5860 Scope : runtimeHookScopeUser ,
5961 Kind : "command" ,
6062 Mode : runtimeHookModeSync ,
6163 Handler : runtimeHookHandlerWarnOnToolCall ,
6264 },
6365 {
6466 ID : "bad-mode" ,
65- Point : runtimeHookPointBeforeToolCall ,
67+ Point : string ( hooks . HookPointBeforeToolCall ) ,
6668 Scope : runtimeHookScopeUser ,
6769 Kind : runtimeHookKindBuiltIn ,
6870 Mode : "async" ,
6971 Handler : runtimeHookHandlerWarnOnToolCall ,
7072 },
7173 {
7274 ID : "bad-handler" ,
73- Point : runtimeHookPointBeforeToolCall ,
75+ Point : string ( hooks . HookPointBeforeToolCall ) ,
7476 Scope : runtimeHookScopeUser ,
7577 Kind : runtimeHookKindBuiltIn ,
7678 Mode : runtimeHookModeSync ,
@@ -113,7 +115,7 @@ func TestRuntimeHooksConfigValidateRejectsExternalKindsWithP6LiteMessage(t *test
113115 cfg .Items = []RuntimeHookItemConfig {
114116 {
115117 ID : "external-kind" ,
116- Point : runtimeHookPointBeforeToolCall ,
118+ Point : string ( hooks . HookPointBeforeToolCall ) ,
117119 Scope : runtimeHookScopeUser ,
118120 Kind : kind ,
119121 Mode : runtimeHookModeSync ,
@@ -144,7 +146,7 @@ func TestRuntimeHooksConfigValidateAllowsCommand(t *testing.T) {
144146 Items : []RuntimeHookItemConfig {
145147 {
146148 ID : "accept-command" ,
147- Point : runtimeHookPointAcceptGate ,
149+ Point : string ( hooks . HookPointAcceptGate ) ,
148150 Scope : runtimeHookScopeUser ,
149151 Kind : runtimeHookKindCommand ,
150152 Mode : runtimeHookModeSync ,
@@ -170,7 +172,7 @@ func TestRuntimeHooksConfigValidateAllowsHTTPObserve(t *testing.T) {
170172 Items : []RuntimeHookItemConfig {
171173 {
172174 ID : "observe-http" ,
173- Point : runtimeHookPointBeforeToolCall ,
175+ Point : string ( hooks . HookPointBeforeToolCall ) ,
174176 Scope : runtimeHookScopeUser ,
175177 Kind : runtimeHookKindHTTP ,
176178 Params : map [string ]any {
@@ -193,7 +195,7 @@ func TestRuntimeHooksConfigValidateRejectsInvalidHTTPObserveConfig(t *testing.T)
193195
194196 base := RuntimeHookItemConfig {
195197 ID : "observe-http" ,
196- Point : runtimeHookPointBeforeToolCall ,
198+ Point : string ( hooks . HookPointBeforeToolCall ) ,
197199 Scope : runtimeHookScopeUser ,
198200 Kind : runtimeHookKindHTTP ,
199201 Mode : runtimeHookModeObserve ,
@@ -281,7 +283,7 @@ func TestRuntimeHooksConfigValidateRejectsDisallowedUserPoint(t *testing.T) {
281283 Items : []RuntimeHookItemConfig {
282284 {
283285 ID : "deny-pre-compact" ,
284- Point : runtimeHookPointPreCompact ,
286+ Point : string ( hooks . HookPointPreCompact ) ,
285287 Scope : runtimeHookScopeUser ,
286288 Kind : runtimeHookKindBuiltIn ,
287289 Mode : runtimeHookModeSync ,
@@ -308,7 +310,7 @@ func TestRuntimeHooksConfigItemDefaultsAndClone(t *testing.T) {
308310 Items : []RuntimeHookItemConfig {
309311 {
310312 ID : "warn-bash" ,
311- Point : runtimeHookPointBeforeToolCall ,
313+ Point : string ( hooks . HookPointBeforeToolCall ) ,
312314 Handler : runtimeHookHandlerWarnOnToolCall ,
313315 Params : map [string ]any {
314316 "tool_name" : "bash" ,
@@ -368,7 +370,7 @@ func TestRuntimeHooksConfigValidateItemFailurePolicy(t *testing.T) {
368370 Items : []RuntimeHookItemConfig {
369371 {
370372 ID : "require-readme" ,
371- Point : runtimeHookPointBeforeCompletionDecision ,
373+ Point : string ( hooks . HookPointBeforeCompletionDecision ) ,
372374 Scope : runtimeHookScopeUser ,
373375 Kind : runtimeHookKindBuiltIn ,
374376 Mode : runtimeHookModeSync ,
@@ -394,7 +396,7 @@ func TestRuntimeHooksConfigValidateWarnOnToolCallRequiresTarget(t *testing.T) {
394396 Items : []RuntimeHookItemConfig {
395397 {
396398 ID : "warn-missing-target" ,
397- Point : runtimeHookPointBeforeToolCall ,
399+ Point : string ( hooks . HookPointBeforeToolCall ) ,
398400 Scope : runtimeHookScopeUser ,
399401 Kind : runtimeHookKindBuiltIn ,
400402 Mode : runtimeHookModeSync ,
@@ -449,8 +451,8 @@ func TestRuntimeHooksConfigEdgeBranches(t *testing.T) {
449451 DefaultTimeoutSec : 2 ,
450452 DefaultFailurePolicy : runtimeHookFailurePolicyWarnOnly ,
451453 Items : []RuntimeHookItemConfig {
452- {ID : "dup" , Point : runtimeHookPointBeforeToolCall , Scope : runtimeHookScopeUser , Kind : runtimeHookKindBuiltIn , Mode : runtimeHookModeSync , Handler : runtimeHookHandlerWarnOnToolCall , TimeoutSec : 1 , Params : map [string ]any {"tool_name" : "bash" }},
453- {ID : " DUP " , Point : runtimeHookPointBeforeToolCall , Scope : runtimeHookScopeUser , Kind : runtimeHookKindBuiltIn , Mode : runtimeHookModeSync , Handler : runtimeHookHandlerWarnOnToolCall , TimeoutSec : 1 , Params : map [string ]any {"tool_name" : "bash" }},
454+ {ID : "dup" , Point : string ( hooks . HookPointBeforeToolCall ) , Scope : runtimeHookScopeUser , Kind : runtimeHookKindBuiltIn , Mode : runtimeHookModeSync , Handler : runtimeHookHandlerWarnOnToolCall , TimeoutSec : 1 , Params : map [string ]any {"tool_name" : "bash" }},
455+ {ID : " DUP " , Point : string ( hooks . HookPointBeforeToolCall ) , Scope : runtimeHookScopeUser , Kind : runtimeHookKindBuiltIn , Mode : runtimeHookModeSync , Handler : runtimeHookHandlerWarnOnToolCall , TimeoutSec : 1 , Params : map [string ]any {"tool_name" : "bash" }},
454456 },
455457 }
456458 if err := cfg .Validate (); err == nil {
@@ -465,7 +467,7 @@ func TestRuntimeHooksConfigEdgeBranches(t *testing.T) {
465467 }
466468 item := RuntimeHookItemConfig {
467469 ID : "x" ,
468- Point : runtimeHookPointBeforeToolCall ,
470+ Point : string ( hooks . HookPointBeforeToolCall ) ,
469471 Scope : runtimeHookScopeUser ,
470472 Kind : runtimeHookKindBuiltIn ,
471473 Mode : runtimeHookModeSync ,
@@ -554,7 +556,7 @@ func TestRuntimeHTTPObserveValidationHelpers(t *testing.T) {
554556 } {
555557 item := RuntimeHookItemConfig {
556558 ID : "observe-http" ,
557- Point : runtimeHookPointBeforeToolCall ,
559+ Point : string ( hooks . HookPointBeforeToolCall ) ,
558560 Scope : runtimeHookScopeUser ,
559561 Kind : runtimeHookKindHTTP ,
560562 Mode : runtimeHookModeObserve ,
@@ -580,7 +582,7 @@ func TestRuntimeHTTPObserveValidationHelpers(t *testing.T) {
580582 name : "invalid absolute url" ,
581583 item : RuntimeHookItemConfig {
582584 ID : "observe-http" ,
583- Point : runtimeHookPointBeforeToolCall ,
585+ Point : string ( hooks . HookPointBeforeToolCall ) ,
584586 Scope : runtimeHookScopeUser ,
585587 Kind : runtimeHookKindHTTP ,
586588 Mode : runtimeHookModeObserve ,
@@ -593,7 +595,7 @@ func TestRuntimeHTTPObserveValidationHelpers(t *testing.T) {
593595 name : "headers must be map" ,
594596 item : RuntimeHookItemConfig {
595597 ID : "observe-http" ,
596- Point : runtimeHookPointBeforeToolCall ,
598+ Point : string ( hooks . HookPointBeforeToolCall ) ,
597599 Scope : runtimeHookScopeUser ,
598600 Kind : runtimeHookKindHTTP ,
599601 Mode : runtimeHookModeObserve ,
@@ -607,7 +609,7 @@ func TestRuntimeHTTPObserveValidationHelpers(t *testing.T) {
607609 name : "empty header name" ,
608610 item : RuntimeHookItemConfig {
609611 ID : "observe-http" ,
610- Point : runtimeHookPointBeforeToolCall ,
612+ Point : string ( hooks . HookPointBeforeToolCall ) ,
611613 Scope : runtimeHookScopeUser ,
612614 Kind : runtimeHookKindHTTP ,
613615 Mode : runtimeHookModeObserve ,
@@ -621,7 +623,7 @@ func TestRuntimeHTTPObserveValidationHelpers(t *testing.T) {
621623 name : "empty header value" ,
622624 item : RuntimeHookItemConfig {
623625 ID : "observe-http" ,
624- Point : runtimeHookPointBeforeToolCall ,
626+ Point : string ( hooks . HookPointBeforeToolCall ) ,
625627 Scope : runtimeHookScopeUser ,
626628 Kind : runtimeHookKindHTTP ,
627629 Mode : runtimeHookModeObserve ,
@@ -663,17 +665,73 @@ func TestRuntimeHTTPObserveValidationHelpers(t *testing.T) {
663665 if got := readRuntimeHookParamString (map [string ]any {"x" : 123 }, "x" ); got != "123" {
664666 t .Fatalf ("readRuntimeHookParamString(non-string) = %q" , got )
665667 }
666- if ! runtimeHookPointUserAllowed ( runtimeHookPointBeforeToolCall ) {
668+ if ! hooks . IsUserAllowed ( hooks . HookPointBeforeToolCall ) {
667669 t .Fatal ("before_tool_call should allow user hooks" )
668670 }
669- for _ , point := range []string {
670- runtimeHookPointBeforePermissionDecision ,
671- runtimeHookPointPreCompact ,
672- runtimeHookPointSubAgentStart ,
671+ for _ , point := range []hooks. HookPoint {
672+ hooks . HookPointBeforePermissionDecision ,
673+ hooks . HookPointPreCompact ,
674+ hooks . HookPointSubAgentStart ,
673675 } {
674- if runtimeHookPointUserAllowed (point ) {
676+ if hooks . IsUserAllowed (point ) {
675677 t .Fatalf ("%s should be rejected for user hooks" , point )
676678 }
677679 }
678680 })
679681}
682+
683+ // TestHookPointSingleSourceConsistency 验证 config 侧与 runtime hooks 包的点位定义一致。
684+ // 新增 hook point 时只需修改 runtime hooks 包,config 侧自动接受。
685+ func TestHookPointSingleSourceConsistency (t * testing.T ) {
686+ t .Parallel ()
687+
688+ // 所有 runtime hooks 包导出的点位都应被 config 接受。
689+ allPoints := hooks .ListHookPoints ()
690+ if len (allPoints ) == 0 {
691+ t .Fatal ("expected at least one hook point from runtime hooks package" )
692+ }
693+
694+ base := RuntimeHooksConfig {
695+ Enabled : boolPtr (true ),
696+ UserHooksEnabled : boolPtr (true ),
697+ DefaultTimeoutSec : 2 ,
698+ DefaultFailurePolicy : runtimeHookFailurePolicyWarnOnly ,
699+ }
700+
701+ for _ , point := range allPoints {
702+ point := point
703+ t .Run (string (point ), func (t * testing.T ) {
704+ t .Parallel ()
705+ if ! hooks .IsUserAllowed (point ) {
706+ // 跳过不允许 user 的点位,它们在 config 校验中会被拒绝。
707+ return
708+ }
709+ cfg := base .Clone ()
710+ cfg .Items = []RuntimeHookItemConfig {
711+ {
712+ ID : "test-" + string (point ),
713+ Point : string (point ),
714+ Scope : runtimeHookScopeUser ,
715+ Kind : runtimeHookKindBuiltIn ,
716+ Mode : runtimeHookModeSync ,
717+ Handler : runtimeHookHandlerAddContextNote ,
718+ TimeoutSec : 2 ,
719+ FailurePolicy : runtimeHookFailurePolicyWarnOnly ,
720+ Params : map [string ]any {"note" : "consistency check" },
721+ },
722+ }
723+ if err := cfg .Validate (); err != nil {
724+ t .Fatalf ("config rejected point %q: %v" , point , err )
725+ }
726+ })
727+ }
728+
729+ // 验证 accept_gate 在 runtime hooks 包中存在且允许 user。
730+ acceptGateCap , ok := hooks .HookPointCapabilities (hooks .HookPointAcceptGate )
731+ if ! ok {
732+ t .Fatal ("accept_gate not found in runtime hooks capabilities" )
733+ }
734+ if ! acceptGateCap .UserAllowed {
735+ t .Fatal ("accept_gate should allow user hooks" )
736+ }
737+ }
0 commit comments