Skip to content

Commit b8d4f5d

Browse files
http: contain exactly correctly marks nodes (#1595)
1 parent 141797b commit b8d4f5d

7 files changed

Lines changed: 86 additions & 61 deletions

File tree

webtau-core-groovy/src/test/groovy/org/testingisdocumenting/webtau/expectation/equality/AnyValueMatcherTest.groovy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,6 @@ class AnyValueMatcherTest {
3333

3434
code {
3535
value.shouldNotBe anyValue
36-
} should throwException("anyValue matches every value")
36+
} should throwException(contain("anyValue matches every value"))
3737
}
3838
}

webtau-core/src/main/java/org/testingisdocumenting/webtau/data/datanode/DataNodeIdParsedPathPart.java

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,31 +16,11 @@
1616

1717
package org.testingisdocumenting.webtau.data.datanode;
1818

19-
public class DataNodeIdParsedPathPart {
19+
public record DataNodeIdParsedPathPart(
20+
DataNodeIdParsedPathPart.PartType type, Integer idx,
21+
String childName) {
2022
public enum PartType {
2123
PEER,
2224
CHILD
2325
}
24-
25-
private final PartType type;
26-
private final Integer idx;
27-
private final String childName;
28-
29-
public DataNodeIdParsedPathPart(PartType type, Integer idx, String childName) {
30-
this.type = type;
31-
this.idx = idx;
32-
this.childName = childName;
33-
}
34-
35-
public PartType getType() {
36-
return type;
37-
}
38-
39-
public Integer getIdx() {
40-
return idx;
41-
}
42-
43-
public String getChildName() {
44-
return childName;
45-
}
4626
}

webtau-core/src/main/java/org/testingisdocumenting/webtau/data/datanode/ValueExtractorByPath.java

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,13 @@ public static <R> R extractFromMapOrList(Object mapOrList, String path) {
2929

3030
List<DataNodeIdParsedPathPart> parts = DataNodeIdPathParser.parse(path);
3131
for (DataNodeIdParsedPathPart part : parts) {
32-
switch (part.getType()) {
32+
switch (part.type()) {
3333
case PEER:
34-
if (!(result instanceof List)) {
34+
if (!(result instanceof List<?> list)) {
3535
return null;
3636
}
3737

38-
List<?> list = (List<?>) result;
39-
40-
int idx = part.getIdx();
38+
int idx = part.idx();
4139
if (idx < 0) {
4240
idx = list.size() + idx;
4341
}
@@ -53,7 +51,7 @@ public static <R> R extractFromMapOrList(Object mapOrList, String path) {
5351
return null;
5452
}
5553

56-
result = ((Map<String, ?>) result).get(part.getChildName());
54+
result = ((Map<String, ?>) result).get(part.childName());
5755
break;
5856
}
5957
}
@@ -66,14 +64,10 @@ public static DataNode extractFromDataNode(DataNode dataNode, String path) {
6664

6765
List<DataNodeIdParsedPathPart> parts = DataNodeIdPathParser.parse(path);
6866
for (DataNodeIdParsedPathPart part : parts) {
69-
switch (part.getType()) {
70-
case PEER:
71-
result = result.get(part.getIdx());
72-
break;
73-
case CHILD:
74-
result = result.get(part.getChildName());
75-
break;
76-
}
67+
result = switch (part.type()) {
68+
case PEER -> result.get(part.idx());
69+
case CHILD -> result.get(part.childName());
70+
};
7771
}
7872

7973
return result;

webtau-core/src/main/java/org/testingisdocumenting/webtau/data/traceable/TraceableValue.java

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ public void updateCheckLevel(CheckLevel newCheckLevel) {
6363
}
6464
}
6565

66+
public void forceCheckLevel(CheckLevel newCheckLevel) {
67+
checkLevel = newCheckLevel;
68+
}
69+
6670
public CheckLevel getCheckLevel() {
6771
return checkLevel;
6872
}
@@ -122,30 +126,20 @@ private Object convertToStringForPrint(Object value) {
122126
}
123127

124128
private String printSurroundWith() {
125-
switch (getCheckLevel()) {
126-
case FuzzyFailed:
127-
case ExplicitFailed:
128-
return "**";
129-
case ExplicitPassed:
130-
return "__";
131-
case FuzzyPassed:
132-
return "~~";
133-
default:
134-
return "";
135-
}
129+
return switch (getCheckLevel()) {
130+
case FuzzyFailed, ExplicitFailed -> "**";
131+
case ExplicitPassed -> "__";
132+
case FuzzyPassed -> "~~";
133+
default -> "";
134+
};
136135
}
137136
private Object[] valuePrintStyle() {
138-
switch (getCheckLevel()) {
139-
case FuzzyFailed:
140-
case ExplicitFailed:
141-
return FAIL_STYLE;
142-
case FuzzyPassed:
143-
case ExplicitPassed:
144-
return PASS_STYLE;
145-
default:
146-
return new Color[]{value == null ?
147-
UNKNOWN_COLOR :
148-
TypeUtils.isString(value) ? STRING_COLOR : NUMBER_COLOR};
149-
}
137+
return switch (getCheckLevel()) {
138+
case FuzzyFailed, ExplicitFailed -> FAIL_STYLE;
139+
case FuzzyPassed, ExplicitPassed -> PASS_STYLE;
140+
default -> new Color[]{value == null ?
141+
UNKNOWN_COLOR :
142+
TypeUtils.isString(value) ? STRING_COLOR : NUMBER_COLOR};
143+
};
150144
}
151145
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* Fix: [containExactly](matchers/contain-exactly) in [HTTP](HTTP/introduction) correctly marks passed and failed nodes in the output

webtau-http-groovy/src/test/groovy/org/testingisdocumenting/webtau/http/HttpGroovyTest.groovy

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,6 +1047,45 @@ class HttpGroovyTest extends HttpTestBase {
10471047
http.doc.capture("end-point-list-contain-exactly-matchers")
10481048
}
10491049

1050+
@Test
1051+
void "contain exactly objects with single failed"() {
1052+
code {
1053+
http.get("/end-point-large-list") {
1054+
body.should containExactly(
1055+
[id: "id3", k1: "v31", k2: "v32"],
1056+
[id: "id1", k1: "v11", k2: "v12"],
1057+
[id: "id4", k1: "v41", k2: "v42_"],
1058+
[id: "id2", k1: "v21", k2: "v22"],
1059+
)
1060+
}
1061+
} should throwException(AssertionError)
1062+
1063+
http.doc.capture("end-point-large-list-contain-exactly-matchers-single-failed")
1064+
1065+
def body = http.lastValidationResult.bodyNode
1066+
1067+
def isPassed = {idx, key ->
1068+
body.get(idx).get(key).traceableValue.checkLevel.should == CheckLevel.ExplicitPassed
1069+
}
1070+
1071+
def isFailed = {idx, key ->
1072+
body.get(idx).get(key).traceableValue.checkLevel.should == CheckLevel.ExplicitFailed
1073+
}
1074+
1075+
isPassed(0, "id")
1076+
isPassed(0, "k1")
1077+
isPassed(0, "k2")
1078+
isPassed(1, "id")
1079+
isPassed(1, "k1")
1080+
isPassed(1, "k2")
1081+
isPassed(2, "id")
1082+
isPassed(2, "k1")
1083+
isPassed(2, "k2")
1084+
isPassed(3, "id")
1085+
isPassed(3, "k1")
1086+
isFailed(3, "k2")
1087+
}
1088+
10501089
@Test
10511090
void "contain containing all matcher"() {
10521091
http.get("/prices") {

webtau-http/src/main/java/org/testingisdocumenting/webtau/http/Http.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1066,8 +1066,17 @@ private <R> R validateAndRecord(HttpValidationResult validationResult,
10661066
@Override
10671067
public Flow onValueMismatch(ValueMatcher valueMatcher, ValuePath actualPath, Object actualValue, TokenizedMessage message) {
10681068
validationResult.addMismatch(new ValuePathMessage(actualPath, () -> message).buildFullMessage());
1069+
1070+
// some matchers would try multiple times to match values, and they may be incorrectly marked as failed
1071+
// we force override all the matched paths from the final matcher result
1072+
reApplyValuePassBasedOnMatcherPaths(body, valueMatcher.matchedPaths());
10691073
return ExpectationHandler.Flow.PassToNext;
10701074
}
1075+
1076+
@Override
1077+
public void onValueMatch(ValueMatcher valueMatcher, ValuePath actualPath, Object actualValue) {
1078+
reApplyValuePassBasedOnMatcherPaths(body, valueMatcher.matchedPaths());
1079+
}
10711080
};
10721081

10731082
// 1. validate using user provided validation block
@@ -1180,6 +1189,14 @@ private Integer defaultExpectedStatusCodeByRequest(HttpValidationResult validati
11801189
};
11811190
}
11821191

1192+
private static void reApplyValuePassBasedOnMatcherPaths(DataNode node, Set<ValuePath> matchedPaths) {
1193+
for (ValuePath matchedPath : matchedPaths) {
1194+
String matchedPathWithoutBodyPrefix = matchedPath.getPath().substring("body".length());
1195+
DataNode dataNode = ValueExtractorByPath.extractFromDataNode(node, matchedPathWithoutBodyPrefix);
1196+
dataNode.getTraceableValue().forceCheckLevel(CheckLevel.ExplicitPassed);
1197+
}
1198+
}
1199+
11831200
private HttpResponse request(String method, String fullUrl,
11841201
HttpHeader requestHeader,
11851202
HttpRequestBody requestBody) {

0 commit comments

Comments
 (0)