Skip to content

Commit 2f2efbf

Browse files
committed
Enhance nested/collection validation API, add tests, update .NET
Validation API improvements and .NET/test updates -- Refactor RuleSet API for clearer, more flexible nested and collection validation, including new NotNullAllNested/AllNested methods and improved property chaining. -- Allow nullable collections in struct validation. -- Improve error reporting for nested validation scenarios. Expanded test coverage for nested validation -- Add NestedCollectionTests.cs with comprehensive tests for nested and collection validation, including IValidable and custom delegates, and null collection handling. -- Add new tests in ValidateTests.cs for property name chaining and nested validation. Support types for new tests -- Introduce OrderColl, AddressColl, FooColl, BarColl, BazColl, Catalog, and Product classes to demonstrate and test nested validation patterns. .NET and package version updates -- Add support for net8.0, net9.0, net10.0; update test dependencies to latest versions; update libs.targets for new frameworks. Miscellaneous improvements -- Bump preview version numbers for solution and packages. -- Improve code formatting and documentation for clarity.
1 parent 26ca50e commit 2f2efbf

File tree

7 files changed

+357
-99
lines changed

7 files changed

+357
-99
lines changed

Directory.Build.props

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project>
22
<PropertyGroup>
3-
<FrmsVer>net8.0</FrmsVer>
4-
<TestVer>net8.0</TestVer>
3+
<FrmsVer>net8.0;net9.0;net10.0</FrmsVer>
4+
<TestVer>net10.0</TestVer>
55
</PropertyGroup>
66
<PropertyGroup>
77
<LangVersion>latest</LangVersion>
@@ -10,9 +10,9 @@
1010
</PropertyGroup>
1111
<PropertyGroup>
1212
<SVVer>1.0.0</SVVer>
13-
<SVPreview>-preview-2.2</SVPreview>
13+
<SVPreview>-preview-3.0</SVPreview>
1414
</PropertyGroup>
1515
<PropertyGroup>
16-
<SPVer>1.0.0-preview-4.8</SPVer>
16+
<SPVer>1.0.0-preview-6.0</SPVer>
1717
</PropertyGroup>
1818
</Project>
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
namespace RoyalCode.SmartValidations.Tests;
2+
3+
using RoyalCode.SmartProblems;
4+
using System.ComponentModel;
5+
using System.Diagnostics.CodeAnalysis;
6+
7+
public class NestedCollectionTests
8+
{
9+
[Fact]
10+
public void AllNested_NoProblems()
11+
{
12+
// Arrange
13+
var order = new OrderColl
14+
{
15+
Addresses =
16+
[
17+
new AddressColl { Street = "123 Main St", City = "Anytown", ZipCode = "12345", Country = "USA" },
18+
new AddressColl { Street = "456 Oak Ave", City = "Othertown", ZipCode = "67890", Country = "USA" }
19+
]
20+
};
21+
22+
// Act
23+
var hasProblems = order.HasProblems(out var problems);
24+
25+
// Assert
26+
Assert.False(hasProblems);
27+
Assert.Null(problems);
28+
}
29+
30+
[Fact]
31+
public void AllNested_WithNestedProblems()
32+
{
33+
// Arrange
34+
var order = new OrderColl
35+
{
36+
Addresses =
37+
[
38+
new AddressColl { Street = string.Empty, City = "Anytown", ZipCode = "12345", Country = "USA" },
39+
new AddressColl { Street = "456 Oak Ave", City = "Othertown", ZipCode = "67890", Country = "USA" }
40+
]
41+
};
42+
43+
// Act
44+
var hasProblems = order.HasProblems(out var problems);
45+
46+
// Assert
47+
Assert.True(hasProblems);
48+
Assert.NotNull(problems);
49+
Assert.Single(problems);
50+
}
51+
52+
[Fact]
53+
public void AllNested_WithNestedProblems_And_NestedValidable()
54+
{
55+
// Arrange
56+
var foo = new FooColl
57+
{
58+
// FooColl.Value empty -> 1 problem
59+
Bars =
60+
[
61+
new BarColl { Value = string.Empty }, // -> 1 problem
62+
new BarColl { Value = "ok" }
63+
]
64+
};
65+
66+
// Act
67+
var hasProblems = foo.HasProblems(out var problems);
68+
69+
// Assert
70+
Assert.True(hasProblems);
71+
Assert.NotNull(problems);
72+
Assert.Equal(2, problems.Count);
73+
}
74+
75+
[Fact]
76+
public void AllNested_WithNestedProblems_And_NestedValidable_And_NestedValidateFunc()
77+
{
78+
// Arrange
79+
var foo = new FooColl
80+
{
81+
Value = "Foo Value", // valid
82+
Bars = [ new BarColl { Value = "Bar Value" } ], // valid
83+
Bazes = [ new BazColl() ] // invalid -> 1 problem
84+
};
85+
86+
// Act
87+
var hasProblems = foo.HasProblems(out var problems);
88+
89+
// Assert
90+
Assert.True(hasProblems);
91+
Assert.NotNull(problems);
92+
Assert.Single(problems);
93+
}
94+
95+
[Fact]
96+
public void AllNested_NullCollection_NoProblems()
97+
{
98+
// Arrange
99+
var order = new OrderColl { Addresses = null };
100+
101+
// Act
102+
var hasProblems = order.HasProblems(out var problems);
103+
104+
// Assert
105+
Assert.False(hasProblems);
106+
Assert.Null(problems);
107+
}
108+
}
109+
110+
[DisplayName("Order (Collection)")]
111+
file class OrderColl : IValidable
112+
{
113+
[DisplayName("Addresses")]
114+
public List<AddressColl>? Addresses { get; set; }
115+
116+
public bool HasProblems([NotNullWhen(true)] out Problems? problems)
117+
{
118+
return Rules.Set<OrderColl>()
119+
.AllNested(Addresses, address => Rules.Set<AddressColl>()
120+
.WithPropertyPrefix("address")
121+
.NotEmpty(address.Street)
122+
.NotEmpty(address.City)
123+
.NotEmpty(address.ZipCode)
124+
.NotEmpty(address.Country))
125+
.HasProblems(out problems);
126+
}
127+
}
128+
129+
[DisplayName("Address (Collection)")]
130+
file class AddressColl
131+
{
132+
[DisplayName("Street")]
133+
public string Street { get; set; } = string.Empty;
134+
135+
[DisplayName("City")]
136+
public string City { get; set; } = string.Empty;
137+
138+
[DisplayName("Zip Code")]
139+
public string ZipCode { get; set; } = string.Empty;
140+
141+
[DisplayName("Country")]
142+
public string Country { get; set; } = string.Empty;
143+
}
144+
145+
file class FooColl : IValidable
146+
{
147+
public string? Value { get; set; } = string.Empty;
148+
149+
public IEnumerable<BarColl>? Bars { get; set; }
150+
151+
public IEnumerable<BazColl>? Bazes { get; set; }
152+
153+
public bool HasProblems([NotNullWhen(true)] out Problems? problems)
154+
{
155+
return Rules.Set<FooColl>()
156+
.NotEmpty(Value)
157+
.AllNested(Bars)
158+
.AllNested(Bazes, b => b.HasProblems)
159+
.HasProblems(out problems);
160+
}
161+
}
162+
163+
file class BarColl : IValidable
164+
{
165+
public string? Value { get; set; } = string.Empty;
166+
167+
public bool HasProblems([NotNullWhen(true)] out Problems? problems)
168+
{
169+
return Rules.Set<BarColl>()
170+
.NotEmpty(Value)
171+
.HasProblems(out problems);
172+
}
173+
}
174+
175+
file class BazColl
176+
{
177+
public string? Value { get; set; } = string.Empty;
178+
179+
public bool HasProblems([NotNullWhen(true)] out Problems? problems)
180+
{
181+
return Rules.Set<BazColl>()
182+
.NotEmpty(Value)
183+
.HasProblems(out problems);
184+
}
185+
}

RoyalCode.SmartValidations.Tests/NestedTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,4 +190,4 @@ public bool HasProblems([NotNullWhen(true)] out Problems? problems)
190190
.NotEmpty(Value)
191191
.HasProblems(out problems);
192192
}
193-
}
193+
}

RoyalCode.SmartValidations.Tests/RoyalCode.SmartValidations.Tests.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>net8.0</TargetFramework>
4+
<TargetFramework>$(TestVer)</TargetFramework>
55
<ImplicitUsings>enable</ImplicitUsings>
66
<Nullable>enable</Nullable>
77

@@ -10,9 +10,9 @@
1010
</PropertyGroup>
1111

1212
<ItemGroup>
13-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1"/>
13+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1"/>
1414
<PackageReference Include="xunit" Version="2.9.3"/>
15-
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.3">
15+
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
1616
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1717
<PrivateAssets>all</PrivateAssets>
1818
</PackageReference>

RoyalCode.SmartValidations.Tests/ValidateTests.cs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,64 @@ public void Validate_Struct_WithProblems()
3838
Assert.True(hasProblems);
3939
Assert.NotNull(problems);
4040
Assert.Equal(3, problems.Count); // Name, Quantity, Price
41+
42+
// Assert property names for each problem
43+
var problemList = problems.ToList();
44+
Assert.Contains(problemList, p => p.Property == nameof(product));
45+
}
46+
47+
[Fact]
48+
public void Validate_ClassWithStruct_WithProblems()
49+
{
50+
// Arrange
51+
var catalog = new Catalog
52+
{
53+
Title = "My Catalog",
54+
Products =
55+
[
56+
new Product(), // invalid -> 3 problems
57+
new Product { Name = "Valid", Quantity = 1, Price = 0.01m } // valid
58+
]
59+
};
60+
// Act
61+
var hasProblems = catalog.HasProblems(out var problems);
62+
63+
// Assert
64+
Assert.True(hasProblems);
65+
Assert.NotNull(problems);
66+
Assert.Equal(3, problems.Count); // Only the first product has problems
67+
68+
// Assert property names for each problem
69+
var problemList = problems.ToList();
70+
Assert.Contains(problemList, p => p.Property == nameof(Catalog.Products));
71+
}
72+
73+
[Fact]
74+
public void Validate_Nestes_ClassWithStruct_WithProblems()
75+
{
76+
// Arrange
77+
var catalog = new Catalog
78+
{
79+
Title = "My Catalog",
80+
Products =
81+
[
82+
new Product(), // invalid -> 3 problems
83+
new Product { Name = "Valid", Quantity = 1, Price = 0.01m } // valid
84+
]
85+
};
86+
// Act
87+
var rules = Rules.Set().Nested(catalog);
88+
var hasProblems = rules.HasProblems(out var problems);
89+
90+
// Assert
91+
Assert.True(hasProblems);
92+
Assert.NotNull(problems);
93+
Assert.Equal(3, problems.Count); // Only the first product has problems
94+
95+
// Assert property names for each problem
96+
var problemList = problems.ToList();
97+
var expectedPropertyName = $"{nameof(catalog)}.{nameof(Catalog.Products)}";
98+
Assert.Contains(problemList, p => p.Property == expectedPropertyName);
4199
}
42100

43101
[Fact]
@@ -110,3 +168,16 @@ public bool HasProblems(out Problems? problems)
110168
.HasProblems(out problems);
111169
}
112170
}
171+
172+
file class Catalog : IValidable
173+
{
174+
public string? Title { get; set; }
175+
public Product[]? Products { get; set; }
176+
public bool HasProblems(out Problems? problems)
177+
{
178+
return Rules.Set<Catalog>()
179+
.NotEmpty(Title)
180+
.Validate(Products)
181+
.HasProblems(out problems);
182+
}
183+
}

0 commit comments

Comments
 (0)