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