@@ -568,3 +568,94 @@ hooks:
568568 t .Fatalf ("cache size = %d, want 1" , cacheSize )
569569 }
570570}
571+
572+ func TestDynamicRepoHookExecutorEarlyReturnBranches (t * testing.T ) {
573+ exec := & dynamicRepoHookExecutor {}
574+ out := exec .Run (context .Background (), runtimehooks .HookPointBeforeToolCall , runtimehooks.HookContext {})
575+ if len (out .Results ) != 0 || out .Blocked {
576+ t .Fatalf ("expected empty output for nil-config executor, got %+v" , out )
577+ }
578+
579+ exec = & dynamicRepoHookExecutor {fallbackWorkdir : " " }
580+ out = exec .Run (context .Background (), runtimehooks .HookPointBeforeToolCall , runtimehooks.HookContext {})
581+ if len (out .Results ) != 0 || out .Blocked {
582+ t .Fatalf ("expected empty output for blank workspace, got %+v" , out )
583+ }
584+
585+ exec = & dynamicRepoHookExecutor {fallbackWorkdir : "relative/path" }
586+ out = exec .Run (context .Background (), runtimehooks .HookPointBeforeToolCall , runtimehooks.HookContext {})
587+ if len (out .Results ) != 0 || out .Blocked {
588+ t .Fatalf ("expected empty output for invalid workspace path, got %+v" , out )
589+ }
590+ }
591+
592+ func TestLoadRepoHookItemsAndDefaultsBranches (t * testing.T ) {
593+ workspace := t .TempDir ()
594+ hooksPath := filepath .Join (workspace , ".neocode" , "hooks.yaml" )
595+ if err := os .MkdirAll (filepath .Dir (hooksPath ), 0o755 ); err != nil {
596+ t .Fatalf ("mkdir hooks dir: %v" , err )
597+ }
598+
599+ if err := os .WriteFile (hooksPath , []byte (" \n \t " ), 0o644 ); err != nil {
600+ t .Fatalf ("write empty hooks file: %v" , err )
601+ }
602+ if _ , err := loadRepoHookItems (hooksPath , config .StaticDefaults ().Runtime .Hooks ); err == nil {
603+ t .Fatal ("expected empty hooks file error" )
604+ }
605+
606+ content := `
607+ hooks:
608+ items:
609+ - id: disabled
610+ enabled: false
611+ point: before_tool_call
612+ handler: add_context_note
613+ params:
614+ note: skip
615+ - id: enabled-defaults
616+ point: before_tool_call
617+ handler: add_context_note
618+ params:
619+ note: ok
620+ `
621+ if err := os .WriteFile (hooksPath , []byte (content ), 0o644 ); err != nil {
622+ t .Fatalf ("write hooks file: %v" , err )
623+ }
624+ items , err := loadRepoHookItems (hooksPath , config .StaticDefaults ().Runtime .Hooks )
625+ if err != nil {
626+ t .Fatalf ("loadRepoHookItems() error = %v" , err )
627+ }
628+ if len (items ) != 1 {
629+ t .Fatalf ("items len = %d, want 1" , len (items ))
630+ }
631+ item := items [0 ]
632+ if item .Scope != "repo" || item .Kind != "builtin" || item .Mode != "sync" {
633+ t .Fatalf ("unexpected defaults: scope=%q kind=%q mode=%q" , item .Scope , item .Kind , item .Mode )
634+ }
635+ }
636+
637+ func TestResolveTrustedWorkspacesPathFallbackBranches (t * testing.T ) {
638+ t .Setenv ("HOME" , "relative-home" )
639+ path := resolveTrustedWorkspacesPath ()
640+ if ! strings .Contains (path , filepath .Join (".neocode" , repoHooksTrustStoreFileName )) {
641+ t .Fatalf ("unexpected trust store path: %q" , path )
642+ }
643+
644+ t .Setenv ("HOME" , "" )
645+ path = resolveTrustedWorkspacesPath ()
646+ if ! strings .Contains (path , filepath .Join (".neocode" , repoHooksTrustStoreFileName )) {
647+ t .Fatalf ("unexpected trust store path with empty HOME: %q" , path )
648+ }
649+ }
650+
651+ func TestRepoHookEventEmittersAndHelpers (t * testing.T ) {
652+ emitRepoHooksLifecycleEvent (nil , EventRepoHooksDiscovered , RepoHooksLifecyclePayload {})
653+ emitRepoHooksTrustStoreInvalidEvent (nil , RepoHooksTrustStoreInvalidPayload {})
654+
655+ if got := coalesceHookMessage (" " , "fallback" , "other" ); got != "fallback" {
656+ t .Fatalf ("coalesceHookMessage() = %q, want fallback" , got )
657+ }
658+ if got := coalesceHookMessage (" " , "\t " ); got != "" {
659+ t .Fatalf ("coalesceHookMessage(blank) = %q, want empty" , got )
660+ }
661+ }
0 commit comments