@@ -2085,8 +2085,14 @@ pub async fn run_worktree_switch_hooks(
20852085
20862086#[ cfg( test) ]
20872087mod tests {
2088- use super :: { ensure_dependency_triggers_compatible, normalize_hook_script} ;
2088+ use super :: {
2089+ build_switch_hook_session_key, ensure_dependency_triggers_compatible,
2090+ mark_switch_hook_ran_this_session, normalize_hook_script, normalize_switch_auto_run_flags,
2091+ parse_switch_hook_source, should_skip_switch_hook_once_per_session,
2092+ validate_switch_once_per_session, HookExecutionContext , RuntimeHook , SWITCH_HOOK_SESSION_RUNS ,
2093+ } ;
20892094 use std:: collections:: HashMap ;
2095+ use std:: path:: PathBuf ;
20902096
20912097 #[ test]
20922098 fn normalize_hook_script_allows_multiline_scripts ( ) {
@@ -2136,4 +2142,97 @@ mod tests {
21362142 Err ( err) => panic ! ( "manual dependency should be allowed: {err}" ) ,
21372143 }
21382144 }
2145+
2146+ #[ test]
2147+ fn parse_switch_hook_source_defaults_to_manual_when_missing_or_blank ( ) {
2148+ assert ! ( matches!( parse_switch_hook_source( None ) , Ok ( super :: SwitchHookSource :: Manual ) ) ) ;
2149+ assert ! ( matches!(
2150+ parse_switch_hook_source( Some ( " " ) ) ,
2151+ Ok ( super :: SwitchHookSource :: Manual )
2152+ ) ) ;
2153+ }
2154+
2155+ #[ test]
2156+ fn parse_switch_hook_source_rejects_unknown_value ( ) {
2157+ match parse_switch_hook_source ( Some ( "invalid" ) ) {
2158+ Ok ( _) => panic ! ( "invalid source should be rejected" ) ,
2159+ Err ( err) => assert ! ( err. contains( "Unsupported switch hook source" ) ) ,
2160+ }
2161+ }
2162+
2163+ #[ test]
2164+ fn validate_switch_once_per_session_allows_only_switch_triggers ( ) {
2165+ match validate_switch_once_per_session ( "before_worktree_switch" , true ) {
2166+ Ok ( value) => assert ! ( value) ,
2167+ Err ( err) => panic ! ( "switch trigger should allow once-per-session: {err}" ) ,
2168+ }
2169+
2170+ match validate_switch_once_per_session ( "before_worktree_create" , true ) {
2171+ Ok ( _) => panic ! ( "non-switch trigger should reject once-per-session" ) ,
2172+ Err ( err) => assert ! ( err. contains( "switchOncePerSession can only be enabled" ) ) ,
2173+ }
2174+ }
2175+
2176+ #[ test]
2177+ fn normalize_switch_auto_run_flags_defaults_for_non_switch_triggers ( ) {
2178+ assert_eq ! (
2179+ normalize_switch_auto_run_flags( "before_worktree_create" , false , true ) ,
2180+ ( true , false )
2181+ ) ;
2182+ assert_eq ! (
2183+ normalize_switch_auto_run_flags( "before_worktree_switch" , false , true ) ,
2184+ ( false , true )
2185+ ) ;
2186+ }
2187+
2188+ #[ test]
2189+ fn switch_once_per_session_marks_and_skips_after_first_run ( ) {
2190+ if let Ok ( mut runs) = SWITCH_HOOK_SESSION_RUNS . lock ( ) {
2191+ runs. clear ( ) ;
2192+ } else {
2193+ panic ! ( "failed to clear switch hook session runs" ) ;
2194+ }
2195+
2196+ let context = HookExecutionContext {
2197+ workspace_path : PathBuf :: from ( "/tmp/workspace" ) ,
2198+ trigger_worktree_path : Some ( PathBuf :: from ( "/tmp/workspace/worktrees/feature-a" ) ) ,
2199+ initiating_worktree_path : None ,
2200+ source_ref : None ,
2201+ } ;
2202+ let hook = RuntimeHook {
2203+ id : "hook-1" . to_string ( ) ,
2204+ name : "hook" . to_string ( ) ,
2205+ scope : "workspace" . to_string ( ) ,
2206+ trigger : "before_worktree_switch" . to_string ( ) ,
2207+ execution_target : "trigger_worktree" . to_string ( ) ,
2208+ execution_mode : "inline" . to_string ( ) ,
2209+ shell : "bash" . to_string ( ) ,
2210+ script : "echo hi" . to_string ( ) ,
2211+ critical : false ,
2212+ switch_once_per_session : true ,
2213+ keep_open_on_completion : false ,
2214+ timeout_seconds : 60 ,
2215+ switch_run_on_create : true ,
2216+ switch_run_on_delete : false ,
2217+ } ;
2218+
2219+ assert ! ( !should_skip_switch_hook_once_per_session(
2220+ "before_worktree_switch" ,
2221+ & hook,
2222+ & context
2223+ ) ) ;
2224+ mark_switch_hook_ran_this_session ( "before_worktree_switch" , & hook. id , & context) ;
2225+ assert ! ( should_skip_switch_hook_once_per_session(
2226+ "before_worktree_switch" ,
2227+ & hook,
2228+ & context
2229+ ) ) ;
2230+
2231+ if let Some ( key) = build_switch_hook_session_key ( "before_worktree_switch" , & hook. id , & context)
2232+ {
2233+ if let Ok ( mut runs) = SWITCH_HOOK_SESSION_RUNS . lock ( ) {
2234+ runs. remove ( & key) ;
2235+ }
2236+ }
2237+ }
21392238}
0 commit comments