@@ -339,4 +339,71 @@ public async Task With_CustomDimensions_Before_Test()
339339 Assert . Equal ( "my-provider" , measurements . Tags [ "feature_flag.provider.name" ] ) ;
340340 Assert . Equal ( "custom_dimension_value" , measurements . Tags [ "custom_dimension_key" ] ) ;
341341 }
342+
343+ [ Fact ]
344+ public async Task ActiveCounter_Before_And_Finally_Have_Same_Tags_Test ( )
345+ {
346+ // Arrange
347+ var metricsHookOptions = MetricsHookOptions . CreateBuilder ( )
348+ . WithCustomDimension ( "custom_dimension_key" , "custom_dimension_value" )
349+ . WithFlagEvaluationMetadata ( "boolean" , m => m . GetBool ( "boolean" ) )
350+ . Build ( ) ;
351+
352+ var metricsHook = new MetricsHook ( metricsHookOptions ) ;
353+
354+ using var collector = new MetricCollector < long > ( metricsHook . _evaluationActiveUpDownCounter ) ;
355+
356+ var flagMetadata = new ImmutableMetadata ( new Dictionary < string , object > { { "boolean" , true } } ) ;
357+
358+ var evaluationContext = EvaluationContext . Empty ;
359+ var ctx = new HookContext < string > ( "my-flag" , "foo" , Constant . FlagValueType . String ,
360+ new ClientMetadata ( "my-client" , "1.0" ) , new Metadata ( "my-provider" ) , evaluationContext ) ;
361+ var evaluationDetails = new FlagEvaluationDetails < string > ( "my-flag" , "foo" , Constant . ErrorType . None , "STATIC" , "default" , flagMetadata : flagMetadata ) ;
362+
363+ // Act
364+ await metricsHook . BeforeAsync ( ctx , new Dictionary < string , object > ( ) ) . ConfigureAwait ( true ) ;
365+ await metricsHook . FinallyAsync ( ctx , evaluationDetails , new Dictionary < string , object > ( ) ) . ConfigureAwait ( true ) ;
366+
367+ var measurements = collector . GetMeasurementSnapshot ( ) ;
368+
369+ // Assert
370+ Assert . NotNull ( measurements ) ;
371+ Assert . Equal ( 2 , measurements . Count ) ;
372+ Assert . Equal ( 0 , measurements . Sum ( m => m . Value ) ) ;
373+
374+ var beforeTagKeys = measurements [ 0 ] . Tags . Keys . OrderBy ( k => k ) . ToList ( ) ;
375+ var finallyTagKeys = measurements [ 1 ] . Tags . Keys . OrderBy ( k => k ) . ToList ( ) ;
376+
377+ Assert . Equal ( beforeTagKeys , finallyTagKeys ) ;
378+ }
379+
380+ [ Fact ]
381+ public async Task ActiveCounter_With_FlagMetadata_Before_Has_Null_Metadata_Test ( )
382+ {
383+ // Arrange
384+ var metricsHookOptions = MetricsHookOptions . CreateBuilder ( )
385+ . WithFlagEvaluationMetadata ( "boolean" , m => m . GetBool ( "boolean" ) )
386+ . Build ( ) ;
387+
388+ var metricsHook = new MetricsHook ( metricsHookOptions ) ;
389+
390+ using var collector = new MetricCollector < long > ( metricsHook . _evaluationActiveUpDownCounter ) ;
391+
392+ var evaluationContext = EvaluationContext . Empty ;
393+ var ctx = new HookContext < string > ( "my-flag" , "foo" , Constant . FlagValueType . String ,
394+ new ClientMetadata ( "my-client" , "1.0" ) , new Metadata ( "my-provider" ) , evaluationContext ) ;
395+
396+ // Act - call BeforeAsync (no flag metadata available yet)
397+ await metricsHook . BeforeAsync ( ctx , new Dictionary < string , object > ( ) ) . ConfigureAwait ( true ) ;
398+
399+ var measurements = collector . LastMeasurement ;
400+
401+ // Assert - should handle null metadata gracefully
402+ Assert . NotNull ( measurements ) ;
403+ Assert . Equal ( 1 , measurements . Value ) ;
404+ Assert . Equal ( "my-flag" , measurements . Tags [ "feature_flag.key" ] ) ;
405+ Assert . Equal ( "my-provider" , measurements . Tags [ "feature_flag.provider.name" ] ) ;
406+ Assert . Contains ( "boolean" , measurements . Tags . Keys ) ;
407+ Assert . Null ( measurements . Tags [ "boolean" ] ) ;
408+ }
342409}
0 commit comments