Skip to content

Commit 2f180c0

Browse files
committed
Add unit tests to cover primitive methods on QueryExpression types
1 parent cd8ae40 commit 2f180c0

File tree

3 files changed

+369
-14
lines changed

3 files changed

+369
-14
lines changed

src/JsonApiDotNetCore/Queries/Expressions/LogicalExpression.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,7 @@ public LogicalExpression(LogicalOperator @operator, IImmutableList<FilterExpress
5252
{
5353
ArgumentNullException.ThrowIfNull(filters);
5454

55-
// Workaround for https://youtrack.jetbrains.com/issue/RSRP-496512/Invalid-Use-collection-expression-suggestion.
56-
// ReSharper disable once UseCollectionExpression
57-
ImmutableArray<FilterExpression> terms = filters.WhereNotNull().ToImmutableArray();
55+
ImmutableArray<FilterExpression> terms = [..filters.WhereNotNull()];
5856

5957
return terms.Length > 1 ? new LogicalExpression(@operator, terms) : terms.FirstOrDefault();
6058
}

src/JsonApiDotNetCore/Queries/Expressions/NullConstantExpression.cs

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,17 +35,7 @@ public override string ToFullString()
3535

3636
public override bool Equals(object? obj)
3737
{
38-
if (ReferenceEquals(this, obj))
39-
{
40-
return true;
41-
}
42-
43-
if (obj is null || GetType() != obj.GetType())
44-
{
45-
return false;
46-
}
47-
48-
return true;
38+
return ReferenceEquals(this, obj);
4939
}
5040

5141
public override int GetHashCode()
Lines changed: 367 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,367 @@
1+
using System.Collections.Immutable;
2+
using FluentAssertions;
3+
using JetBrains.Annotations;
4+
using JsonApiDotNetCore.Configuration;
5+
using JsonApiDotNetCore.Queries.Expressions;
6+
using JsonApiDotNetCore.Resources;
7+
using JsonApiDotNetCore.Resources.Annotations;
8+
using Microsoft.Extensions.Logging.Abstractions;
9+
using Microsoft.Extensions.Primitives;
10+
using Xunit;
11+
12+
namespace JsonApiDotNetCoreTests.UnitTests.Queries;
13+
14+
public sealed class QueryExpressionTests
15+
{
16+
public static IEnumerable<object[]> ExpressionTestData =>
17+
new QueryExpression[][]
18+
{
19+
[
20+
TestExpressionFactory.Instance.Any(),
21+
TestExpressionFactory.Instance.Any()
22+
],
23+
[
24+
TestExpressionFactory.Instance.Comparison(),
25+
TestExpressionFactory.Instance.Comparison()
26+
],
27+
[
28+
TestExpressionFactory.Instance.Count(),
29+
TestExpressionFactory.Instance.Count()
30+
],
31+
[
32+
TestExpressionFactory.Instance.Has(),
33+
TestExpressionFactory.Instance.Has()
34+
],
35+
[
36+
TestExpressionFactory.Instance.IncludeElement(),
37+
TestExpressionFactory.Instance.IncludeElement()
38+
],
39+
[
40+
TestExpressionFactory.Instance.Include(),
41+
TestExpressionFactory.Instance.Include()
42+
],
43+
[
44+
TestExpressionFactory.Instance.IsType(),
45+
TestExpressionFactory.Instance.IsType()
46+
],
47+
[
48+
TestExpressionFactory.Instance.LiteralConstant(),
49+
TestExpressionFactory.Instance.LiteralConstant()
50+
],
51+
[
52+
TestExpressionFactory.Instance.Logical(),
53+
TestExpressionFactory.Instance.Logical()
54+
],
55+
[
56+
TestExpressionFactory.Instance.MatchText(),
57+
TestExpressionFactory.Instance.MatchText()
58+
],
59+
[
60+
TestExpressionFactory.Instance.Not(),
61+
TestExpressionFactory.Instance.Not()
62+
],
63+
[
64+
TestExpressionFactory.Instance.NullConstant(),
65+
TestExpressionFactory.Instance.NullConstant()
66+
],
67+
[
68+
TestExpressionFactory.Instance.PaginationElementQueryStringValue(),
69+
TestExpressionFactory.Instance.PaginationElementQueryStringValue()
70+
],
71+
[
72+
TestExpressionFactory.Instance.Pagination(),
73+
TestExpressionFactory.Instance.Pagination()
74+
],
75+
[
76+
TestExpressionFactory.Instance.PaginationQueryStringValue(),
77+
TestExpressionFactory.Instance.PaginationQueryStringValue()
78+
],
79+
[
80+
TestExpressionFactory.Instance.QueryableHandler(),
81+
TestExpressionFactory.Instance.QueryableHandler()
82+
],
83+
[
84+
TestExpressionFactory.Instance.QueryStringParameterScope(),
85+
TestExpressionFactory.Instance.QueryStringParameterScope()
86+
],
87+
[
88+
TestExpressionFactory.Instance.ResourceFieldChainForText(),
89+
TestExpressionFactory.Instance.ResourceFieldChainForText()
90+
],
91+
[
92+
TestExpressionFactory.Instance.ResourceFieldChainForParent(),
93+
TestExpressionFactory.Instance.ResourceFieldChainForParent()
94+
],
95+
[
96+
TestExpressionFactory.Instance.ResourceFieldChainForChildren(),
97+
TestExpressionFactory.Instance.ResourceFieldChainForChildren()
98+
],
99+
[
100+
TestExpressionFactory.Instance.SortElement(),
101+
TestExpressionFactory.Instance.SortElement()
102+
],
103+
[
104+
TestExpressionFactory.Instance.Sort(),
105+
TestExpressionFactory.Instance.Sort()
106+
],
107+
[
108+
TestExpressionFactory.Instance.SparseFieldSet(),
109+
TestExpressionFactory.Instance.SparseFieldSet()
110+
],
111+
[
112+
TestExpressionFactory.Instance.SparseFieldTable(),
113+
TestExpressionFactory.Instance.SparseFieldTable()
114+
]
115+
};
116+
117+
[Theory]
118+
[MemberData(nameof(ExpressionTestData))]
119+
public void Expressions_are_equal(QueryExpression left, QueryExpression right)
120+
{
121+
// Assert
122+
left.Equals(right).Should().BeTrue();
123+
right.Equals(left).Should().BeTrue();
124+
125+
// ReSharper disable once EqualExpressionComparison
126+
left.Equals(left).Should().BeTrue();
127+
}
128+
129+
[Theory]
130+
[MemberData(nameof(ExpressionTestData))]
131+
public void Expressions_are_not_equal_to_null(QueryExpression left, QueryExpression right)
132+
{
133+
// Assert
134+
left.Equals(null).Should().BeFalse();
135+
right.Equals(null).Should().BeFalse();
136+
}
137+
138+
[Theory]
139+
[MemberData(nameof(ExpressionTestData))]
140+
public void Expressions_have_same_hash_code(QueryExpression left, QueryExpression right)
141+
{
142+
// Assert
143+
left.GetHashCode().Should().Be(right.GetHashCode());
144+
}
145+
146+
[Theory]
147+
[MemberData(nameof(ExpressionTestData))]
148+
public void Expressions_convert_to_same_string(QueryExpression left, QueryExpression right)
149+
{
150+
// Assert
151+
left.ToString().Should().Be(right.ToString());
152+
}
153+
154+
[Theory]
155+
[MemberData(nameof(ExpressionTestData))]
156+
public void Expressions_convert_to_same_full_string(QueryExpression left, QueryExpression right)
157+
{
158+
// Assert
159+
left.ToFullString().Should().Be(right.ToFullString());
160+
}
161+
162+
[Theory]
163+
[MemberData(nameof(ExpressionTestData))]
164+
public void Expressions_have_same_return_type(QueryExpression left, QueryExpression right)
165+
{
166+
if (left is FunctionExpression leftFunction && right is FunctionExpression rightFunction)
167+
{
168+
// Assert
169+
leftFunction.ReturnType.Should().Be(rightFunction.ReturnType);
170+
}
171+
}
172+
173+
[Theory]
174+
[MemberData(nameof(ExpressionTestData))]
175+
public void Expressions_can_accept_visitor(QueryExpression left, QueryExpression right)
176+
{
177+
// Assert
178+
left.Accept(EmptyQueryExpressionVisitor.Instance, null).Should().BeNull();
179+
right.Accept(EmptyQueryExpressionVisitor.Instance, null).Should().BeNull();
180+
}
181+
182+
[UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)]
183+
private class BaseTestResource : Identifiable<Guid>
184+
{
185+
[Attr]
186+
public string? Text { get; set; }
187+
188+
[HasOne]
189+
public BaseTestResource? Parent { get; set; }
190+
191+
[HasMany]
192+
public ISet<BaseTestResource> Children { get; set; } = new HashSet<BaseTestResource>();
193+
}
194+
195+
[UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)]
196+
private sealed class DerivedTestResource : BaseTestResource;
197+
198+
private sealed class TestExpressionFactory
199+
{
200+
private readonly ResourceType _baseTestResourceType;
201+
private readonly ResourceType _derivedTestResourceType;
202+
private readonly AttrAttribute _textAttribute;
203+
private readonly RelationshipAttribute _parentRelationship;
204+
private readonly RelationshipAttribute _childrenRelationship;
205+
public static TestExpressionFactory Instance { get; } = new();
206+
207+
private TestExpressionFactory()
208+
{
209+
var options = new JsonApiOptions();
210+
211+
var builder = new ResourceGraphBuilder(options, NullLoggerFactory.Instance);
212+
builder.Add<BaseTestResource, Guid>();
213+
builder.Add<DerivedTestResource, Guid>();
214+
IResourceGraph resourceGraph = builder.Build();
215+
216+
_baseTestResourceType = resourceGraph.GetResourceType<BaseTestResource>();
217+
_derivedTestResourceType = resourceGraph.GetResourceType<DerivedTestResource>();
218+
_textAttribute = _baseTestResourceType.GetAttributeByPropertyName(nameof(BaseTestResource.Text));
219+
_parentRelationship = _baseTestResourceType.GetRelationshipByPropertyName(nameof(BaseTestResource.Parent));
220+
_childrenRelationship = _baseTestResourceType.GetRelationshipByPropertyName(nameof(BaseTestResource.Children));
221+
}
222+
223+
public AnyExpression Any()
224+
{
225+
return new AnyExpression(ResourceFieldChainForText(), [LiteralConstant()]);
226+
}
227+
228+
public ComparisonExpression Comparison()
229+
{
230+
return new ComparisonExpression(ComparisonOperator.Equals, ResourceFieldChainForText(), LiteralConstant());
231+
}
232+
233+
public CountExpression Count()
234+
{
235+
return new CountExpression(ResourceFieldChainForChildren());
236+
}
237+
238+
public HasExpression Has()
239+
{
240+
return new HasExpression(ResourceFieldChainForChildren(), Comparison());
241+
}
242+
243+
public IncludeElementExpression IncludeElement()
244+
{
245+
return new IncludeElementExpression(_parentRelationship, [new IncludeElementExpression(_childrenRelationship)]);
246+
}
247+
248+
public IncludeExpression Include()
249+
{
250+
return new IncludeExpression([IncludeElement()]);
251+
}
252+
253+
public IsTypeExpression IsType()
254+
{
255+
return new IsTypeExpression(ResourceFieldChainForParent(), _derivedTestResourceType, Has());
256+
}
257+
258+
public LiteralConstantExpression LiteralConstant()
259+
{
260+
return new LiteralConstantExpression("example");
261+
}
262+
263+
public LogicalExpression Logical()
264+
{
265+
return new LogicalExpression(LogicalOperator.Or, Comparison(), MatchText());
266+
}
267+
268+
public MatchTextExpression MatchText()
269+
{
270+
return new MatchTextExpression(ResourceFieldChainForText(), LiteralConstant(), TextMatchKind.Contains);
271+
}
272+
273+
public NotExpression Not()
274+
{
275+
return new NotExpression(Comparison());
276+
}
277+
278+
public NullConstantExpression NullConstant()
279+
{
280+
return NullConstantExpression.Instance;
281+
}
282+
283+
public PaginationElementQueryStringValueExpression PaginationElementQueryStringValue()
284+
{
285+
return new PaginationElementQueryStringValueExpression(ResourceFieldChainForChildren(), 5, 8);
286+
}
287+
288+
public PaginationExpression Pagination()
289+
{
290+
return new PaginationExpression(new PageNumber(2), new PageSize(5));
291+
}
292+
293+
public PaginationQueryStringValueExpression PaginationQueryStringValue()
294+
{
295+
return new PaginationQueryStringValueExpression([PaginationElementQueryStringValue()]);
296+
}
297+
298+
public QueryableHandlerExpression QueryableHandler()
299+
{
300+
#pragma warning disable CS8974 // Converting method group to non-delegate type
301+
object handler = TestQueryableHandler;
302+
#pragma warning restore CS8974 // Converting method group to non-delegate type
303+
return new QueryableHandlerExpression(handler, "disableCache");
304+
}
305+
306+
public QueryStringParameterScopeExpression QueryStringParameterScope()
307+
{
308+
return new QueryStringParameterScopeExpression(LiteralConstant(), ResourceFieldChainForChildren());
309+
}
310+
311+
public ResourceFieldChainExpression ResourceFieldChainForText()
312+
{
313+
return new ResourceFieldChainExpression(_textAttribute);
314+
}
315+
316+
public ResourceFieldChainExpression ResourceFieldChainForParent()
317+
{
318+
return new ResourceFieldChainExpression([_parentRelationship]);
319+
}
320+
321+
public ResourceFieldChainExpression ResourceFieldChainForChildren()
322+
{
323+
return new ResourceFieldChainExpression([_childrenRelationship]);
324+
}
325+
326+
public SortElementExpression SortElement()
327+
{
328+
return new SortElementExpression(Count(), false);
329+
}
330+
331+
public SortExpression Sort()
332+
{
333+
return new SortExpression([SortElement()]);
334+
}
335+
336+
public SparseFieldSetExpression SparseFieldSet()
337+
{
338+
return new SparseFieldSetExpression([
339+
_textAttribute,
340+
_childrenRelationship
341+
]);
342+
}
343+
344+
public SparseFieldTableExpression SparseFieldTable()
345+
{
346+
return new SparseFieldTableExpression(new Dictionary<ResourceType, SparseFieldSetExpression>
347+
{
348+
[_baseTestResourceType] = SparseFieldSet(),
349+
[_derivedTestResourceType] = SparseFieldSet()
350+
}.ToImmutableDictionary());
351+
}
352+
353+
private static IQueryable<BaseTestResource> TestQueryableHandler(IQueryable<BaseTestResource> source, StringValues parameterValue)
354+
{
355+
throw new NotImplementedException();
356+
}
357+
}
358+
359+
private sealed class EmptyQueryExpressionVisitor : QueryExpressionVisitor<BaseTestResource?, object?>
360+
{
361+
public static EmptyQueryExpressionVisitor Instance { get; } = new();
362+
363+
private EmptyQueryExpressionVisitor()
364+
{
365+
}
366+
}
367+
}

0 commit comments

Comments
 (0)