Skip to content

Commit f4926bd

Browse files
committed
Address feedback and add tests
1 parent 012df36 commit f4926bd

12 files changed

+95
-41
lines changed

src/Http/Http.Extensions/gen/Microsoft.AspNetCore.Http.ValidationsGenerator/Emitters/ValidationsGenerator.Emitter.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,8 @@ private sealed record CacheKey(global::System.Type ContainingType, string Proper
145145
results.AddRange(propertyAttributes);
146146
}
147147
148-
// Check constructors for parameters that match the property name to handle
149-
// record scenarios
148+
// Check constructors for parameters that match the property name
149+
// to handle record scenarios
150150
foreach (var constructor in k.ContainingType.GetConstructors())
151151
{
152152
// Look for parameter with matching name (case insensitive)

src/Http/Http.Extensions/gen/Microsoft.AspNetCore.Http.ValidationsGenerator/Extensions/ITypeSymbolExtensions.cs

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
using System.Collections.Generic;
54
using System.Collections.Immutable;
65
using System.Linq;
76
using Microsoft.CodeAnalysis;
@@ -123,19 +122,4 @@ internal static bool IsExemptType(this ITypeSymbol type, RequiredSymbols require
123122

124123
return null;
125124
}
126-
127-
// Helper method to get all properties including inherited ones
128-
internal static IEnumerable<IPropertySymbol> GetAllProperties(this ITypeSymbol typeSymbol)
129-
{
130-
var current = typeSymbol;
131-
var properties = new List<IPropertySymbol>();
132-
133-
while (current != null && current.SpecialType != SpecialType.System_Object)
134-
{
135-
properties.AddRange(current.GetMembers().OfType<IPropertySymbol>());
136-
current = current.BaseType;
137-
}
138-
139-
return properties;
140-
}
141125
}

src/Http/Http.Extensions/test/ValidationsGenerator/ValidationsGenerator.ComplexType.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,11 @@ public class ComplexType
3939
public int IntegerWithRangeAndDisplayName { get; set; } = 50;
4040
4141
[Required]
42-
public SubType PropertyWithMemberAttributes { get; set; } = new SubType();
42+
public SubType PropertyWithMemberAttributes { get; set; } = new SubType("some-value", default);
4343
44-
public SubType PropertyWithoutMemberAttributes { get; set; } = new SubType();
44+
public SubType PropertyWithoutMemberAttributes { get; set; } = new SubType("some-value", default);
4545
46-
public SubTypeWithInheritance PropertyWithInheritance { get; set; } = new SubTypeWithInheritance();
46+
public SubTypeWithInheritance PropertyWithInheritance { get; set; } = new SubTypeWithInheritance("some-value", default);
4747
4848
public List<SubType> ListOfSubTypes { get; set; } = [];
4949
@@ -62,16 +62,16 @@ public class DerivedValidationAttribute : ValidationAttribute
6262
public override bool IsValid(object? value) => value is int number && number % 2 == 0;
6363
}
6464
65-
public class SubType
65+
public class SubType(string? requiredProperty, string? stringWithLength)
6666
{
6767
[Required]
68-
public string RequiredProperty { get; set; } = "some-value";
68+
public string RequiredProperty { get; } = requiredProperty;
6969
7070
[StringLength(10)]
71-
public string? StringWithLength { get; set; }
71+
public string? StringWithLength { get; } = stringWithLength;
7272
}
7373
74-
public class SubTypeWithInheritance : SubType
74+
public class SubTypeWithInheritance(string? requiredProperty, string? stringWithLength) : SubType(requiredProperty, stringWithLength)
7575
{
7676
[EmailAddress]
7777
public string? EmailString { get; set; }

src/Http/Http.Extensions/test/ValidationsGenerator/ValidationsGenerator.RecordType.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,15 @@ public record SubType([Required] string RequiredProperty = "some-value", [String
3939
4040
public record SubTypeWithInheritance([EmailAddress] string? EmailString, string RequiredProperty, string? StringWithLength) : SubType(RequiredProperty, StringWithLength);
4141
42+
public record SubTypeWithoutConstructor
43+
{
44+
[Required]
45+
public string RequiredProperty { get; set; } = "some-value";
46+
47+
[StringLength(10)]
48+
public string? StringWithLength { get; set; }
49+
}
50+
4251
public static class CustomValidators
4352
{
4453
public static ValidationResult Validate(int number, ValidationContext validationContext)
@@ -63,6 +72,7 @@ public record ValidatableRecord(
6372
SubType PropertyWithMemberAttributes = default,
6473
SubType PropertyWithoutMemberAttributes = default,
6574
SubTypeWithInheritance PropertyWithInheritance = default,
75+
SubTypeWithoutConstructor PropertyOfSubtypeWithoutConstructor = default,
6676
List<SubType> ListOfSubTypes = default,
6777
[DerivedValidation(ErrorMessage = "Value must be an even number")]
6878
int IntegerWithDerivedValidationAttribute = 0,
@@ -83,6 +93,7 @@ await VerifyEndpoint(compilation, "/validatable-record", async (endpoint, servic
8393
await InvalidPropertyWithDerivedValidationAttributeProducesError(endpoint);
8494
await InvalidPropertyWithMultipleAttributesProducesError(endpoint);
8595
await InvalidPropertyWithCustomValidationProducesError(endpoint);
96+
await InvalidPropertyOfSubtypeWithoutConstructorProducesError(endpoint);
8697
await ValidInputProducesNoWarnings(endpoint);
8798

8899
async Task InvalidIntegerWithRangeProducesError(Endpoint endpoint)
@@ -296,6 +307,34 @@ async Task InvalidPropertyWithCustomValidationProducesError(Endpoint endpoint)
296307
});
297308
}
298309

310+
async Task InvalidPropertyOfSubtypeWithoutConstructorProducesError(Endpoint endpoint)
311+
{
312+
var payload = """
313+
{
314+
"PropertyOfSubtypeWithoutConstructor": {
315+
"RequiredProperty": "",
316+
"StringWithLength": "way-too-long"
317+
}
318+
}
319+
""";
320+
var context = CreateHttpContextWithPayload(payload, serviceProvider);
321+
322+
await endpoint.RequestDelegate(context);
323+
324+
var problemDetails = await AssertBadRequest(context);
325+
Assert.Collection(problemDetails.Errors,
326+
kvp =>
327+
{
328+
Assert.Equal("PropertyOfSubtypeWithoutConstructor.RequiredProperty", kvp.Key);
329+
Assert.Equal("The RequiredProperty field is required.", kvp.Value.Single());
330+
},
331+
kvp =>
332+
{
333+
Assert.Equal("PropertyOfSubtypeWithoutConstructor.StringWithLength", kvp.Key);
334+
Assert.Equal("The field StringWithLength must be a string with a maximum length of 10.", kvp.Value.Single());
335+
});
336+
}
337+
299338
async Task ValidInputProducesNoWarnings(Endpoint endpoint)
300339
{
301340
var payload = """

src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.CanValidateComplexTypes#ValidatableInfoResolver.g.verified.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,8 +228,8 @@ private sealed record CacheKey(global::System.Type ContainingType, string Proper
228228
results.AddRange(propertyAttributes);
229229
}
230230

231-
// Check constructors for parameters that match the property name to handle
232-
// record scenarios
231+
// Check constructors for parameters that match the property name
232+
// to handle record scenarios
233233
foreach (var constructor in k.ContainingType.GetConstructors())
234234
{
235235
// Look for parameter with matching name (case insensitive)

src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.CanValidateIValidatableObject#ValidatableInfoResolver.g.verified.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,8 +179,8 @@ private sealed record CacheKey(global::System.Type ContainingType, string Proper
179179
results.AddRange(propertyAttributes);
180180
}
181181

182-
// Check constructors for parameters that match the property name to handle
183-
// record scenarios
182+
// Check constructors for parameters that match the property name
183+
// to handle record scenarios
184184
foreach (var constructor in k.ContainingType.GetConstructors())
185185
{
186186
// Look for parameter with matching name (case insensitive)

src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.CanValidateParameters#ValidatableInfoResolver.g.verified.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,8 @@ private sealed record CacheKey(global::System.Type ContainingType, string Proper
117117
results.AddRange(propertyAttributes);
118118
}
119119

120-
// Check constructors for parameters that match the property name to handle
121-
// record scenarios
120+
// Check constructors for parameters that match the property name
121+
// to handle record scenarios
122122
foreach (var constructor in k.ContainingType.GetConstructors())
123123
{
124124
// Look for parameter with matching name (case insensitive)

src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.CanValidatePolymorphicTypes#ValidatableInfoResolver.g.verified.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -217,8 +217,8 @@ private sealed record CacheKey(global::System.Type ContainingType, string Proper
217217
results.AddRange(propertyAttributes);
218218
}
219219

220-
// Check constructors for parameters that match the property name to handle
221-
// record scenarios
220+
// Check constructors for parameters that match the property name
221+
// to handle record scenarios
222222
foreach (var constructor in k.ContainingType.GetConstructors())
223223
{
224224
// Look for parameter with matching name (case insensitive)

src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.CanValidateRecordTypes#ValidatableInfoResolver.g.verified.cs

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@ public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.
7070
validatableInfo = CreateSubTypeWithInheritance();
7171
return true;
7272
}
73+
if (type == typeof(global::SubTypeWithoutConstructor))
74+
{
75+
validatableInfo = CreateSubTypeWithoutConstructor();
76+
return true;
77+
}
7378
if (type == typeof(global::ValidatableRecord))
7479
{
7580
validatableInfo = CreateValidatableRecord();
@@ -120,6 +125,26 @@ private ValidatableTypeInfo CreateSubTypeWithInheritance()
120125
]
121126
);
122127
}
128+
private ValidatableTypeInfo CreateSubTypeWithoutConstructor()
129+
{
130+
return new GeneratedValidatableTypeInfo(
131+
type: typeof(global::SubTypeWithoutConstructor),
132+
members: [
133+
new GeneratedValidatablePropertyInfo(
134+
containingType: typeof(global::SubTypeWithoutConstructor),
135+
propertyType: typeof(string),
136+
name: "RequiredProperty",
137+
displayName: "RequiredProperty"
138+
),
139+
new GeneratedValidatablePropertyInfo(
140+
containingType: typeof(global::SubTypeWithoutConstructor),
141+
propertyType: typeof(string),
142+
name: "StringWithLength",
143+
displayName: "StringWithLength"
144+
),
145+
]
146+
);
147+
}
123148
private ValidatableTypeInfo CreateValidatableRecord()
124149
{
125150
return new GeneratedValidatableTypeInfo(
@@ -155,6 +180,12 @@ private ValidatableTypeInfo CreateValidatableRecord()
155180
name: "PropertyWithInheritance",
156181
displayName: "PropertyWithInheritance"
157182
),
183+
new GeneratedValidatablePropertyInfo(
184+
containingType: typeof(global::ValidatableRecord),
185+
propertyType: typeof(global::SubTypeWithoutConstructor),
186+
name: "PropertyOfSubtypeWithoutConstructor",
187+
displayName: "PropertyOfSubtypeWithoutConstructor"
188+
),
158189
new GeneratedValidatablePropertyInfo(
159190
containingType: typeof(global::ValidatableRecord),
160191
propertyType: typeof(global::System.Collections.Generic.List<global::SubType>),
@@ -228,8 +259,8 @@ private sealed record CacheKey(global::System.Type ContainingType, string Proper
228259
results.AddRange(propertyAttributes);
229260
}
230261

231-
// Check constructors for parameters that match the property name to handle
232-
// record scenarios
262+
// Check constructors for parameters that match the property name
263+
// to handle record scenarios
233264
foreach (var constructor in k.ContainingType.GetConstructors())
234265
{
235266
// Look for parameter with matching name (case insensitive)

src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.CanValidateRecursiveTypes#ValidatableInfoResolver.g.verified.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,8 @@ private sealed record CacheKey(global::System.Type ContainingType, string Proper
142142
results.AddRange(propertyAttributes);
143143
}
144144

145-
// Check constructors for parameters that match the property name to handle
146-
// record scenarios
145+
// Check constructors for parameters that match the property name
146+
// to handle record scenarios
147147
foreach (var constructor in k.ContainingType.GetConstructors())
148148
{
149149
// Look for parameter with matching name (case insensitive)

0 commit comments

Comments
 (0)