Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions TUnit.Assertions.Tests/Bugs/Issue5706Tests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
namespace TUnit.Assertions.Tests.Bugs;

/// <summary>
/// Regression tests for GitHub issue #5706:
/// ItemAt(...).Satisfies(...) should preserve specialised assertion sources for
/// collection-like item values instead of exposing only IAssertionSource&lt;TItem&gt;.
/// </summary>
public class Issue5706Tests
{
[Test]
public async Task List_ItemAt_Satisfies_Preserves_String_Item_Source()
{
IList<string> items = new List<string> { "alpha" };

await Assert.That(items).ItemAt(0).Satisfies(item => item.Contains("pha"));
}

[Test]
public async Task List_ItemAt_Satisfies_Preserves_Collection_Item_Source()
{
IList<IEnumerable<int>> items = new List<IEnumerable<int>> { new[] { 1, 2, 3 } };

await Assert.That(items).ItemAt(0).Satisfies(item => item.Count().IsEqualTo(3));
}

[Test]
public async Task List_ItemAt_Satisfies_Preserves_Dictionary_Item_Source()
{
IList<IDictionary<string, int>> items = new List<IDictionary<string, int>>
{
new Dictionary<string, int> { ["answer"] = 42 }
};

await Assert.That(items).ItemAt(0).Satisfies(item => item.ContainsKey("answer"));
}

[Test]
public async Task List_ItemAt_Satisfies_Preserves_Set_Item_Source()
{
IList<ISet<int>> items = new List<ISet<int>>
{
new HashSet<int> { 1, 2, 3 }
};

await Assert.That(items).ItemAt(0).Satisfies(item => item.IsSupersetOf(new[] { 1, 2 }));
}

[Test]
public async Task ReadOnlyList_ItemAt_Satisfies_Preserves_String_Item_Source()
{
IReadOnlyList<string> items = new List<string> { "alpha" };

await Assert.That(items).ItemAt(0).Satisfies(item => item.Contains("pha"));
}

[Test]
public async Task ReadOnlyList_ItemAt_Satisfies_Preserves_Collection_Item_Source()
{
IReadOnlyList<List<int>> items = new List<List<int>>
{
new() { 1, 2, 3 }
};

await Assert.That(items).ItemAt(0).Satisfies(item => item.Count().IsEqualTo(3));
}

[Test]
public async Task ReadOnlyList_ItemAt_Satisfies_Preserves_Dictionary_Item_Source()
{
IReadOnlyList<Dictionary<string, int>> items = new List<Dictionary<string, int>>
{
new() { ["answer"] = 42 }
};

await Assert.That(items).ItemAt(0).Satisfies(item => item.ContainsKey("answer"));
}

[Test]
public async Task ReadOnlyList_ItemAt_Satisfies_Preserves_Set_Item_Source()
{
IReadOnlyList<HashSet<int>> items = new List<HashSet<int>>
{
new() { 1, 2, 3 }
};

await Assert.That(items).ItemAt(0).Satisfies(item => item.IsSupersetOf(new[] { 1, 2 }));
}
}
20 changes: 17 additions & 3 deletions TUnit.Assertions/Conditions/ListAssertions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ public class ListItemAtSource<TList, TItem> : IAssertionSource<TItem>
private readonly AssertionContext<TList> _listContext;
private readonly int _index;

internal AssertionContext<TList> InternalListContext => _listContext;

internal int InternalIndex => _index;

public AssertionContext<TItem> Context { get; }

public ListItemAtSource(AssertionContext<TList> listContext, int index)
Expand Down Expand Up @@ -420,12 +424,23 @@ public class ListItemAtSatisfiesAssertion<TList, TItem> : ListAssertionBase<TLis
where TList : IList<TItem>
{
private readonly int _index;
private readonly Func<IAssertionSource<TItem>, Assertion<TItem>?> _assertion;
private readonly Func<TItem, int, IAssertion?> _assertion;

public ListItemAtSatisfiesAssertion(
AssertionContext<TList> context,
int index,
Func<IAssertionSource<TItem>, Assertion<TItem>?> assertion)
: this(
context,
index,
(item, itemIndex) => assertion(new ValueAssertion<TItem>(item, $"item[{itemIndex}]")))
{
}

internal ListItemAtSatisfiesAssertion(
AssertionContext<TList> context,
int index,
Func<TItem, int, IAssertion?> assertion)
: base(context)
{
_index = index;
Expand All @@ -451,8 +466,7 @@ protected override async Task<AssertionResult> CheckAsync(EvaluationMetadata<TLi
}

var actualItem = metadata.Value[_index];
var itemSource = new ValueAssertion<TItem>(actualItem, $"item[{_index}]");
var resultingAssertion = _assertion(itemSource);
var resultingAssertion = _assertion(actualItem, _index);

if (resultingAssertion != null)
{
Expand Down
20 changes: 17 additions & 3 deletions TUnit.Assertions/Conditions/ReadOnlyListAssertions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ public class ReadOnlyListItemAtSource<TList, TItem> : IAssertionSource<TItem>
private readonly AssertionContext<TList> _listContext;
private readonly int _index;

internal AssertionContext<TList> InternalListContext => _listContext;

internal int InternalIndex => _index;

public AssertionContext<TItem> Context { get; }

public ReadOnlyListItemAtSource(AssertionContext<TList> listContext, int index)
Expand Down Expand Up @@ -422,12 +426,23 @@ public class ReadOnlyListItemAtSatisfiesAssertion<TList, TItem> : ReadOnlyListAs
where TList : IReadOnlyList<TItem>
{
private readonly int _index;
private readonly Func<IAssertionSource<TItem>, Assertion<TItem>?> _assertion;
private readonly Func<TItem, int, IAssertion?> _assertion;

public ReadOnlyListItemAtSatisfiesAssertion(
AssertionContext<TList> context,
int index,
Func<IAssertionSource<TItem>, Assertion<TItem>?> assertion)
: this(
context,
index,
(item, itemIndex) => assertion(new ValueAssertion<TItem>(item, $"item[{itemIndex}]")))
{
}

internal ReadOnlyListItemAtSatisfiesAssertion(
AssertionContext<TList> context,
int index,
Func<TItem, int, IAssertion?> assertion)
: base(context)
{
_index = index;
Expand All @@ -453,8 +468,7 @@ protected override async Task<AssertionResult> CheckAsync(EvaluationMetadata<TLi
}

var actualItem = metadata.Value[_index];
var itemSource = new ValueAssertion<TItem>(actualItem, $"item[{_index}]");
var resultingAssertion = _assertion(itemSource);
var resultingAssertion = _assertion(actualItem, _index);

if (resultingAssertion != null)
{
Expand Down
Loading
Loading