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
25 changes: 21 additions & 4 deletions src/WireMock.Net.Minimal/Matchers/JsonMatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ public class JsonMatcher : IJsonMatcher
/// </summary>
public bool Regex { get; }

/// <summary>
/// Ignore array order when comparing
/// </summary>
public bool IgnoreArrayOrder { get; }

private readonly JToken _valueAsJToken;

/// <summary>
Expand All @@ -40,7 +45,8 @@ public class JsonMatcher : IJsonMatcher
/// <param name="value">The string value to check for equality.</param>
/// <param name="ignoreCase">Ignore the case from the PropertyName and PropertyValue (string only).</param>
/// <param name="regex">Support Regex.</param>
public JsonMatcher(string value, bool ignoreCase = false, bool regex = false) : this(MatchBehaviour.AcceptOnMatch, value, ignoreCase, regex)
/// <param name="ignoreArrayOrder">Ignore array element order when comparing.</param>
public JsonMatcher(string value, bool ignoreCase = false, bool regex = false, bool ignoreArrayOrder = false) : this(MatchBehaviour.AcceptOnMatch, value, ignoreCase, regex, ignoreArrayOrder)
{
}

Expand All @@ -50,7 +56,8 @@ public JsonMatcher(string value, bool ignoreCase = false, bool regex = false) :
/// <param name="value">The object value to check for equality.</param>
/// <param name="ignoreCase">Ignore the case from the PropertyName and PropertyValue (string only).</param>
/// <param name="regex">Support Regex.</param>
public JsonMatcher(object value, bool ignoreCase = false, bool regex = false) : this(MatchBehaviour.AcceptOnMatch, value, ignoreCase, regex)
/// <param name="ignoreArrayOrder">Ignore array element order when comparing.</param>
public JsonMatcher(object value, bool ignoreCase = false, bool regex = false, bool ignoreArrayOrder = false) : this(MatchBehaviour.AcceptOnMatch, value, ignoreCase, regex, ignoreArrayOrder)
{
}

Expand All @@ -61,13 +68,15 @@ public JsonMatcher(object value, bool ignoreCase = false, bool regex = false) :
/// <param name="value">The value to check for equality.</param>
/// <param name="ignoreCase">Ignore the case from the PropertyName and PropertyValue (string only).</param>
/// <param name="regex">Support Regex.</param>
public JsonMatcher(MatchBehaviour matchBehaviour, object value, bool ignoreCase = false, bool regex = false)
/// <param name="ignoreArrayOrder">Ignore array element order when comparing.</param>
public JsonMatcher(MatchBehaviour matchBehaviour, object value, bool ignoreCase = false, bool regex = false, bool ignoreArrayOrder = false)
{
Guard.NotNull(value);

MatchBehaviour = matchBehaviour;
IgnoreCase = ignoreCase;
Regex = regex;
IgnoreArrayOrder = ignoreArrayOrder;

Value = value;
_valueAsJToken = ConvertValueToJToken(value);
Expand Down Expand Up @@ -106,7 +115,8 @@ public virtual string GetCSharpCodeArguments()
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
$"{CSharpFormatter.ConvertToAnonymousObjectDefinition(Value, 3)}, " +
$"{CSharpFormatter.ToCSharpBooleanLiteral(IgnoreCase)}, " +
$"{CSharpFormatter.ToCSharpBooleanLiteral(Regex)}" +
$"{CSharpFormatter.ToCSharpBooleanLiteral(Regex)}, " +
$"{CSharpFormatter.ToCSharpBooleanLiteral(IgnoreArrayOrder)}" +
$")";
}

Expand Down Expand Up @@ -183,6 +193,13 @@ protected virtual bool IsMatch(JToken value, JToken? input)
return false;
}

if (IgnoreArrayOrder)
{
// Sort both arrays by their string representation and compare
valueArray = valueArray.OrderBy(t => t.ToString()).ToArray();
inputArray = inputArray.OrderBy(t => t.ToString()).ToArray();
}

return !valueArray.Where((valueToken, index) => !IsMatch(valueToken, inputArray[index])).Any();

default:
Expand Down
29 changes: 23 additions & 6 deletions src/WireMock.Net.Minimal/Matchers/SystemTextJsonMatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ public class SystemTextJsonMatcher : IJsonMatcher
/// </summary>
public bool Regex { get; }

/// <summary>
/// Ignore array order when comparing
/// </summary>
public bool IgnoreArrayOrder { get; }

private readonly JsonElement _valueAsJsonElement;

/// <summary>
Expand All @@ -43,8 +48,9 @@ public class SystemTextJsonMatcher : IJsonMatcher
/// <param name="value">The string value to check for equality.</param>
/// <param name="ignoreCase">Ignore the case from the PropertyName and PropertyValue (string only).</param>
/// <param name="regex">Support Regex.</param>
public SystemTextJsonMatcher(string value, bool ignoreCase = false, bool regex = false)
: this(MatchBehaviour.AcceptOnMatch, value, ignoreCase, regex)
/// <param name="ignoreArrayOrder">Ignore array element order when comparing.</param>
public SystemTextJsonMatcher(string value, bool ignoreCase = false, bool regex = false, bool ignoreArrayOrder = false)
: this(MatchBehaviour.AcceptOnMatch, value, ignoreCase, regex, ignoreArrayOrder)
{
}

Expand All @@ -54,8 +60,9 @@ public SystemTextJsonMatcher(string value, bool ignoreCase = false, bool regex =
/// <param name="value">The object value to check for equality.</param>
/// <param name="ignoreCase">Ignore the case from the PropertyName and PropertyValue (string only).</param>
/// <param name="regex">Support Regex.</param>
public SystemTextJsonMatcher(object value, bool ignoreCase = false, bool regex = false)
: this(MatchBehaviour.AcceptOnMatch, value, ignoreCase, regex)
/// <param name="ignoreArrayOrder">Ignore array element order when comparing.</param>
public SystemTextJsonMatcher(object value, bool ignoreCase = false, bool regex = false, bool ignoreArrayOrder = false)
: this(MatchBehaviour.AcceptOnMatch, value, ignoreCase, regex, ignoreArrayOrder)
{
}

Expand All @@ -66,13 +73,15 @@ public SystemTextJsonMatcher(object value, bool ignoreCase = false, bool regex =
/// <param name="value">The value to check for equality.</param>
/// <param name="ignoreCase">Ignore the case from the PropertyName and PropertyValue (string only).</param>
/// <param name="regex">Support Regex.</param>
public SystemTextJsonMatcher(MatchBehaviour matchBehaviour, object value, bool ignoreCase = false, bool regex = false)
/// <param name="ignoreArrayOrder">Ignore array element order when comparing.</param>
public SystemTextJsonMatcher(MatchBehaviour matchBehaviour, object value, bool ignoreCase = false, bool regex = false, bool ignoreArrayOrder = false)
{
Guard.NotNull(value);

MatchBehaviour = matchBehaviour;
IgnoreCase = ignoreCase;
Regex = regex;
IgnoreArrayOrder = ignoreArrayOrder;

Value = value;
_valueAsJsonElement = ConvertToJsonElement(value);
Expand Down Expand Up @@ -111,7 +120,8 @@ public virtual string GetCSharpCodeArguments()
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
$"{CSharpFormatter.ConvertToAnonymousObjectDefinition(Value, 3)}, " +
$"{CSharpFormatter.ToCSharpBooleanLiteral(IgnoreCase)}, " +
$"{CSharpFormatter.ToCSharpBooleanLiteral(Regex)}" +
$"{CSharpFormatter.ToCSharpBooleanLiteral(Regex)}, " +
$"{CSharpFormatter.ToCSharpBooleanLiteral(IgnoreArrayOrder)}" +
$")";
}

Expand Down Expand Up @@ -202,6 +212,13 @@ protected virtual bool IsMatch(JsonElement value, JsonElement? input)
return false;
}

if (IgnoreArrayOrder)
{
// Sort both arrays by their string representation and compare
valueArray = valueArray.OrderBy(e => e.GetRawText()).ToArray();
inputArray = inputArray.OrderBy(e => e.GetRawText()).ToArray();
}

return !valueArray.Where((valueToken, index) => !IsMatch(valueToken, inputArray[index])).Any();
}

Expand Down
149 changes: 148 additions & 1 deletion test/WireMock.Net.Tests/Matchers/JsonMatcherTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -523,4 +523,151 @@ public void JsonMatcher_IsMatch_StringAndGuid()
// Assert
Assert.Equal(1.0, score);
}
}

[Fact]
public void JsonMatcher_IsMatch_Array_WithIgnoreArrayOrderTrue_DifferentOrder_ShouldMatch()
{
// Assign
var matcher = new JsonMatcher(new[] { "a", "b", "c" }, ignoreArrayOrder: true);

// Act
var jArray = new JArray
{
"c",
"a",
"b"
};
var score = matcher.IsMatch(jArray).Score;

// Assert
Assert.Equal(1.0, score);
}

[Fact]
public void JsonMatcher_IsMatch_Array_WithIgnoreArrayOrderFalse_DifferentOrder_ShouldNotMatch()
{
// Assign
var matcher = new JsonMatcher(new[] { "a", "b", "c" }, ignoreArrayOrder: false);

// Act
var jArray = new JArray
{
"c",
"a",
"b"
};
var score = matcher.IsMatch(jArray).Score;

// Assert
Assert.Equal(MatchScores.Mismatch, score);
}

[Fact]
public void JsonMatcher_IsMatch_Array_WithIgnoreArrayOrderTrue_SameOrder_ShouldMatch()
{
// Assign
var matcher = new JsonMatcher(new[] { "a", "b", "c" }, ignoreArrayOrder: true);

// Act
var jArray = new JArray
{
"a",
"b",
"c"
};
var score = matcher.IsMatch(jArray).Score;

// Assert
Assert.Equal(1.0, score);
}

[Fact]
public void JsonMatcher_IsMatch_Array_WithIgnoreArrayOrderTrue_DifferentLength_ShouldNotMatch()
{
// Assign
var matcher = new JsonMatcher(new[] { "a", "b", "c" }, ignoreArrayOrder: true);

// Act
var jArray = new JArray
{
"a",
"b"
};
var score = matcher.IsMatch(jArray).Score;

// Assert
Assert.Equal(MatchScores.Mismatch, score);
}

[Fact]
public void JsonMatcher_IsMatch_ObjectWithArray_WithIgnoreArrayOrderTrue_DifferentOrder_ShouldMatch()
{
// Assign
var matcher = new JsonMatcher(new { Items = new[] { "x", "y", "z" } }, ignoreArrayOrder: true);

// Act
var jObject = new JObject
{
{ "Items", new JArray("z", "x", "y") }
};
var score = matcher.IsMatch(jObject).Score;

// Assert
Assert.Equal(1.0, score);
}

[Fact]
public void JsonMatcher_IsMatch_ArrayAsString_WithIgnoreArrayOrderTrue_DifferentOrder_ShouldMatch()
{
// Assign
var matcher = new JsonMatcher("[ \"a\", \"b\", \"c\" ]", ignoreArrayOrder: true);

// Act
var jArray = new JArray
{
"c",
"b",
"a"
};
var score = matcher.IsMatch(jArray).Score;

// Assert
Assert.Equal(1.0, score);
}

[Fact]
public void JsonMatcher_IsMatch_ArrayOfNumbers_WithIgnoreArrayOrderTrue_DifferentOrder_ShouldMatch()
{
// Assign
var matcher = new JsonMatcher(new[] { 1, 2, 3 }, ignoreArrayOrder: true);

// Act
var jArray = new JArray
{
3,
1,
2
};
var score = matcher.IsMatch(jArray).Score;

// Assert
Assert.Equal(1.0, score);
}

[Theory]
[InlineData(MatchBehaviour.AcceptOnMatch, false, false, false)]
[InlineData(MatchBehaviour.AcceptOnMatch, true, false, true)]
[InlineData(MatchBehaviour.RejectOnMatch, true, true, false)]
public void JsonMatcher_GetCSharpCodeArguments_ShouldIncludeAllConstructorArguments(MatchBehaviour matchBehaviour, bool ignoreCase, bool regex, bool ignoreArrayOrder)
{
// Assign
var matcher = new JsonMatcher(matchBehaviour, "{ \"id\": 1 }", ignoreCase, regex, ignoreArrayOrder);

// Act
var result = matcher.GetCSharpCodeArguments();

// Assert
result.Should().StartWith($"new JsonMatcher(WireMock.Matchers.MatchBehaviour.{matchBehaviour},");
result.Should().EndWith($", {ignoreCase.ToString().ToLowerInvariant()}, {regex.ToString().ToLowerInvariant()}, {ignoreArrayOrder.ToString().ToLowerInvariant()})");
}
}
Loading
Loading