Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
44 changes: 44 additions & 0 deletions TUnit.Assertions.Tests/Bugs/Issue5613Tests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
namespace TUnit.Assertions.Tests.Bugs;

/// <summary>
/// Regression tests for GitHub issue #5613 findings #1 and #3:
/// Assert.That(Task task) returns AsyncDelegateAssertion which implements both
/// IAssertionSource&lt;object?&gt; and IAssertionSource&lt;Task&gt;. This ambiguity breaks
/// generic inference on IsNotNull / IsNull / IsSameReferenceAs / IsNotSameReferenceAs
/// (CS0411) and produces a misleading CS1929 pointing at JsonElement.
///
/// Fix: instance methods on AsyncDelegateAssertion that forward to the
/// IAssertionSource&lt;Task&gt; interface.
/// </summary>
public class Issue5613Tests
{
[Test]
public async Task IsNotNull_On_Task_Reference_Compiles_And_Passes()
{
Task t = Task.CompletedTask;
await Assert.That(t).IsNotNull();
}

[Test]
public async Task IsNull_On_Null_Task_Reference_Compiles_And_Passes()
{
Task? t = null;
await Assert.That(t!).IsNull();
}

[Test]
public async Task IsSameReferenceAs_On_Task_Compiles_Without_Generic_Annotation()
{
Task a = Task.CompletedTask;
Task b = a;
await Assert.That(a).IsSameReferenceAs(b);
}

[Test]
public async Task IsNotSameReferenceAs_On_Task_Compiles_Without_Generic_Annotation()
{
Task a = Task.CompletedTask;
Task b = Task.FromResult(true);
await Assert.That(a).IsNotSameReferenceAs(b);
}
}
40 changes: 40 additions & 0 deletions TUnit.Assertions/Sources/AsyncDelegateAssertion.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Runtime.CompilerServices;
using System.Text;
using TUnit.Assertions.Conditions;
using TUnit.Assertions.Core;
Expand Down Expand Up @@ -55,6 +56,45 @@ public AsyncDelegateAssertion(Func<Task> action, string? expression)
// Forwarding methods for Task state assertions
// These allow calling task assertions directly on AsyncDelegateAssertion without type inference issues

/// <summary>
/// Asserts that the task reference is not null.
/// Without this instance method, <c>Assert.That(task).IsNotNull()</c> would fail generic inference
/// because <see cref="AsyncDelegateAssertion"/> implements both <c>IAssertionSource&lt;object?&gt;</c>
/// and <c>IAssertionSource&lt;Task&gt;</c>.
/// </summary>
public NotNullAssertion<Task> IsNotNull()
{
return ((IAssertionSource<Task>)this).IsNotNull();
}

/// <summary>
/// Asserts that the task reference is null.
/// </summary>
public TValue_IsNull_Assertion<Task> IsNull()
{
return ((IAssertionSource<Task>)this).IsNull();
}

/// <summary>
/// Asserts that the task is the same reference as <paramref name="expected"/>.
/// </summary>
public TValue_IsSameReferenceAs_Object_Assertion<Task> IsSameReferenceAs(
object? expected,
[CallerArgumentExpression(nameof(expected))] string? expectedExpression = null)
{
return ((IAssertionSource<Task>)this).IsSameReferenceAs(expected, expectedExpression);
}

/// <summary>
/// Asserts that the task is not the same reference as <paramref name="expected"/>.
/// </summary>
public TValue_IsNotSameReferenceAs_Object_Assertion<Task> IsNotSameReferenceAs(
object? expected,
[CallerArgumentExpression(nameof(expected))] string? expectedExpression = null)
{
return ((IAssertionSource<Task>)this).IsNotSameReferenceAs(expected, expectedExpression);
}

/// <summary>
/// Asserts that the task is completed.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6063,7 +6063,11 @@ namespace .Sources
public .<.> IsNotCompleted() { }
public .<.> IsNotCompletedSuccessfully() { }
public .<.> IsNotFaulted() { }
public .<.> IsNotNull() { }
public ._IsNotSameReferenceAs_Object_Assertion<.> IsNotSameReferenceAs(object? expected, [.("expected")] string? expectedExpression = null) { }
public .<object?, TExpected> IsNotTypeOf<TExpected>() { }
public ._IsNull_Assertion<.> IsNull() { }
public ._IsSameReferenceAs_Object_Assertion<.> IsSameReferenceAs(object? expected, [.("expected")] string? expectedExpression = null) { }
public .<object?, TExpected> IsTypeOf<TExpected>() { }
public .<?> Throws( exceptionType) { }
public .<TException> Throws<TException>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5997,7 +5997,11 @@ namespace .Sources
public .<.> IsNotCompleted() { }
public .<.> IsNotCompletedSuccessfully() { }
public .<.> IsNotFaulted() { }
public .<.> IsNotNull() { }
public ._IsNotSameReferenceAs_Object_Assertion<.> IsNotSameReferenceAs(object? expected, [.("expected")] string? expectedExpression = null) { }
public .<object?, TExpected> IsNotTypeOf<TExpected>() { }
public ._IsNull_Assertion<.> IsNull() { }
public ._IsSameReferenceAs_Object_Assertion<.> IsSameReferenceAs(object? expected, [.("expected")] string? expectedExpression = null) { }
public .<object?, TExpected> IsTypeOf<TExpected>() { }
public .<?> Throws( exceptionType) { }
public .<TException> Throws<TException>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6063,7 +6063,11 @@ namespace .Sources
public .<.> IsNotCompleted() { }
public .<.> IsNotCompletedSuccessfully() { }
public .<.> IsNotFaulted() { }
public .<.> IsNotNull() { }
public ._IsNotSameReferenceAs_Object_Assertion<.> IsNotSameReferenceAs(object? expected, [.("expected")] string? expectedExpression = null) { }
public .<object?, TExpected> IsNotTypeOf<TExpected>() { }
public ._IsNull_Assertion<.> IsNull() { }
public ._IsSameReferenceAs_Object_Assertion<.> IsSameReferenceAs(object? expected, [.("expected")] string? expectedExpression = null) { }
public .<object?, TExpected> IsTypeOf<TExpected>() { }
public .<?> Throws( exceptionType) { }
public .<TException> Throws<TException>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5240,7 +5240,11 @@ namespace .Sources
public .<.> IsNotCanceled() { }
public .<.> IsNotCompleted() { }
public .<.> IsNotFaulted() { }
public .<.> IsNotNull() { }
public ._IsNotSameReferenceAs_Object_Assertion<.> IsNotSameReferenceAs(object? expected, [.("expected")] string? expectedExpression = null) { }
public .<object?, TExpected> IsNotTypeOf<TExpected>() { }
public ._IsNull_Assertion<.> IsNull() { }
public ._IsSameReferenceAs_Object_Assertion<.> IsSameReferenceAs(object? expected, [.("expected")] string? expectedExpression = null) { }
public .<object?, TExpected> IsTypeOf<TExpected>() { }
public .<?> Throws( exceptionType) { }
public .<TException> Throws<TException>()
Expand Down
Loading