1- using Moq ;
1+ using FluentAssertions ;
2+ using Moq ;
23using System . Collections . Generic ;
34using System . Linq . Dynamic . Core . CustomTypeProviders ;
45using System . Linq . Dynamic . Core . Parser ;
56using System . Linq . Expressions ;
7+ using System . Reflection ;
8+ using System . Threading ;
9+ using System . Threading . Tasks ;
610using Xunit ;
711
8- namespace System . Linq . Dynamic . Core . Tests . Parser ;
9-
10- public class ExpressionPromoterTests
12+ namespace System . Linq . Dynamic . Core . Tests . Parser
1113{
12- public class SampleDto
14+ public class ExpressionPromoterTests
1315 {
14- public Guid Data { get ; set ; }
15- }
16+ public class SampleDto
17+ {
18+ public Guid data { get ; set ; }
19+ }
1620
17- private readonly Mock < IExpressionPromoter > _expressionPromoterMock ;
18- private readonly Mock < IDynamicLinqCustomTypeProvider > _dynamicLinkCustomTypeProviderMock ;
21+ private readonly Mock < IExpressionPromoter > _expressionPromoterMock ;
22+ private readonly Mock < IDynamicLinqCustomTypeProvider > _dynamicLinqCustomTypeProviderMock ;
1923
20- public ExpressionPromoterTests ( )
21- {
22- _dynamicLinkCustomTypeProviderMock = new Mock < IDynamicLinqCustomTypeProvider > ( ) ;
23- _dynamicLinkCustomTypeProviderMock . Setup ( d => d . GetCustomTypes ( ) ) . Returns ( new HashSet < Type > ( ) ) ;
24- _dynamicLinkCustomTypeProviderMock . Setup ( d => d . ResolveType ( It . IsAny < string > ( ) ) ) . Returns ( typeof ( SampleDto ) ) ;
24+ public ExpressionPromoterTests ( )
25+ {
26+ _dynamicLinqCustomTypeProviderMock = new Mock < IDynamicLinqCustomTypeProvider > ( ) ;
27+ _dynamicLinqCustomTypeProviderMock . Setup ( d => d . GetCustomTypes ( ) ) . Returns ( new HashSet < Type > ( ) ) ;
28+ _dynamicLinqCustomTypeProviderMock . Setup ( d => d . ResolveType ( It . IsAny < string > ( ) ) ) . Returns ( typeof ( SampleDto ) ) ;
29+
30+ _expressionPromoterMock = new Mock < IExpressionPromoter > ( ) ;
31+ _expressionPromoterMock . Setup ( e => e . Promote ( It . IsAny < Expression > ( ) , It . IsAny < Type > ( ) , It . IsAny < bool > ( ) , It . IsAny < bool > ( ) ) ) . Returns ( Expression . Constant ( Guid . NewGuid ( ) ) ) ;
32+ }
33+
34+ [ Fact ]
35+ public void DynamicExpressionParser_ParseLambda_WithCustomExpressionPromoter ( )
36+ {
37+ // Assign
38+ var parsingConfig = new ParsingConfig ( )
39+ {
40+ AllowNewToEvaluateAnyType = true ,
41+ CustomTypeProvider = _dynamicLinqCustomTypeProviderMock . Object ,
42+ ExpressionPromoter = _expressionPromoterMock . Object
43+ } ;
44+
45+ // Act
46+ string query = $ "new { typeof ( SampleDto ) . FullName } (@0 as data)";
47+ LambdaExpression expression = DynamicExpressionParser . ParseLambda ( parsingConfig , null , query , new object [ ] { Guid . NewGuid ( ) . ToString ( ) } ) ;
48+ Delegate del = expression . Compile ( ) ;
49+ SampleDto result = ( SampleDto ) del . DynamicInvoke ( ) ;
50+
51+ // Assert
52+ Assert . NotNull ( result ) ;
53+
54+ // Verify
55+ _dynamicLinqCustomTypeProviderMock . Verify ( d => d . GetCustomTypes ( ) , Times . Once ) ;
56+ _dynamicLinqCustomTypeProviderMock . Verify ( d => d . ResolveType ( $ "{ typeof ( SampleDto ) . FullName } ") , Times . Once ) ;
57+
58+ _expressionPromoterMock . Verify ( e => e . Promote ( It . IsAny < ConstantExpression > ( ) , typeof ( Guid ) , true , true ) , Times . Once ) ;
59+ }
2560
26- _expressionPromoterMock = new Mock < IExpressionPromoter > ( ) ;
27- _expressionPromoterMock . Setup ( e => e . Promote ( It . IsAny < Expression > ( ) , It . IsAny < Type > ( ) , It . IsAny < bool > ( ) , It . IsAny < bool > ( ) ) ) . Returns ( Expression . Constant ( Guid . NewGuid ( ) ) ) ;
61+ [ Fact ]
62+ public async Task Promote_Should_Succeed_Even_When_LiteralsCache_Is_Cleaned ( )
63+ {
64+ // Arrange
65+ var parsingConfig = new ParsingConfig ( )
66+ {
67+ ConstantExpressionCacheConfig = new Core . Util . Cache . CacheConfig
68+ {
69+ CleanupFrequency = TimeSpan . FromMilliseconds ( 500 ) , // Run cleanup more often
70+ TimeToLive = TimeSpan . FromMilliseconds ( 500 ) , // Shorten TTL to force expiration
71+ ReturnExpiredItems = false
72+ }
73+ } ;
74+
75+ // because the field is static only one process is setting the field,
76+ // we need a way to set up because the instance is private we are not able to overwrite the configuration.
77+ ConstantExpressionHelperReflection . Initiate ( parsingConfig ) ;
78+
79+ var constantExpressionHelper = ConstantExpressionHelperFactory . GetInstance ( parsingConfig ) ;
80+ var expressionPromoter = new ExpressionPromoter ( parsingConfig ) ;
81+
82+ double value = 0.40 ;
83+ string text = "0.40" ;
84+ Type targetType = typeof ( decimal ) ;
85+
86+ // Step 1: Add constant to cache
87+ var literalExpression = constantExpressionHelper . CreateLiteral ( value , text ) ;
88+ Assert . NotNull ( literalExpression ) ; // Ensure it was added
89+
90+ // Step 2: Manually trigger cleanup
91+ var cts = new CancellationTokenSource ( 500 ) ;
92+ await Task . Run ( async ( ) =>
93+ {
94+ while ( ! cts . IsCancellationRequested )
95+ {
96+ constantExpressionHelper . TryGetText ( literalExpression , out _ ) ;
97+ await Task . Delay ( 50 ) ; // Give some time for cleanup to be triggered
98+ }
99+ } ) ;
100+
101+ // Ensure some cleanup cycles have passed
102+ await Task . Delay ( 500 ) ; // Allow cache cleanup to happen
103+
104+ // Step 3: Attempt to promote the expression after cleanup
105+ var promotedExpression = expressionPromoter . Promote ( literalExpression , targetType , exact : false , true ) ;
106+
107+ // Assert: Promotion should still work even if the cache was cleaned
108+ promotedExpression . Should ( ) . NotBeNull ( ) ; // Ensure `Promote()` still returns a valid expression
109+ }
28110 }
29111
30- [ Fact ]
31- public void DynamicExpressionParser_ParseLambda_WithCustomExpressionPromoter ( )
112+ public static class ConstantExpressionHelperReflection
32113 {
33- // Assign
34- var parsingConfig = new ParsingConfig ( )
114+ private static readonly Type _constantExpressionHelperFactoryType ;
115+
116+ static ConstantExpressionHelperReflection ( )
35117 {
36- AllowNewToEvaluateAnyType = true ,
37- CustomTypeProvider = _dynamicLinkCustomTypeProviderMock . Object ,
38- ExpressionPromoter = _expressionPromoterMock . Object
39- } ;
118+ var assembly = Assembly . GetAssembly ( typeof ( DynamicClass ) ) ! ;
40119
41- // Act
42- string query = $ "new { typeof ( SampleDto ) . FullName } (@0 as Data)";
43- LambdaExpression expression = DynamicExpressionParser . ParseLambda ( parsingConfig , null , query , Guid . NewGuid ( ) . ToString ( ) ) ;
44- Delegate del = expression . Compile ( ) ;
45- SampleDto result = ( SampleDto ) del . DynamicInvoke ( ) ;
120+ _constantExpressionHelperFactoryType = assembly . GetType ( "System.Linq.Dynamic.Core.Parser.ConstantExpressionHelperFactory" ) ! ;
121+ }
46122
47- // Assert
48- Assert . NotNull ( result ) ;
123+ public static void Initiate ( ParsingConfig parsingConfig )
124+ {
125+ var instance = new ConstantExpressionHelper ( parsingConfig ) ;
49126
50- // Verify
51- _dynamicLinkCustomTypeProviderMock . Verify ( d => d . GetCustomTypes ( ) , Times . Once ) ;
52- _dynamicLinkCustomTypeProviderMock . Verify ( d => d . ResolveType ( $ "{ typeof ( SampleDto ) . FullName } ") , Times . Once ) ;
127+ var field = _constantExpressionHelperFactoryType . GetField ( "_instance" , BindingFlags . NonPublic | BindingFlags . Static ) ;
53128
54- _expressionPromoterMock . Verify ( e => e . Promote ( It . IsAny < ConstantExpression > ( ) , typeof ( Guid ) , true , true ) , Times . Once ) ;
129+ field ? . SetValue ( field , instance ) ;
130+ }
55131 }
56- }
132+ }
0 commit comments