Skip to content

Commit 0abb94d

Browse files
committed
More tests and fixes for IValueObjectConverter and IValueObjectFactory
1 parent 1ee98c8 commit 0abb94d

File tree

14 files changed

+8967
-7477
lines changed

14 files changed

+8967
-7477
lines changed

src/Thinktecture.Runtime.Extensions.AspNetCore/AspNetCore/ModelBinding/TrimmingSmartEnumModelBinder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace Thinktecture.AspNetCore.ModelBinding;
88
/// </summary>
99
/// <typeparam name="T">Type of the value object.</typeparam>
1010
public sealed class TrimmingSmartEnumModelBinder<T> : ValueObjectModelBinderBase<T, string>
11-
where T : IKeyedValueObject<T, string>
11+
where T : IValueObjectFactory<T, string>
1212
{
1313
/// <summary>
1414
/// Initializes a new instance of <see cref="ValueObjectModelBinder{T,TKey}"/>.

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,9 @@ private void GenerateEnum(CancellationToken cancellationToken)
5252

5353
_sb.Append(@"
5454
[global::System.ComponentModel.TypeConverter(typeof(global::Thinktecture.ValueObjectTypeConverter<").Append(_state.TypeFullyQualified).Append(", ").Append(_state.KeyProperty.TypeFullyQualified).Append(@">))]
55-
partial ").Append(_state.IsReferenceType ? "class" : "struct").Append(" ").Append(_state.Name).Append(" : global::Thinktecture.IEnum<").Append(_state.KeyProperty.TypeFullyQualified).Append(", ").Append(_state.TypeFullyQualified).Append(">,");
55+
partial ").Append(_state.IsReferenceType ? "class" : "struct").Append(" ").Append(_state.Name).Append(" : global::Thinktecture.IEnum<").Append(_state.KeyProperty.TypeFullyQualified).Append(", ").Append(_state.TypeFullyQualified).Append(@">,
56+
global::Thinktecture.IValueObjectFactory<").Append(_state.TypeFullyQualified).Append(", ").Append(_state.KeyProperty.TypeFullyQualified).Append(@">,
57+
global::Thinktecture.IValueObjectConverter<").Append(_state.KeyProperty.TypeFullyQualified).Append(">,");
5658

5759
foreach (var desiredFactory in _state.Settings.DesiredFactories)
5860
{
@@ -64,8 +66,8 @@ private void GenerateEnum(CancellationToken cancellationToken)
6466

6567
if (desiredFactory.UseForSerialization != SerializationFrameworks.None)
6668
{
67-
_sb.Append(@",
68-
global::Thinktecture.IValueObjectConverter<").Append(desiredFactory.TypeFullyQualified).Append(">");
69+
_sb.Append(@"
70+
global::Thinktecture.IValueObjectConverter<").Append(desiredFactory.TypeFullyQualified).Append(">,");
6971
}
7072
}
7173

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,14 @@ private void GenerateValueObject(bool emptyStringYieldsNull, CancellationToken c
6060
if (_state.HasKeyMember)
6161
{
6262
_sb.Append(@",
63-
global::Thinktecture.IKeyedValueObject<").Append(_state.KeyMember.Member.TypeFullyQualifiedWithNullability).Append(">");
63+
global::Thinktecture.IKeyedValueObject<").Append(_state.KeyMember.Member.TypeFullyQualifiedWithNullability).Append(@">,
64+
global::Thinktecture.IValueObjectConverter<").Append(_state.KeyMember.Member.TypeFullyQualified).Append(">");
6465

6566
if (!_state.Settings.SkipFactoryMethods)
6667
{
6768
_sb.Append(@",
68-
global::Thinktecture.IKeyedValueObject<").Append(_state.TypeFullyQualified).Append(", ").Append(_state.KeyMember.Member.TypeFullyQualifiedWithNullability).Append(">");
69+
global::Thinktecture.IKeyedValueObject<").Append(_state.TypeFullyQualified).Append(", ").Append(_state.KeyMember.Member.TypeFullyQualifiedWithNullability).Append(@">,
70+
global::Thinktecture.IValueObjectFactory<").Append(_state.TypeFullyQualified).Append(", ").Append(_state.KeyMember.Member.TypeFullyQualified).Append(">");
6971
}
7072
}
7173
else

src/Thinktecture.Runtime.Extensions/IKeyedValueObject.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public interface IKeyedValueObject
1414
/// Common interface of keyed value objects.
1515
/// </summary>
1616
/// <typeparam name="TKey">Type of the key member.</typeparam>
17-
public interface IKeyedValueObject<out TKey> : IValueObjectConverter<TKey>, IKeyedValueObject
17+
public interface IKeyedValueObject<out TKey> : IKeyedValueObject
1818
where TKey : notnull
1919
{
2020
/// <summary>
@@ -32,7 +32,7 @@ public interface IKeyedValueObject<out TKey> : IValueObjectConverter<TKey>, IKey
3232
/// <remarks>
3333
/// Don't implement this interface directly. It will be implemented by a source generator.
3434
/// </remarks>
35-
public interface IKeyedValueObject<T, TKey> : IKeyedValueObject<TKey>, IValueObjectFactory<T, TKey>
35+
public interface IKeyedValueObject<T, TKey> : IKeyedValueObject<TKey>
3636
where TKey : notnull
3737
{
3838
/// <summary>

src/Thinktecture.Runtime.Extensions/IValueObjectConverter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@ public interface IValueObjectConverter<out T>
1313
/// <summary>
1414
/// Converts the value object to type <typeparamref name="T"/>.
1515
/// </summary>
16-
T ToValue() => throw new NotImplementedException("This method will be implemented by the source generator.");
16+
T ToValue();
1717
}

src/Thinktecture.Runtime.Extensions/IValueObjectFactory.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,5 @@ public interface IValueObjectFactory<T, TValue> : IValueObjectFactory<TValue>
3333
/// <param name="provider">An object that provides culture-specific formatting information.</param>
3434
/// <param name="item">Item with key property equals to the provided <paramref name="value"/>.</param>
3535
/// <returns>Validation result.</returns>
36-
static virtual ValidationResult? Validate(TValue? value, IFormatProvider? provider, out T? item) => throw new NotImplementedException("This method will be implemented by the source generator.");
36+
static abstract ValidationResult? Validate(TValue? value, IFormatProvider? provider, out T? item);
3737
}

test/Thinktecture.Runtime.Extensions.AspNetCore.Tests/AspNetCore/ModelBinding/ValueObjectModelBinderProviderTests/GetBinder.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,19 @@ public void Should_return_binder_for_string_based_value_type()
3232
}
3333

3434
[Fact]
35-
public void Should_return_string_base_binder_specified_by_ValueObjectFactoryAttribute()
35+
public void Should_return_string_base_binder_specified_by_ValueObjectFactoryAttribute_of_value_object_()
3636
{
3737
var binder = GetModelBinder<BoundaryWithFactories>();
3838
binder.Should().BeOfType<ValueObjectModelBinder<BoundaryWithFactories, string>>();
3939
}
4040

41+
[Fact]
42+
public void Should_return_string_base_binder_specified_by_ValueObjectFactoryAttribute_smart_enum()
43+
{
44+
var binder = GetModelBinder<EnumWithFactory>();
45+
binder.Should().BeOfType<TrimmingSmartEnumModelBinder<EnumWithFactory>>();
46+
}
47+
4148
[Fact]
4249
public void Should_return_null_for_non_enums_and_non_value_types()
4350
{

test/Thinktecture.Runtime.Extensions.AspNetCore.Tests/AspNetCore/ModelBinding/ValueObjectModelBinderTests/BindModelAsync.cs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ public async Task Should_return_error_if_value_violates_validation_rules()
197197
}
198198

199199
[Fact]
200-
public async Task Should_bind_successfully_having_string_base_factory_specified_by_ValueObjectFactoryAttribute()
200+
public async Task Should_bind_successfully_value_object_having_string_base_factory_specified_by_ValueObjectFactoryAttribute()
201201
{
202202
var ctx = await BindAsync<BoundaryWithFactories, string>("1:2");
203203

@@ -207,7 +207,7 @@ public async Task Should_bind_successfully_having_string_base_factory_specified_
207207
}
208208

209209
[Fact]
210-
public async Task Should_return_error_having_string_base_factory_specified_by_ValueObjectFactoryAttribute()
210+
public async Task Should_return_error_when_binding_value_object_having_string_base_factory_specified_by_ValueObjectFactoryAttribute()
211211
{
212212
var ctx = await BindAsync<BoundaryWithFactories, string>("1");
213213

@@ -216,6 +216,26 @@ public async Task Should_return_error_having_string_base_factory_specified_by_Va
216216
ctx.Result.IsModelSet.Should().BeFalse();
217217
}
218218

219+
[Fact]
220+
public async Task Should_bind_successfully_smart_enum_having_string_base_factory_specified_by_ValueObjectFactoryAttribute()
221+
{
222+
var ctx = await BindAsync<EnumWithFactory, string>("=1=");
223+
224+
ctx.ModelState.ErrorCount.Should().Be(0);
225+
ctx.Result.IsModelSet.Should().BeTrue();
226+
ctx.Result.Model.Should().Be(EnumWithFactory.Item1);
227+
}
228+
229+
[Fact]
230+
public async Task Should_return_error_when_binding_smart_enum_having_string_base_factory_specified_by_ValueObjectFactoryAttribute()
231+
{
232+
var ctx = await BindAsync<EnumWithFactory, string>("A");
233+
234+
ctx.ModelState.ErrorCount.Should().Be(1);
235+
ctx.ModelState[ctx.ModelName]!.Errors.Should().BeEquivalentTo(new[] { new ModelError("Unknown item 'A'") });
236+
ctx.Result.IsModelSet.Should().BeFalse();
237+
}
238+
219239
private static async Task<DefaultModelBindingContext> BindAsync<T, TKey>(
220240
string value)
221241
where T : IValueObjectFactory<T, TKey>

0 commit comments

Comments
 (0)