Skip to content

Commit

Permalink
Merge pull request #28 from tgharold/24-add-index-number-to-member-na…
Browse files Browse the repository at this point in the history
…me-for-enumerables-1

v2: Report the index position in IEnumerable validations
  • Loading branch information
tgharold authored Nov 18, 2022
2 parents d4b01ae + 219fc02 commit 640003e
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 23 deletions.
12 changes: 12 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Breaking Changes

Any breaking changes will require either a change in the code that uses the library or describes a change in behavior.

## v2.0.0: November 2022

The member names reported back for IEnumerable collection properties will now return the index inside of square brackets as suggested by issue #24.

- OLD: `Items.SimpleA.BoolC`
- NEW: `Items[1].SimpleA.BoolC`

This will impact any code that was parsing the member name string if IEnumerable properties were validated.
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,12 @@ private bool TryValidateObjectRecursive(
continue;

case IEnumerable asEnumerable:
var arrayIndex = -1;
foreach (var item in asEnumerable)
{
//NOTE: This does not tell you which item in the IEnumerable<T> failed
//Possibly, should have a separate case for Array/Dictionary
arrayIndex++;

//NOTE: Possibly should have a separate case for Dictionary which reports on the key

if (item == null) continue;
nestedResults = new List<ValidationResult>();
Expand All @@ -119,7 +121,9 @@ private bool TryValidateObjectRecursive(
validationResults.Add(
new ValidationResult(
validationResult.ErrorMessage,
validationResult.MemberNames.Select(x => property1.Name + '.' + x)
validationResult.MemberNames
.Select(x => $"{property1.Name}[{arrayIndex}].{x}")
.ToList()
));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public void Passes_all_validation_with_children()
{
var model = new EnumerableExample
{
Name = "Passes all",
Name = "Passes all with children",
Age = 75,
Items = new List<ItemExample>
{
Expand Down Expand Up @@ -125,7 +125,7 @@ public void Fails_on_Items_Child2_SimpleA_BoolC()
{
var model = new EnumerableExample
{
Name = "Passes all",
Name = "Fails_on_Items_Child2_SimpleA_BoolC",
Age = 75,
Items = new List<ItemExample>
{
Expand Down Expand Up @@ -159,23 +159,22 @@ public void Fails_on_Items_Child2_SimpleA_BoolC()

Assert.False(result);
Assert.NotEmpty(validationResults);
Assert.NotNull(validationResults
.FirstOrDefault(x => x.MemberNames.Contains("Items.SimpleA.BoolC")));
AssertThereIsAnErrorForMemberName("Items[1].SimpleA.BoolC", validationResults);
}

private readonly EnumerableExample _multipleFailureModel = new EnumerableExample
{
Name = "Passes all",
Name = "Multiple failures",
Age = 75,
Items = new List<ItemExample>
{
new ItemExample
{
Name = "Child 3E",
Name = "Child 0E",
SimpleA = new SimpleExample
{
IntegerA = 65,
StringB = "child-3E-stringB",
StringB = "child-0E-stringB",
BoolC = true,
ExampleEnumD = ExampleEnum.ValueB
}
Expand Down Expand Up @@ -207,22 +206,22 @@ public void Fails_on_Items_Child2_SimpleA_BoolC()
{
new ItemExample
{
Name = "Child 1L",
Name = "Child 0L",
SimpleA = new SimpleExample
{
IntegerA = 123,
StringB = "child-1L-stringB",
StringB = "child-0L-stringB",
BoolC = true,
ExampleEnumD = ExampleEnum.ValueC
}
},
new ItemExample
{
Name = "Child 3L",
Name = "Child 1L",
SimpleA = new SimpleExample
{
IntegerA = 13,
StringB = "child-3L-stringB",
StringB = "child-1L-stringB",
BoolC = true,
ExampleEnumD = null
}
Expand All @@ -243,11 +242,11 @@ public void Fails_on_Items_Child2_SimpleA_BoolC()
{
new ItemExample
{
Name = "Child 1C",
Name = "Child 0C",
SimpleA = new SimpleExample
{
IntegerA = null,
StringB = "child-1C-stringB",
StringB = "child-0C-stringB",
BoolC = true,
ExampleEnumD = null
}
Expand All @@ -258,27 +257,61 @@ public void Fails_on_Items_Child2_SimpleA_BoolC()
SimpleA = new SimpleExample
{
IntegerA = null,
StringB = "child-2C-string-abc",
StringB = "child-1C-string-abc",
BoolC = false,
ExampleEnumD = ExampleEnum.ValueA
}
},
new ItemExample
{
Name = "Child 2C",
SimpleA = new SimpleExample
{
IntegerA = 250,
StringB = "child-2C-stringB",
BoolC = null,
ExampleEnumD = null
}
},
new ItemExample
{
Name = "Child 3C",
SimpleA = new SimpleExample
{
IntegerA = null,
StringB = null,
BoolC = true,
ExampleEnumD = ExampleEnum.ValueA
}
},
}
};

[Theory]
[InlineData("Items.SimpleA.BoolC")]
[InlineData("ItemsCollection.Name")]
[InlineData("ItemsCollection.SimpleA.ExampleEnumD")]
[InlineData("ItemsCollection.SimpleA.IntegerA")]
[InlineData("ItemsList.SimpleA.ExampleEnumD")]
public void Multiple_failures(string expectedMemberName)
[InlineData("Items[1].SimpleA.BoolC")]
[InlineData("ItemsCollection[1].Name")]
[InlineData("ItemsCollection[0].SimpleA.ExampleEnumD")]
[InlineData("ItemsCollection[1].SimpleA.IntegerA")]
[InlineData("ItemsCollection[2].SimpleA.BoolC")]
[InlineData("ItemsCollection[2].SimpleA.ExampleEnumD")]
[InlineData("ItemsCollection[3].SimpleA.IntegerA")]
[InlineData("ItemsCollection[3].SimpleA.StringB")]
[InlineData("ItemsList[1].SimpleA.ExampleEnumD")]
public void Multiple_failures_contains_expected_memberName(string expectedMemberName)
{
var validationResults = new List<ValidationResult>();
var result = _sut.TryValidateObjectRecursive(_multipleFailureModel, validationResults);

Assert.False(result);
Assert.NotEmpty(validationResults);
AssertThereIsAnErrorForMemberName(expectedMemberName, validationResults);
}

private void AssertThereIsAnErrorForMemberName(
string expectedMemberName,
IEnumerable<ValidationResult> validationResults
)
{
var memberNames = validationResults
.SelectMany(x => x.MemberNames)
.OrderBy(x => x)
Expand Down

0 comments on commit 640003e

Please sign in to comment.