• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

fslev / jtest-utils / #945

05 May 2026 12:19PM UTC coverage: 93.566% (-0.1%) from 93.691%
#945

push

web-flow
Big refactoring (#266)

* Bump version to 7.0-SNAPSHOT and Java baseline to 17

Raise the main-code Java release from 8 to 17, drop the now-redundant
testRelease 11, and bump the in-development version. Unlocks records,
sealed types, switch expressions, and pattern matching for the upcoming
refactor pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Remove deprecated Polling API

The Polling class and the polling-based ObjectMatcher overloads were
deprecated in 5.14 (Dec 2023) with a recommendation to use Awaitility.
Drop them now as part of the 7.0 cut. Removes Polling, PollingTimeoutException,
the five deprecated polling overloads in ObjectMatcher, the private polling
helper, all polling-coupled tests, and the polling sections of the README
and _config.yml description.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Convert PlainHttpResponse to a record

Reduces ~110 lines of class boilerplate to a 4-component record while
preserving the public Builder, ParseException, and the custom toString
that downstream tests assert on. The compact constructor takes a
defensive copy of the headers via List.copyOf, replacing the previous
unmodifiableList wrap that didn't actually copy.

Public API break (7.0): accessors getStatus()/getReasonPhrase()/
getEntity()/getHeaders() are now status()/reasonPhrase()/entity()/
headers(). HttpResponseMatcher updated to match.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Refactor CustomXmlDiffEvaluator dispatch with switch + pattern matching

Replace the if-else-if chain over ComparisonType with a single switch,
and use pattern matching for instanceof to remove the redundant casts
on Attr and Text nodes. The expected-node null guard is split out from
the type-based short-circuit cases for clarity.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Use switch + EnumSet for JsonMatcher.jsonCompareModes

Replace the HashSet + ... (continued)

129 of 133 new or added lines in 8 files covered. (96.99%)

2 existing lines in 1 file now uncovered.

509 of 544 relevant lines covered (93.57%)

0.94 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.67
/src/main/java/io/jtest/utils/matcher/HttpResponseMatcher.java
1
package io.jtest.utils.matcher;
2

3

4
import io.jtest.utils.exceptions.InvalidTypeException;
5
import io.jtest.utils.matcher.condition.MatchCondition;
6
import io.jtest.utils.matcher.http.PlainHttpResponse;
7
import org.junit.jupiter.api.AssertionFailureBuilder;
8

9
import java.util.HashMap;
10
import java.util.List;
11
import java.util.Map;
12
import java.util.Set;
13

14
class HttpResponseMatcher extends AbstractObjectMatcher<PlainHttpResponse> {
15

16
    private final Object expectedStatus;
17
    private final String expectedReason;
18
    private final List<Map.Entry<String, Object>> expectedHeaders;
19
    private final Object expectedEntity;
20

21
    public HttpResponseMatcher(String message, PlainHttpResponse expected, PlainHttpResponse actual, Set<MatchCondition> matchConditions) throws InvalidTypeException {
22
        super(message, expected, actual, matchConditions);
1✔
23
        this.expectedStatus = this.expected.status();
1✔
24
        this.expectedReason = this.expected.reasonPhrase();
1✔
25
        this.expectedHeaders = this.expected.headers();
1✔
26
        this.expectedEntity = this.expected.entity();
1✔
27
    }
1✔
28

29
    @Override
30
    protected String negativeMatchMessage() {
31
        return System.lineSeparator() + "HTTP responses match!" + System.lineSeparator();
1✔
32
    }
33

34
    @Override
35
    PlainHttpResponse convert(Object value) {
36
        return (PlainHttpResponse) value;
1✔
37
    }
38

39

40
    @Override
41
    public Map<String, Object> match() {
42
        if (matchConditions.remove(MatchCondition.DO_NOT_MATCH)) {
1✔
43
            try {
44
                positiveMatch();
1✔
45
            } catch (AssertionError e) {
1✔
46
                return new HashMap<>();
1✔
47
            }
1✔
48
            AssertionFailureBuilder.assertionFailure().message(negativeMatchMessage).includeValuesInMessage(false)
1✔
49
                    .expected(expected).actual(actual).buildAndThrow();
×
50
        }
51
        return positiveMatch();
1✔
52
    }
53

54
    public Map<String, Object> positiveMatch() {
55
        Map<String, Object> properties = new HashMap<>();
1✔
56
        Set<MatchCondition> headersConditions = filteredMatchConditions(matchConditions,
1✔
57
                cond -> cond != MatchCondition.JSON_NON_EXTENSIBLE_ARRAY
1✔
58
                        && cond != MatchCondition.JSON_NON_EXTENSIBLE_OBJECT
59
                        && cond != MatchCondition.JSON_STRICT_ORDER_ARRAY);
60
        try {
61
            matchComponent("statuses", expectedStatus, actual.status(),
1✔
62
                    MatchCondition.DO_NOT_MATCH_HTTP_RESPONSE_BY_STATUS, matchConditions,
63
                    (m, e, a, c) -> new StringMatcher(m, e, a, c).match(), properties);
1✔
64
            matchComponent("reasons", expectedReason, actual.reasonPhrase(),
1✔
65
                    MatchCondition.DO_NOT_MATCH_HTTP_RESPONSE_BY_REASON, matchConditions,
66
                    (m, e, a, c) -> new StringMatcher(m, e, a, c).match(), properties);
1✔
67
            matchComponent("headers", expectedHeaders, actual.headers(),
1✔
68
                    MatchCondition.DO_NOT_MATCH_HTTP_RESPONSE_BY_HEADERS, headersConditions,
69
                    (m, e, a, c) -> new JsonMatcher(m, e, a, c).match(), properties);
1✔
70
            matchComponent("bodies", expectedEntity, actual.entity(),
1✔
71
                    MatchCondition.DO_NOT_MATCH_HTTP_RESPONSE_BY_BODY, matchConditions,
72
                    (m, e, a, c) -> new FlowMatcher().match(m, e, a, c), properties);
1✔
73
        } catch (InvalidTypeException e) {
×
74
            throw new RuntimeException(e);
×
75
        }
1✔
76
        return properties;
1✔
77
    }
78

79
    private void matchComponent(String label, Object expected, Object actual,
80
                                MatchCondition negateCondition,
81
                                Set<MatchCondition> conditions,
82
                                ComponentMatcher matcher,
83
                                Map<String, Object> properties) throws InvalidTypeException {
84
        if (expected == null) {
1✔
85
            return;
1✔
86
        }
87
        if (matchConditions.contains(negateCondition)) {
1✔
88
            boolean matched;
89
            try {
90
                matcher.match(null, expected, actual, conditions);
1✔
91
                matched = true;
1✔
92
            } catch (AssertionError ignored) {
1✔
93
                matched = false;
1✔
94
            }
1✔
95
            if (matched) {
1✔
96
                AssertionFailureBuilder.assertionFailure()
1✔
97
                        .message(this.message + System.lineSeparator() + "HTTP Response " + label + " match!" + System.lineSeparator())
1✔
98
                        .expected(expected).actual(actual)
1✔
NEW
99
                        .includeValuesInMessage(false).buildAndThrow();
×
100
            }
101
        } else {
1✔
102
            properties.putAll(matcher.match(
1✔
103
                    "HTTP Response " + label + " do not match!" + System.lineSeparator() + message,
1✔
104
                    expected, actual, conditions));
105
        }
106
    }
1✔
107

108
    @FunctionalInterface
109
    private interface ComponentMatcher {
110
        Map<String, Object> match(String message, Object expected, Object actual, Set<MatchCondition> conditions) throws InvalidTypeException;
111
    }
112
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc