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
8 changes: 4 additions & 4 deletions Source/aweXpect.Core/Core/MemberAccessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ private MemberAccessor(Func<TSource, TTarget> accessor, string name) :
/// <summary>
/// Creates a member accessor from the given <paramref name="expression" />.
/// </summary>
public static MemberAccessor<TSource, TTarget?> FromExpression(
Expression<Func<TSource, TTarget?>> expression)
public static MemberAccessor<TSource, TTarget> FromExpression(
Expression<Func<TSource, TTarget>> expression)
{
Func<TSource, TTarget?> compiled = expression.Compile();
return new MemberAccessor<TSource, TTarget?>(
Func<TSource, TTarget> compiled = expression.Compile();
return new MemberAccessor<TSource, TTarget>(
v => compiled(v),
$"{ExpressionHelpers.GetMemberPath(expression)} ");
}
Expand Down
4 changes: 1 addition & 3 deletions Source/aweXpect.Core/Core/Nodes/WhichNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,7 @@ public override async Task<ConstraintResult> IsMetBy<TValue>(

private bool Equals(WhichNode<TSource, TMember> other) =>
_parent?.Equals(other._parent) != false &&
_inner?.Equals(other._inner) != false &&
_memberAccessor?.ToString()?.Equals(other._memberAccessor?.ToString()) != false &&
_asyncMemberAccessor?.ToString()?.Equals(other._asyncMemberAccessor?.ToString()) != false;
_inner?.Equals(other._inner) != false;

/// <inheritdoc cref="object.GetHashCode()" />
public override int GetHashCode() => _parent?.GetHashCode() ?? 17;
Expand Down
196 changes: 194 additions & 2 deletions Tests/aweXpect.Core.Tests/Core/Nodes/WhichNodeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,34 @@ namespace aweXpect.Core.Tests.Core.Nodes;

public sealed class WhichNodeTests
{
[Fact]
public async Task AddAsyncMapping_WithInnerNode_ShouldAddMappingToInnerNode()
{
MemberAccessor<string, Task<int>> memberAccessor =
MemberAccessor<string, Task<int>>.FromExpression(s => Task.FromResult(s.Length));
DummyNode innerNode = new("", () => new DummyConstraintResult<string?>(Outcome.Success, "inner", ""));
DummyNode node1 = new("", () => new DummyConstraintResult<string?>(Outcome.Success, "1", ""));
WhichNode<string, int> whichNode = new(node1, s => s.Length);
whichNode.AddNode(innerNode);

whichNode.AddAsyncMapping(memberAccessor);

await That(innerNode.MappingMemberAccessor).IsSameAs(memberAccessor);
}

[Fact]
public async Task AddAsyncMapping_WithoutInnerNode_ShouldNotThrow()
{
MemberAccessor<string, Task<int>> memberAccessor =
MemberAccessor<string, Task<int>>.FromExpression(s => Task.FromResult(s.Length));
DummyNode node1 = new("", () => new DummyConstraintResult<string?>(Outcome.Success, "1", ""));
WhichNode<string, int> whichNode = new(node1, s => s.Length);

void Act() => whichNode.AddMapping(memberAccessor);
Comment thread
vbreuss marked this conversation as resolved.
Outdated

await That(Act).DoesNotThrow();
}

[Fact]
public async Task AddConstraint_WithoutInnerNode_ShouldNotThrow()
{
Expand All @@ -21,17 +49,171 @@ void Act() => whichNode.AddConstraint(
await That(Act).DoesNotThrow();
}

[Fact]
public async Task AddMapping_WithInnerNode_ShouldAddMappingToInnerNode()
{
MemberAccessor<string, int> memberAccessor = MemberAccessor<string, int>.FromExpression(s => s.Length);
DummyNode innerNode = new("", () => new DummyConstraintResult<string?>(Outcome.Success, "inner", ""));
DummyNode node1 = new("", () => new DummyConstraintResult<string?>(Outcome.Success, "1", ""));
WhichNode<string, int> whichNode = new(node1, s => s.Length);
whichNode.AddNode(innerNode);

whichNode.AddMapping(memberAccessor);

await That(innerNode.MappingMemberAccessor).IsSameAs(memberAccessor);
}

[Fact]
public async Task AddMapping_WithoutInnerNode_ShouldNotThrow()
{
MemberAccessor<string, int> memberAccessor = MemberAccessor<string, int>.FromExpression(s => s.Length);
DummyNode node1 = new("", () => new DummyConstraintResult<string?>(Outcome.Success, "1", ""));
WhichNode<string, int> whichNode = new(node1, s => s.Length);

void Act() => whichNode.AddMapping(MemberAccessor<string, int>.FromExpression(s => s.Length));
void Act() => whichNode.AddMapping(memberAccessor);

await That(Act).DoesNotThrow();
}

[Fact]
public async Task AppendExpectation_WithInnerNode_ShouldAppendSeparatorAndInnerExpectation()
{
DummyNode innerNode = new("inner-node", () => new DummyConstraintResult<string?>(Outcome.Success, "inner", ""));
WhichNode<string, int> whichNode = new(null, s => s.Length, "foo-separator ");
whichNode.AddNode(innerNode);
StringBuilder sb = new();

whichNode.AppendExpectation(sb);

await That(sb.ToString()).IsEqualTo("foo-separator inner-node");
}

[Fact]
public async Task AppendExpectation_WithoutInnerNode_ShouldAppendSeparator()
{
WhichNode<string, int> whichNode = new(null, s => s.Length, "foo-separator");
StringBuilder sb = new();

whichNode.AppendExpectation(sb);

await That(sb.ToString()).IsEqualTo("foo-separator");
}

[Fact]
public async Task AppendExpectation_WithoutSeparator_WithInnerNode_ShouldOnlyAppendInnerExpectation()
{
DummyNode innerNode = new("inner-node", () => new DummyConstraintResult<string?>(Outcome.Success, "inner", ""));
WhichNode<string, int> whichNode = new(null, s => s.Length);
whichNode.AddNode(innerNode);
StringBuilder sb = new();

whichNode.AppendExpectation(sb);

await That(sb.ToString()).IsEqualTo("inner-node");
}

[Theory]
[InlineData(Outcome.Success, Outcome.Success, Outcome.Failure)]
[InlineData(Outcome.Failure, Outcome.Success, Outcome.Success)]
[InlineData(Outcome.Success, Outcome.Failure, Outcome.Success)]
[InlineData(Outcome.Failure, Outcome.Failure, Outcome.Success)]
[InlineData(Outcome.Failure, Outcome.Undecided, Outcome.Success)]
[InlineData(Outcome.Undecided, Outcome.Failure, Outcome.Success)]
[InlineData(Outcome.Undecided, Outcome.Undecided, Outcome.Undecided)]
public async Task CombinedResult_ShouldBeNegatable(Outcome node1, Outcome node2, Outcome expectedOutcome)
{
WhichNode<string, int> whichNode = new(new DummyNode("", () => new DummyConstraintResult(node1)),
s => s.Length);
whichNode.AddNode(new ExpectationNode());
whichNode.AddConstraint(new DummyConstraint("", () => new DummyConstraintResult(node2)));

ConstraintResult result = await whichNode.IsMetBy("", null!, CancellationToken.None);
result.Negate();

await That(result.Outcome).IsEqualTo(expectedOutcome);
}

[Fact]
public async Task Equals_IfInnerAreDifferent_ShouldBeFalse()
{
DummyNode node1 = new("", () => new DummyConstraintResult<string?>(Outcome.Success, "1", ""));
DummyNode node2 = new("", () => new DummyConstraintResult<string?>(Outcome.Success, "2", ""));
WhichNode<string, int> whichNode1 = new(null, s => s.Length);
whichNode1.AddNode(node1);
WhichNode<string, int> whichNode2 = new(null, s => s.Length);
whichNode2.AddNode(node2);

bool result = whichNode1.Equals(whichNode2);

await That(result).IsFalse();
}

[Fact]
public async Task Equals_IfInnerAreSame_ShouldBeTrue()
{
DummyNode node1 = new("", () => new DummyConstraintResult<string?>(Outcome.Success, "1", ""));
WhichNode<string, int> whichNode1 = new(null, s => s.Length);
whichNode1.AddNode(node1);
WhichNode<string, int> whichNode2 = new(null, s => s.Length);
whichNode2.AddNode(node1);

bool result = whichNode1.Equals(whichNode2);

await That(result).IsTrue();
await That(whichNode1.GetHashCode()).IsEqualTo(whichNode2.GetHashCode());
}

[Fact]
public async Task Equals_IfParentsAreBothNull_ShouldBeTrue()
{
WhichNode<string, int> whichNode1 = new(null, s => s.Length);
WhichNode<string, int> whichNode2 = new(null, s => s.Length);

bool result = whichNode1.Equals(whichNode2);

await That(result).IsTrue();
}

[Fact]
public async Task Equals_IfParentsAreDifferent_ShouldBeFalse()
{
DummyNode node1 = new("", () => new DummyConstraintResult<string?>(Outcome.Success, "1", ""));
DummyNode node2 = new("", () => new DummyConstraintResult<string?>(Outcome.Success, "2", ""));
WhichNode<string, int> whichNode1 = new(node1, s => s.Length);
WhichNode<string, int> whichNode2 = new(node2, s => s.Length);

bool result = whichNode1.Equals(whichNode2);

await That(result).IsFalse();
await That(whichNode1.GetHashCode()).IsNotEqualTo(whichNode2.GetHashCode());
}

[Fact]
public async Task Equals_IfParentsAreSame_ShouldBeTrue()
{
DummyNode node1 = new("", () => new DummyConstraintResult<string?>(Outcome.Success, "1", ""));
WhichNode<string, int> whichNode1 = new(node1, s => s.Length);
WhichNode<string, int> whichNode2 = new(node1, s => s.Length);

bool result = whichNode1.Equals(whichNode2);

await That(result).IsTrue();
await That(whichNode1.GetHashCode()).IsEqualTo(whichNode2.GetHashCode());
}

[Fact]
public async Task Equals_IfTypesAreDifferent_ShouldBeFalse()
{
DummyNode node1 = new("", () => new DummyConstraintResult<string?>(Outcome.Success, "1", ""));
DummyNode node2 = new("", () => new DummyConstraintResult<string?>(Outcome.Success, "2", ""));
WhichNode<string, int> whichNode1 = new(node1, s => s.Length);
object whichNode2 = new WhichNode<string, string>(node2, s => s.Substring(0, 1));

bool result = whichNode1.Equals(whichNode2);

await That(result).IsFalse();
}

[Fact]
public async Task GetResult_WhenBothFailed_ShouldUseOnlyFirst()
{
Expand Down Expand Up @@ -106,7 +288,6 @@ public async Task IsMetBy_WhenTypeDoesNotMatch_ShouldThrowInvalidOperationExcept
whichNode.AddNode(new ExpectationNode());
whichNode.AddConstraint(new DummyConstraint("c2",
() => new DummyConstraintResult<int>(Outcome.Success, 4, "e2")));
StringBuilder sb = new();

async Task Act()
=> await whichNode.IsMetBy(DateTime.Now, null!, CancellationToken.None);
Expand Down Expand Up @@ -166,6 +347,17 @@ public async Task Outcome_ShouldBeExpected(Outcome node1, Outcome node2, Outcome
await That(result.Outcome).IsEqualTo(expectedOutcome);
}

[Fact]
public async Task SetReason_InnerIsNull_ShouldNotThrow()
{
DummyNode node1 = new("", () => new DummyConstraintResult<string?>(Outcome.Success, "1", "e1"));
WhichNode<string, int> whichNode = new(node1, s => s.Length);

void Act() => whichNode.SetReason(new BecauseReason("bc"));

await That(Act).DoesNotThrow();
}

[Fact]
public async Task SetReason_ShouldBeForwardedToInnerNode()
{
Expand Down
16 changes: 12 additions & 4 deletions Tests/aweXpect.Core.Tests/TestHelpers/DummyNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,30 @@ namespace aweXpect.Core.Tests.TestHelpers;

internal class DummyNode(string name, Func<ConstraintResult>? result = null) : Node
{
public MemberAccessor? MappingMemberAccessor { get; private set; }

public override void AddConstraint(IConstraint constraint)
=> throw new NotSupportedException();

public override Node? AddMapping<TValue, TTarget>(
public override Node AddMapping<TValue, TTarget>(
MemberAccessor<TValue, TTarget> memberAccessor,
Action<MemberAccessor, StringBuilder>? expectationTextGenerator = null)
where TValue : default
where TTarget : default
=> throw new NotSupportedException();
{
MappingMemberAccessor = memberAccessor;
return this;
}

public override Node? AddAsyncMapping<TValue, TTarget>(
public override Node AddAsyncMapping<TValue, TTarget>(
MemberAccessor<TValue, Task<TTarget>> memberAccessor,
Action<MemberAccessor, StringBuilder>? expectationTextGenerator = null)
where TValue : default
where TTarget : default
=> throw new NotSupportedException();
{
MappingMemberAccessor = memberAccessor;
return this;
}

public override void AddNode(Node node, string? separator = null)
=> throw new NotSupportedException();
Expand Down
Loading