Skip to content

Commit 9c81745

Browse files
committed
Pass key member name to ValidationResult
1 parent ed45902 commit 9c81745

File tree

6 files changed

+152
-14
lines changed

6 files changed

+152
-14
lines changed

samples/Thinktecture.Runtime.Extensions.Samples/SmartEnums/SmartEnumDemos.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.ComponentModel.DataAnnotations;
12
using Serilog;
23

34
namespace Thinktecture.SmartEnums;
@@ -25,6 +26,17 @@ private static void DemoForNonValidatableEnum(ILogger logger)
2526
if (ProductType.TryGet("Housewares", out var housewares))
2627
logger.Information("Product type {Type} with TryGet found", housewares);
2728

29+
var validationResult = ProductType.Validate("Groceries", out var groceries);
30+
31+
if (validationResult == ValidationResult.Success)
32+
{
33+
logger.Information("Product type {Type} found with Validate", groceries);
34+
}
35+
else
36+
{
37+
logger.Warning("Failed to fetch the product type with Validate. Validation result: {ValidationResult}", validationResult!.ErrorMessage);
38+
}
39+
2840
string keyOfTheProductType = productType;
2941
logger.Information("Implicit conversion of ProductType -> string: {Key}", keyOfTheProductType);
3042

src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/SmartEnums/SmartEnumCodeGenerator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,7 @@ private void GenerateValidate()
441441
}
442442

443443
_sb.Append($@"
444-
return new global::System.ComponentModel.DataAnnotations.ValidationResult($""There is no item of type '{_state.TypeMinimallyQualified}' with the identifier '{{{_state.KeyProperty.ArgumentName}}}'."");
444+
return new global::System.ComponentModel.DataAnnotations.ValidationResult($""There is no item of type '{_state.TypeMinimallyQualified}' with the identifier '{{{_state.KeyProperty.ArgumentName}}}'."", global::Thinktecture.SingleItem.Collection(nameof({_state.TypeFullyQualified}.{_state.KeyProperty.Name})));
445445
}}
446446
}}");
447447
}

src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/ValueObjects/ValueObjectCodeGenerator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,7 @@ private void GenerateValidateMethod(bool allowNullKeyMemberInput, bool allowNull
427427
if({_state.KeyMember.Member.ArgumentName} is null)
428428
{{
429429
obj = default;
430-
return new global::System.ComponentModel.DataAnnotations.ValidationResult(""The argument '{_state.KeyMember.Member.ArgumentName}' must not be null."");
430+
return new global::System.ComponentModel.DataAnnotations.ValidationResult(""The argument '{_state.KeyMember.Member.ArgumentName}' must not be null."", global::Thinktecture.SingleItem.Collection(nameof({_state.TypeFullyQualified}.{_state.KeyMember.Member.Name})));
431431
}}
432432
");
433433
}

test/Thinktecture.Runtime.Extensions.SourceGenerator.Tests/SourceGeneratorTests/EnumSourceGeneratorTests.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ public static bool TryGet([global::System.Diagnostics.CodeAnalysis.AllowNull] st
131131
}
132132
else
133133
{
134-
return new global::System.ComponentModel.DataAnnotations.ValidationResult($"There is no item of type 'TestEnum' with the identifier '{key}'.");
134+
return new global::System.ComponentModel.DataAnnotations.ValidationResult($"There is no item of type 'TestEnum' with the identifier '{key}'.", global::Thinktecture.SingleItem.Collection(nameof(global::Thinktecture.Tests.TestEnum.Key)));
135135
}
136136
}
137137
@@ -606,7 +606,7 @@ public static bool TryGet([global::System.Diagnostics.CodeAnalysis.AllowNull] st
606606
}
607607
else
608608
{
609-
return new global::System.ComponentModel.DataAnnotations.ValidationResult($"There is no item of type 'TestEnum' with the identifier '{key}'.");
609+
return new global::System.ComponentModel.DataAnnotations.ValidationResult($"There is no item of type 'TestEnum' with the identifier '{key}'.", global::Thinktecture.SingleItem.Collection(nameof(global::Thinktecture.Tests.TestEnum.Key)));
610610
}
611611
}
612612
@@ -1014,7 +1014,7 @@ public static bool TryGet([global::System.Diagnostics.CodeAnalysis.AllowNull] st
10141014
}
10151015
else
10161016
{
1017-
return new global::System.ComponentModel.DataAnnotations.ValidationResult($"There is no item of type 'TestEnum' with the identifier '{key}'.");
1017+
return new global::System.ComponentModel.DataAnnotations.ValidationResult($"There is no item of type 'TestEnum' with the identifier '{key}'.", global::Thinktecture.SingleItem.Collection(nameof(global::TestEnum.Key)));
10181018
}
10191019
}
10201020
@@ -1418,7 +1418,7 @@ public static bool TryGet([global::System.Diagnostics.CodeAnalysis.AllowNull] st
14181418
}
14191419
else
14201420
{
1421-
return new global::System.ComponentModel.DataAnnotations.ValidationResult($"There is no item of type 'TestEnum' with the identifier '{key}'.");
1421+
return new global::System.ComponentModel.DataAnnotations.ValidationResult($"There is no item of type 'TestEnum' with the identifier '{key}'.", global::Thinktecture.SingleItem.Collection(nameof(global::Thinktecture.Tests.TestEnum.Key)));
14221422
}
14231423
}
14241424
@@ -1911,7 +1911,7 @@ public static bool TryGet([global::System.Diagnostics.CodeAnalysis.AllowNull] st
19111911
{
19121912
if(key is not null)
19131913
item = CreateAndCheckInvalidItem(key);
1914-
return new global::System.ComponentModel.DataAnnotations.ValidationResult($"There is no item of type 'TestEnum' with the identifier '{key}'.");
1914+
return new global::System.ComponentModel.DataAnnotations.ValidationResult($"There is no item of type 'TestEnum' with the identifier '{key}'.", global::Thinktecture.SingleItem.Collection(nameof(global::Thinktecture.Tests.TestEnum.Key)));
19151915
}
19161916
}
19171917
@@ -2346,7 +2346,7 @@ public static bool TryGet([global::System.Diagnostics.CodeAnalysis.AllowNull] st
23462346
{
23472347
if(key is not null)
23482348
item = CreateAndCheckInvalidItem(key);
2349-
return new global::System.ComponentModel.DataAnnotations.ValidationResult($"There is no item of type 'TestEnum' with the identifier '{key}'.");
2349+
return new global::System.ComponentModel.DataAnnotations.ValidationResult($"There is no item of type 'TestEnum' with the identifier '{key}'.", global::Thinktecture.SingleItem.Collection(nameof(global::Thinktecture.Tests.TestEnum.Key)));
23502350
}
23512351
}
23522352
@@ -2805,7 +2805,7 @@ public static bool TryGet([global::System.Diagnostics.CodeAnalysis.AllowNull] st
28052805
{
28062806
if(name is not null)
28072807
item = CreateAndCheckInvalidItem(name);
2808-
return new global::System.ComponentModel.DataAnnotations.ValidationResult($"There is no item of type 'TestEnum' with the identifier '{name}'.");
2808+
return new global::System.ComponentModel.DataAnnotations.ValidationResult($"There is no item of type 'TestEnum' with the identifier '{name}'.", global::Thinktecture.SingleItem.Collection(nameof(global::Thinktecture.Tests.TestEnum.Name)));
28092809
}
28102810
}
28112811

test/Thinktecture.Runtime.Extensions.SourceGenerator.Tests/SourceGeneratorTests/ValueObjectSourceGeneratorTests.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1243,7 +1243,7 @@ internal static void ModuleInit()
12431243
if(referenceField is null)
12441244
{
12451245
obj = default;
1246-
return new global::System.ComponentModel.DataAnnotations.ValidationResult("The argument 'referenceField' must not be null.");
1246+
return new global::System.ComponentModel.DataAnnotations.ValidationResult("The argument 'referenceField' must not be null.", global::Thinktecture.SingleItem.Collection(nameof(global::Thinktecture.Tests.TestValueObject.ReferenceField)));
12471247
}
12481248
12491249
var validationResult = global::System.ComponentModel.DataAnnotations.ValidationResult.Success;
@@ -1746,7 +1746,7 @@ internal static void ModuleInit()
17461746
if(referenceField is null)
17471747
{
17481748
obj = default;
1749-
return new global::System.ComponentModel.DataAnnotations.ValidationResult("The argument 'referenceField' must not be null.");
1749+
return new global::System.ComponentModel.DataAnnotations.ValidationResult("The argument 'referenceField' must not be null.", global::Thinktecture.SingleItem.Collection(nameof(global::Thinktecture.Tests.TestValueObject.ReferenceField)));
17501750
}
17511751
17521752
var validationResult = global::System.ComponentModel.DataAnnotations.ValidationResult.Success;
@@ -1971,7 +1971,7 @@ internal static void ModuleInit()
19711971
if(referenceField is null)
19721972
{
19731973
obj = default;
1974-
return new global::System.ComponentModel.DataAnnotations.ValidationResult("The argument 'referenceField' must not be null.");
1974+
return new global::System.ComponentModel.DataAnnotations.ValidationResult("The argument 'referenceField' must not be null.", global::Thinktecture.SingleItem.Collection(nameof(global::Thinktecture.Tests.TestValueObject.ReferenceField)));
19751975
}
19761976
19771977
var validationResult = global::System.ComponentModel.DataAnnotations.ValidationResult.Success;
@@ -2194,7 +2194,7 @@ internal static void ModuleInit()
21942194
if(referenceField is null)
21952195
{
21962196
obj = default;
2197-
return new global::System.ComponentModel.DataAnnotations.ValidationResult("The argument 'referenceField' must not be null.");
2197+
return new global::System.ComponentModel.DataAnnotations.ValidationResult("The argument 'referenceField' must not be null.", global::Thinktecture.SingleItem.Collection(nameof(global::Thinktecture.Tests.TestValueObject.ReferenceField)));
21982198
}
21992199
22002200
var validationResult = global::System.ComponentModel.DataAnnotations.ValidationResult.Success;
@@ -4277,7 +4277,7 @@ internal static void ModuleInit()
42774277
if(referenceField is null)
42784278
{
42794279
obj = default;
4280-
return new global::System.ComponentModel.DataAnnotations.ValidationResult("The argument 'referenceField' must not be null.");
4280+
return new global::System.ComponentModel.DataAnnotations.ValidationResult("The argument 'referenceField' must not be null.", global::Thinktecture.SingleItem.Collection(nameof(global::Thinktecture.Tests.TestValueObject.ReferenceField)));
42814281
}
42824282
42834283
var validationResult = global::System.ComponentModel.DataAnnotations.ValidationResult.Success;
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
using System;
2+
using System.Reflection;
3+
using Thinktecture.Runtime.Tests.TestEnums;
4+
5+
namespace Thinktecture.Runtime.Tests.EnumTests;
6+
7+
public class Validate
8+
{
9+
[Fact]
10+
public void Should_return_error_if_null_is_provided()
11+
{
12+
var testEnumValidationResult = TestEnum.Validate(null!, out var testEnum);
13+
testEnumValidationResult.Should().NotBeNull();
14+
testEnumValidationResult.ErrorMessage.Should().Be("There is no item of type 'TestEnum' with the identifier ''.");
15+
testEnumValidationResult.MemberNames.Should().BeEquivalentTo(nameof(TestEnum.Key));
16+
17+
testEnum.Should().BeNull();
18+
19+
var validTestEnumValidationResult = ValidTestEnum.Validate(null, out var validTestEnum);
20+
validTestEnumValidationResult.Should().NotBeNull();
21+
validTestEnumValidationResult.ErrorMessage.Should().Be("There is no item of type 'ValidTestEnum' with the identifier ''.");
22+
validTestEnumValidationResult.MemberNames.Should().BeEquivalentTo(nameof(TestEnum.Key));
23+
24+
validTestEnum.Should().BeNull();
25+
}
26+
27+
[Fact]
28+
public void Should_return_invalid_item_if_enum_doesnt_have_any_items()
29+
{
30+
var validationResult = EmptyEnum.Validate("unknown", out var item);
31+
32+
validationResult.Should().NotBeNull();
33+
validationResult.ErrorMessage.Should().Be("There is no item of type 'EmptyEnum' with the identifier 'unknown'.");
34+
validationResult.MemberNames.Should().BeEquivalentTo(nameof(EmptyEnum.Key));
35+
36+
item.Should().NotBeNull();
37+
item!.IsValid.Should().BeFalse();
38+
item.Key.Should().Be("unknown");
39+
}
40+
41+
[Fact]
42+
public void Should_return_invalid_item_if_enum_doesnt_have_item_with_provided_key()
43+
{
44+
var validationResult = TestEnum.Validate("unknown", out var item);
45+
46+
validationResult.Should().NotBeNull();
47+
validationResult.ErrorMessage.Should().Be("There is no item of type 'TestEnum' with the identifier 'unknown'.");
48+
validationResult.MemberNames.Should().BeEquivalentTo(nameof(TestEnum.Key));
49+
50+
item.Should().NotBeNull();
51+
item!.IsValid.Should().BeFalse();
52+
item.Key.Should().Be("unknown");
53+
}
54+
55+
[Fact]
56+
public void Should_throw_if_CreateInvalidItem_uses_key_of_valid_item()
57+
{
58+
Action action = () => TestEnumWithInvalidCreateInvalidItem.Validate(TestEnumWithInvalidCreateInvalidItem.INVALID_KEY_FOR_TESTING_KEY_REUSE, out _);
59+
action.Should().Throw<Exception>().WithMessage("The implementation of method 'CreateInvalidItem' must not return an instance with property 'Key' equals to one of a valid item.");
60+
}
61+
62+
[Fact]
63+
public void Should_throw_if_CreateInvalidItem_isValid_is_true()
64+
{
65+
Action action = () => TestEnumWithInvalidCreateInvalidItem.Validate(TestEnumWithInvalidCreateInvalidItem.INVALID_KEY_FOR_TESTING_ISVALID_TRUE, out _);
66+
action.Should().Throw<Exception>().WithMessage("The implementation of method 'CreateInvalidItem' must return an instance with property 'IsValid' equals to 'false'.");
67+
}
68+
69+
[Fact]
70+
public void Should_throw_if_custom_validation_throws()
71+
{
72+
Action action = () => TestEnum.Validate(String.Empty, out _);
73+
action.Should().Throw<ArgumentException>().WithMessage("Key cannot be empty.");
74+
}
75+
76+
[Fact]
77+
public void Should_return_item_with_provided_key()
78+
{
79+
TestEnum.Validate("item2", out var testEnum).Should().BeNull();
80+
testEnum.Should().Be(TestEnum.Item2);
81+
82+
ValidTestEnum.Validate("item1", out var validTestEnum).Should().BeNull();
83+
validTestEnum.Should().Be(ValidTestEnum.Item1);
84+
}
85+
86+
[Fact]
87+
public void Should_return_item_with_provided_key_ignoring_casing()
88+
{
89+
TestEnum.Validate("Item1", out var item).Should().BeNull();
90+
item.Should().Be(TestEnum.Item1);
91+
92+
TestEnum.Validate("item1", out item).Should().BeNull();
93+
item.Should().Be(TestEnum.Item1);
94+
}
95+
96+
[Fact]
97+
public void Should_return_invalid_item_if_the_casing_does_not_match_according_to_comparer()
98+
{
99+
var validationResult = TestEnumWithNonDefaultComparer.Validate("Item2", out var item);
100+
validationResult.Should().NotBeNull();
101+
validationResult.ErrorMessage.Should().Be("There is no item of type 'TestEnumWithNonDefaultComparer' with the identifier 'Item2'.");
102+
validationResult.MemberNames.Should().BeEquivalentTo(nameof(TestEnum.Key));
103+
104+
item!.Key.Should().Be("Item2");
105+
item.IsValid.Should().BeFalse();
106+
}
107+
108+
[Fact]
109+
public void Should_return_derived_type()
110+
{
111+
EnumWithDerivedType.Validate(2, out var item).Should().BeNull();
112+
item.Should().Be(EnumWithDerivedType.ItemOfDerivedType);
113+
114+
AbstractEnum.Validate(1, out var otherItem).Should().BeNull();
115+
otherItem.Should().Be(AbstractEnum.Item);
116+
}
117+
118+
[Fact]
119+
public void Should_return_error_if_key_is_unknown_to_non_validatable_enum()
120+
{
121+
var validationResult = ValidTestEnum.Validate("invalid", out var item);
122+
validationResult.ErrorMessage.Should().Be("There is no item of type 'ValidTestEnum' with the identifier 'invalid'.");
123+
124+
item.Should().BeNull();
125+
}
126+
}

0 commit comments

Comments
 (0)