@@ -436,5 +436,224 @@ public void GitConfiguration_UnsetAll_All_ThrowsException()
436436 Assert . Throws < InvalidOperationException > ( ( ) =>
437437 config . UnsetAll ( GitConfigurationLevel . All , "core.foobar" , Constants . RegexPatterns . Any ) ) ;
438438 }
439+
440+ [ Fact ]
441+ public void GitConfiguration_CacheTryGet_ReturnsValueFromCache ( )
442+ {
443+ string repoPath = CreateRepository ( out string workDirPath ) ;
444+ ExecGit ( repoPath , workDirPath , "config --local user.name john.doe" ) . AssertSuccess ( ) ;
445+ ExecGit ( repoPath , workDirPath , "config --local user.email john@example.com" ) . AssertSuccess ( ) ;
446+
447+ string gitPath = GetGitPath ( ) ;
448+ var trace = new NullTrace ( ) ;
449+ var trace2 = new NullTrace2 ( ) ;
450+ var processManager = new TestProcessManager ( ) ;
451+
452+ var git = new GitProcess ( trace , trace2 , processManager , gitPath , repoPath ) ;
453+ IGitConfiguration config = git . GetConfiguration ( ) ;
454+
455+ // First access loads cache
456+ bool result1 = config . TryGet ( "user.name" , false , out string value1 ) ;
457+ Assert . True ( result1 ) ;
458+ Assert . Equal ( "john.doe" , value1 ) ;
459+
460+ // Second access should use cache
461+ bool result2 = config . TryGet ( "user.email" , false , out string value2 ) ;
462+ Assert . True ( result2 ) ;
463+ Assert . Equal ( "john@example.com" , value2 ) ;
464+ }
465+
466+ [ Fact ]
467+ public void GitConfiguration_CacheGetAll_ReturnsAllValuesFromCache ( )
468+ {
469+ string repoPath = CreateRepository ( out string workDirPath ) ;
470+ ExecGit ( repoPath , workDirPath , "config --local --add test.multi value1" ) . AssertSuccess ( ) ;
471+ ExecGit ( repoPath , workDirPath , "config --local --add test.multi value2" ) . AssertSuccess ( ) ;
472+ ExecGit ( repoPath , workDirPath , "config --local --add test.multi value3" ) . AssertSuccess ( ) ;
473+
474+ string gitPath = GetGitPath ( ) ;
475+ var trace = new NullTrace ( ) ;
476+ var trace2 = new NullTrace2 ( ) ;
477+ var processManager = new TestProcessManager ( ) ;
478+
479+ var git = new GitProcess ( trace , trace2 , processManager , gitPath , repoPath ) ;
480+ IGitConfiguration config = git . GetConfiguration ( ) ;
481+
482+ var values = new List < string > ( config . GetAll ( "test.multi" ) ) ;
483+
484+ Assert . Equal ( 3 , values . Count ) ;
485+ Assert . Equal ( "value1" , values [ 0 ] ) ;
486+ Assert . Equal ( "value2" , values [ 1 ] ) ;
487+ Assert . Equal ( "value3" , values [ 2 ] ) ;
488+ }
489+
490+ [ Fact ]
491+ public void GitConfiguration_CacheEnumerate_EnumeratesFromCache ( )
492+ {
493+ string repoPath = CreateRepository ( out string workDirPath ) ;
494+ ExecGit ( repoPath , workDirPath , "config --local cache.name test-value" ) . AssertSuccess ( ) ;
495+ ExecGit ( repoPath , workDirPath , "config --local cache.enabled true" ) . AssertSuccess ( ) ;
496+
497+ string gitPath = GetGitPath ( ) ;
498+ var trace = new NullTrace ( ) ;
499+ var trace2 = new NullTrace2 ( ) ;
500+ var processManager = new TestProcessManager ( ) ;
501+
502+ var git = new GitProcess ( trace , trace2 , processManager , gitPath , repoPath ) ;
503+ IGitConfiguration config = git . GetConfiguration ( ) ;
504+
505+ var cacheEntries = new List < ( string key , string value ) > ( ) ;
506+ config . Enumerate ( entry =>
507+ {
508+ if ( entry . Key . StartsWith ( "cache." ) )
509+ {
510+ cacheEntries . Add ( ( entry . Key , entry . Value ) ) ;
511+ }
512+ return true ;
513+ } ) ;
514+
515+ Assert . Equal ( 2 , cacheEntries . Count ) ;
516+ Assert . Contains ( ( "cache.name" , "test-value" ) , cacheEntries ) ;
517+ Assert . Contains ( ( "cache.enabled" , "true" ) , cacheEntries ) ;
518+ }
519+
520+ [ Fact ]
521+ public void GitConfiguration_CacheInvalidation_SetInvalidatesCache ( )
522+ {
523+ string repoPath = CreateRepository ( out string workDirPath ) ;
524+ ExecGit ( repoPath , workDirPath , "config --local test.value initial" ) . AssertSuccess ( ) ;
525+
526+ string gitPath = GetGitPath ( ) ;
527+ var trace = new NullTrace ( ) ;
528+ var trace2 = new NullTrace2 ( ) ;
529+ var processManager = new TestProcessManager ( ) ;
530+
531+ var git = new GitProcess ( trace , trace2 , processManager , gitPath , repoPath ) ;
532+ IGitConfiguration config = git . GetConfiguration ( ) ;
533+
534+ // Load cache with initial value
535+ bool result1 = config . TryGet ( "test.value" , false , out string value1 ) ;
536+ Assert . True ( result1 ) ;
537+ Assert . Equal ( "initial" , value1 ) ;
538+
539+ // Set new value (should invalidate cache)
540+ config . Set ( GitConfigurationLevel . Local , "test.value" , "updated" ) ;
541+
542+ // Next read should get updated value
543+ bool result2 = config . TryGet ( "test.value" , false , out string value2 ) ;
544+ Assert . True ( result2 ) ;
545+ Assert . Equal ( "updated" , value2 ) ;
546+ }
547+
548+ [ Fact ]
549+ public void GitConfiguration_CacheInvalidation_AddInvalidatesCache ( )
550+ {
551+ string repoPath = CreateRepository ( out string workDirPath ) ;
552+ ExecGit ( repoPath , workDirPath , "config --local test.multi first" ) . AssertSuccess ( ) ;
553+
554+ string gitPath = GetGitPath ( ) ;
555+ var trace = new NullTrace ( ) ;
556+ var trace2 = new NullTrace2 ( ) ;
557+ var processManager = new TestProcessManager ( ) ;
558+
559+ var git = new GitProcess ( trace , trace2 , processManager , gitPath , repoPath ) ;
560+ IGitConfiguration config = git . GetConfiguration ( ) ;
561+
562+ // Load cache
563+ var values1 = new List < string > ( config . GetAll ( "test.multi" ) ) ;
564+ Assert . Single ( values1 ) ;
565+ Assert . Equal ( "first" , values1 [ 0 ] ) ;
566+
567+ // Add new value (should invalidate cache)
568+ config . Add ( GitConfigurationLevel . Local , "test.multi" , "second" ) ;
569+
570+ // Next read should include new value
571+ var values2 = new List < string > ( config . GetAll ( "test.multi" ) ) ;
572+ Assert . Equal ( 2 , values2 . Count ) ;
573+ Assert . Equal ( "first" , values2 [ 0 ] ) ;
574+ Assert . Equal ( "second" , values2 [ 1 ] ) ;
575+ }
576+
577+ [ Fact ]
578+ public void GitConfiguration_CacheInvalidation_UnsetInvalidatesCache ( )
579+ {
580+ string repoPath = CreateRepository ( out string workDirPath ) ;
581+ ExecGit ( repoPath , workDirPath , "config --local test.value exists" ) . AssertSuccess ( ) ;
582+
583+ string gitPath = GetGitPath ( ) ;
584+ var trace = new NullTrace ( ) ;
585+ var trace2 = new NullTrace2 ( ) ;
586+ var processManager = new TestProcessManager ( ) ;
587+
588+ var git = new GitProcess ( trace , trace2 , processManager , gitPath , repoPath ) ;
589+ IGitConfiguration config = git . GetConfiguration ( ) ;
590+
591+ // Load cache
592+ bool result1 = config . TryGet ( "test.value" , false , out string value1 ) ;
593+ Assert . True ( result1 ) ;
594+ Assert . Equal ( "exists" , value1 ) ;
595+
596+ // Unset value (should invalidate cache)
597+ config . Unset ( GitConfigurationLevel . Local , "test.value" ) ;
598+
599+ // Next read should not find value
600+ bool result2 = config . TryGet ( "test.value" , false , out string value2 ) ;
601+ Assert . False ( result2 ) ;
602+ Assert . Null ( value2 ) ;
603+ }
604+
605+ [ Fact ]
606+ public void GitConfiguration_CacheLevelFilter_ReturnsOnlyLocalValues ( )
607+ {
608+ string repoPath = CreateRepository ( out string workDirPath ) ;
609+
610+ try
611+ {
612+ ExecGit ( repoPath , workDirPath , "config --global test.level global-value" ) . AssertSuccess ( ) ;
613+ ExecGit ( repoPath , workDirPath , "config --local test.level local-value" ) . AssertSuccess ( ) ;
614+
615+ string gitPath = GetGitPath ( ) ;
616+ var trace = new NullTrace ( ) ;
617+ var trace2 = new NullTrace2 ( ) ;
618+ var processManager = new TestProcessManager ( ) ;
619+
620+ var git = new GitProcess ( trace , trace2 , processManager , gitPath , repoPath ) ;
621+ IGitConfiguration config = git . GetConfiguration ( ) ;
622+
623+ // Get local value only
624+ bool result = config . TryGet ( GitConfigurationLevel . Local , GitConfigurationType . Raw ,
625+ "test.level" , out string value ) ;
626+ Assert . True ( result ) ;
627+ Assert . Equal ( "local-value" , value ) ;
628+ }
629+ finally
630+ {
631+ // Cleanup global config
632+ ExecGit ( repoPath , workDirPath , "config --global --unset test.level" ) ;
633+ }
634+ }
635+
636+ [ Fact ]
637+ public void GitConfiguration_TypedQuery_DoesNotUseCache ( )
638+ {
639+ string repoPath = CreateRepository ( out string workDirPath ) ;
640+ ExecGit ( repoPath , workDirPath , "config --local test.path ~/example" ) . AssertSuccess ( ) ;
641+
642+ string gitPath = GetGitPath ( ) ;
643+ var trace = new NullTrace ( ) ;
644+ var trace2 = new NullTrace2 ( ) ;
645+ var processManager = new TestProcessManager ( ) ;
646+
647+ var git = new GitProcess ( trace , trace2 , processManager , gitPath , repoPath ) ;
648+ IGitConfiguration config = git . GetConfiguration ( ) ;
649+
650+ // Path type should not use cache (needs Git's canonicalization)
651+ bool result = config . TryGet ( GitConfigurationLevel . Local , GitConfigurationType . Path ,
652+ "test.path" , out string value ) ;
653+ Assert . True ( result ) ;
654+ Assert . NotNull ( value ) ;
655+ // Value should be canonicalized path, not raw "~/example"
656+ Assert . NotEqual ( "~/example" , value ) ;
657+ }
439658 }
440659}
0 commit comments