@@ -280,3 +280,182 @@ fn test_merge_configs_shell_env() {
280280 Some ( & "active" . to_string( ) )
281281 ) ;
282282}
283+
284+ #[ test]
285+ fn test_inherit_global_true_merges_configs ( ) {
286+ let temp_dir = TempDir :: new ( ) . unwrap ( ) ;
287+
288+ // Write a global config
289+ let global_path = temp_dir. path ( ) . join ( "global.toml" ) ;
290+ std:: fs:: write (
291+ & global_path,
292+ r#"
293+ [filesystem]
294+ allow_read = ["~/.gitconfig"]
295+ "# ,
296+ )
297+ . unwrap ( ) ;
298+
299+ // Write a project config with inherit_global = true (default)
300+ let project_path = temp_dir. path ( ) . join ( ".sandbox.toml" ) ;
301+ std:: fs:: write (
302+ & project_path,
303+ r#"
304+ [sandbox]
305+ inherit_global = true
306+
307+ [filesystem]
308+ allow_read = ["~/.claude"]
309+ "# ,
310+ )
311+ . unwrap ( ) ;
312+
313+ let global = load_global_config ( Some ( & global_path) ) . unwrap ( ) ;
314+ let project = load_project_config ( temp_dir. path ( ) ) . unwrap ( ) . unwrap ( ) ;
315+
316+ // Simulate load_effective_config: inherit_global=true → merge
317+ assert ! ( project. sandbox. inherit_global) ;
318+ let effective = merge_configs ( & global, & project) ;
319+
320+ // Both global and project paths should be present
321+ assert ! ( effective. filesystem. allow_read. contains( & "~/.gitconfig" . to_string( ) ) ) ;
322+ assert ! ( effective. filesystem. allow_read. contains( & "~/.claude" . to_string( ) ) ) ;
323+ }
324+
325+ #[ test]
326+ fn test_inherit_global_false_skips_global ( ) {
327+ let temp_dir = TempDir :: new ( ) . unwrap ( ) ;
328+
329+ // Write a global config with extra paths
330+ let global_path = temp_dir. path ( ) . join ( "global.toml" ) ;
331+ std:: fs:: write (
332+ & global_path,
333+ r#"
334+ [sandbox]
335+ default_network = "online"
336+
337+ [filesystem]
338+ allow_read = ["~/.gitconfig", "~/.cargo"]
339+ "# ,
340+ )
341+ . unwrap ( ) ;
342+
343+ // Write a project config that opts out of global inheritance
344+ let project_path = temp_dir. path ( ) . join ( ".sandbox.toml" ) ;
345+ std:: fs:: write (
346+ & project_path,
347+ r#"
348+ [sandbox]
349+ inherit_global = false
350+
351+ [filesystem]
352+ allow_read = ["~/.claude"]
353+ "# ,
354+ )
355+ . unwrap ( ) ;
356+
357+ let global = load_global_config ( Some ( & global_path) ) . unwrap ( ) ;
358+ let project = load_project_config ( temp_dir. path ( ) ) . unwrap ( ) . unwrap ( ) ;
359+
360+ // Simulate load_effective_config: inherit_global=false → use project only
361+ assert ! ( !project. sandbox. inherit_global) ;
362+ let effective = if project. sandbox . inherit_global {
363+ merge_configs ( & global, & project)
364+ } else {
365+ project
366+ } ;
367+
368+ // Only project paths, global paths must NOT be present
369+ assert ! ( effective. filesystem. allow_read. contains( & "~/.claude" . to_string( ) ) ) ;
370+ assert ! ( !effective. filesystem. allow_read. contains( & "~/.gitconfig" . to_string( ) ) ) ;
371+ assert ! ( !effective. filesystem. allow_read. contains( & "~/.cargo" . to_string( ) ) ) ;
372+ // Network stays at project default (offline), not inherited from global (online)
373+ assert_eq ! ( effective. sandbox. default_network, NetworkMode :: Offline ) ;
374+ }
375+
376+ #[ test]
377+ fn test_custom_config_inherit_global_false_uses_standalone ( ) {
378+ // When -c specifies a config with inherit_global = false,
379+ // it is used as-is without merging with the global config.
380+ let temp_dir = TempDir :: new ( ) . unwrap ( ) ;
381+
382+ let global_path = temp_dir. path ( ) . join ( "global.toml" ) ;
383+ std:: fs:: write (
384+ & global_path,
385+ r#"
386+ [filesystem]
387+ allow_read = ["~/.gitconfig"]
388+ "# ,
389+ )
390+ . unwrap ( ) ;
391+
392+ let custom_path = temp_dir. path ( ) . join ( "custom.toml" ) ;
393+ std:: fs:: write (
394+ & custom_path,
395+ r#"
396+ [sandbox]
397+ inherit_global = false
398+
399+ [filesystem]
400+ allow_read = ["~/.custom"]
401+ "# ,
402+ )
403+ . unwrap ( ) ;
404+
405+ // Simulate load_effective_config with -c flag
406+ let content = std:: fs:: read_to_string ( & custom_path) . unwrap ( ) ;
407+ let project: Config = toml:: from_str ( & content) . unwrap ( ) ;
408+
409+ // inherit_global = false → use project config as-is
410+ assert ! ( !project. sandbox. inherit_global) ;
411+ assert ! ( project. filesystem. allow_read. contains( & "~/.custom" . to_string( ) ) ) ;
412+ assert ! ( !project. filesystem. allow_read. contains( & "~/.gitconfig" . to_string( ) ) ) ;
413+ }
414+
415+ #[ test]
416+ fn test_custom_config_inherit_global_true_merges_with_global ( ) {
417+ // When -c specifies a config with inherit_global = true,
418+ // it is merged with the global config from the default location.
419+ let temp_dir = TempDir :: new ( ) . unwrap ( ) ;
420+
421+ let global_path = temp_dir. path ( ) . join ( "global.toml" ) ;
422+ std:: fs:: write (
423+ & global_path,
424+ r#"
425+ [filesystem]
426+ allow_read = ["~/.gitconfig", "~/.config/git/"]
427+ allow_write = ["~/.cache/"]
428+ "# ,
429+ )
430+ . unwrap ( ) ;
431+
432+ let custom_path = temp_dir. path ( ) . join ( "custom.toml" ) ;
433+ std:: fs:: write (
434+ & custom_path,
435+ r#"
436+ [sandbox]
437+ inherit_global = true
438+ profiles = ["online"]
439+
440+ [filesystem]
441+ allow_read = ["~/.custom"]
442+ allow_write = ["~/.custom-data/"]
443+ "# ,
444+ )
445+ . unwrap ( ) ;
446+
447+ // Simulate load_effective_config with -c flag + inherit_global = true
448+ let content = std:: fs:: read_to_string ( & custom_path) . unwrap ( ) ;
449+ let project: Config = toml:: from_str ( & content) . unwrap ( ) ;
450+ let global = load_global_config ( Some ( & global_path) ) . unwrap ( ) ;
451+
452+ assert ! ( project. sandbox. inherit_global) ;
453+ let effective = merge_configs ( & global, & project) ;
454+
455+ // Both global and project paths should be merged
456+ assert ! ( effective. filesystem. allow_read. contains( & "~/.gitconfig" . to_string( ) ) ) ;
457+ assert ! ( effective. filesystem. allow_read. contains( & "~/.config/git/" . to_string( ) ) ) ;
458+ assert ! ( effective. filesystem. allow_read. contains( & "~/.custom" . to_string( ) ) ) ;
459+ assert ! ( effective. filesystem. allow_write. contains( & "~/.cache/" . to_string( ) ) ) ;
460+ assert ! ( effective. filesystem. allow_write. contains( & "~/.custom-data/" . to_string( ) ) ) ;
461+ }
0 commit comments