Skip to content

Commit f55fda1

Browse files
committed
chore: Increase test coverage for BucketsCalculator
1 parent f929577 commit f55fda1

2 files changed

Lines changed: 223 additions & 1 deletion

File tree

src/main/java/dev/dochia/cli/core/util/WordUtils.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,8 @@ private static String replaceUpperTokens(String s) {
392392
// Helper: collapse occurrences of \" ... \" to \"\"
393393
// (works well for messages like: ... parsing \"👩🏾false\" : invalid syntax)
394394
private static String collapseEscapedQuotedSegments(String s) {
395-
int i = 0, n = s.length();
395+
int i = 0;
396+
int n = s.length();
396397
StringBuilder sb = new StringBuilder(n);
397398
while (i < n) {
398399
int open = s.indexOf("\\\"", i);
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
package dev.dochia.cli.core.report;
2+
3+
import dev.dochia.cli.core.model.TestCaseSummary;
4+
import io.quarkus.test.junit.QuarkusTest;
5+
import org.junit.jupiter.api.Test;
6+
7+
import java.util.List;
8+
import java.util.Map;
9+
import java.util.regex.Pattern;
10+
11+
import static org.assertj.core.api.Assertions.assertThat;
12+
13+
@QuarkusTest
14+
class BucketsCalculatorTest {
15+
// Helper to create a TestCaseSummary with minimal fields
16+
private static TestCaseSummary summary(String id, int code, String reason, String body, boolean error, boolean warning, String path) {
17+
TestCaseSummary t = new TestCaseSummary();
18+
setField(t, "id", id);
19+
setField(t, "httpResponseCode", code);
20+
setField(t, "resultReason", reason);
21+
setField(t, "responseBody", body);
22+
setField(t, "path", path);
23+
setField(t, "result", error ? "FAILED" : "PASSED");
24+
setField(t, "switchedResult", false);
25+
setField(t, "playbook", "pb");
26+
setField(t, "scenario", "sc");
27+
setField(t, "resultDetails", "details");
28+
setField(t, "httpMethod", "get");
29+
setField(t, "timeToExecuteInSec", 1.0);
30+
setField(t, "timeToExecuteInMs", 1000L);
31+
// error/warning are not fields, but logic uses getError()/getWarning() - simulate via result string
32+
return new TestCaseSummary() {
33+
@Override
34+
public boolean getError() {
35+
return error;
36+
}
37+
38+
@Override
39+
public boolean getWarning() {
40+
return warning;
41+
}
42+
43+
@Override
44+
public String getId() {
45+
return id;
46+
}
47+
48+
@Override
49+
public int getHttpResponseCode() {
50+
return code;
51+
}
52+
53+
@Override
54+
public String getResultReason() {
55+
return reason;
56+
}
57+
58+
@Override
59+
public String getResponseBody() {
60+
return body;
61+
}
62+
63+
@Override
64+
public String getPath() {
65+
return path;
66+
}
67+
68+
@Override
69+
public String getResult() {
70+
return error ? "FAILED" : "PASSED";
71+
}
72+
73+
@Override
74+
public String getKey() {
75+
return id == null ? "" : id.replace("/", "-");
76+
}
77+
};
78+
}
79+
80+
private static void setField(Object obj, String field, Object value) {
81+
try {
82+
var f = obj.getClass().getDeclaredField(field);
83+
f.setAccessible(true);
84+
f.set(obj, value);
85+
} catch (Exception ignored) {
86+
}
87+
}
88+
89+
@Test
90+
void testEmptyInput() {
91+
List<Map<String, Object>> result = BucketsCalculator.createBuckets(List.of());
92+
assertThat(result).isEmpty();
93+
}
94+
95+
@Test
96+
void testAll2xxFiltered() {
97+
var t1 = summary("1", 200, "reason", "body", true, false, "/a");
98+
var t2 = summary("2", 201, "reason", "body", true, false, "/b");
99+
var t3 = summary("3", 299, "reason", "body", true, false, "/c");
100+
List<Map<String, Object>> result = BucketsCalculator.createBuckets(List.of(t1, t2, t3));
101+
assertThat(result).isEmpty();
102+
}
103+
104+
@Test
105+
void testNon2xxWithoutErrorOrWarningFiltered() {
106+
var t1 = summary("1", 404, "reason", "body", false, false, "/a");
107+
var t2 = summary("2", 500, "reason", "body", false, false, "/b");
108+
List<Map<String, Object>> result = BucketsCalculator.createBuckets(List.of(t1, t2));
109+
assertThat(result).isEmpty();
110+
}
111+
112+
@Test
113+
void testNon2xxWithoutReasonFiltered() {
114+
var t1 = summary("1", 404, null, "body", true, false, "/a");
115+
var t2 = summary("2", 500, "", "body", true, false, "/b");
116+
List<Map<String, Object>> result = BucketsCalculator.createBuckets(List.of(t1, t2));
117+
assertThat(result).isEmpty();
118+
}
119+
120+
@Test
121+
void testBucketsByReason() {
122+
var t1 = summary("1", 404, "reason1", "body1", true, false, "/a");
123+
var t2 = summary("2", 500, "reason2", "body2", true, false, "/b");
124+
var t3 = summary("3", 400, "reason1", "body3", true, false, "/c");
125+
List<Map<String, Object>> result = BucketsCalculator.createBuckets(List.of(t1, t2, t3));
126+
assertThat(result).hasSize(2);
127+
assertThat(result).anySatisfy(map -> assertThat(map.get("resultReason")).isEqualTo("reason1"));
128+
assertThat(result).anySatisfy(map -> assertThat(map.get("resultReason")).isEqualTo("reason2"));
129+
}
130+
131+
@Test
132+
void testBucketsByEmptyResponseBody() {
133+
var t1 = summary("1", 404, "reason", "", true, false, "/a");
134+
var t2 = summary("2", 404, "reason", null, true, false, "/b");
135+
var t3 = summary("3", 404, "reason", " ", true, false, "/c");
136+
List<Map<String, Object>> result = BucketsCalculator.createBuckets(List.of(t1, t2, t3));
137+
assertThat(result).hasSize(1);
138+
var buckets = (List<?>) result.get(0).get("buckets");
139+
assertThat(buckets).hasSize(1);
140+
var bucket = (Map<?, ?>) buckets.get(0);
141+
assertThat(bucket.get("errorMessage")).isEqualTo("<empty response body>");
142+
}
143+
144+
@Test
145+
void testBucketsByExactResponseBody() {
146+
var t1 = summary("1", 404, "reason", "body", true, false, "/a");
147+
var t2 = summary("2", 404, "reason", "body", true, false, "/b");
148+
List<Map<String, Object>> result = BucketsCalculator.createBuckets(List.of(t1, t2));
149+
var buckets = (List<?>) result.get(0).get("buckets");
150+
assertThat(buckets).hasSize(1);
151+
var bucket = (Map<?, ?>) buckets.get(0);
152+
assertThat(bucket.get("errorMessage")).isEqualTo("body");
153+
var paths = (List<?>) bucket.get("paths");
154+
assertThat(paths).hasSize(2);
155+
}
156+
157+
@Test
158+
void testBucketsByNormalizedResponseBody() {
159+
// Use two bodies that normalize to the same string
160+
var t1 = summary("1", 404, "reason", "Error: ID=123", true, false, "/a");
161+
var t2 = summary("2", 404, "reason", "Error: ID=456", true, false, "/b");
162+
List<Map<String, Object>> result = BucketsCalculator.createBuckets(List.of(t1, t2));
163+
var buckets = (List<?>) result.get(0).get("buckets");
164+
assertThat(buckets).hasSize(1);
165+
}
166+
167+
@Test
168+
void testBucketsByJaccardSimilarity() {
169+
// Use two bodies with high Jaccard similarity
170+
var t1 = summary("1", 404, "reason", "foo bar baz", true, false, "/a");
171+
var t2 = summary("2", 404, "reason", "foo bar qux", true, false, "/b");
172+
List<Map<String, Object>> result = BucketsCalculator.createBuckets(List.of(t1, t2));
173+
var buckets = (List<?>) result.get(0).get("buckets");
174+
assertThat(buckets).hasSize(2);
175+
}
176+
177+
@Test
178+
void testBucketsByCustomSimilarity() {
179+
// Use two bodies that are not exact, not normalized, but areErrorsSimilar returns true
180+
var t1 = summary("1", 404, "reason", "Error: code 123", true, false, "/a");
181+
var t2 = summary("2", 404, "reason", "Error: code 124", true, false, "/b");
182+
List<Map<String, Object>> result = BucketsCalculator.createBuckets(List.of(t1, t2));
183+
var buckets = (List<?>) result.get(0).get("buckets");
184+
assertThat(buckets).hasSize(1);
185+
}
186+
187+
@Test
188+
void testBucketsSeparateWhenNotSimilar() {
189+
var t1 = summary("1", 404, "reason", "foo", true, false, "/a");
190+
var t2 = summary("2", 404, "reason", "bar", true, false, "/b");
191+
List<Map<String, Object>> result = BucketsCalculator.createBuckets(List.of(t1, t2));
192+
var buckets = (List<?>) result.get(0).get("buckets");
193+
assertThat(buckets).hasSize(2);
194+
}
195+
196+
@Test
197+
void testPathGroupingAndHtmlLinks() {
198+
var t1 = summary("1", 404, "reason", "body", true, false, "/api/foo");
199+
var t2 = summary("2", 404, "reason", "body", true, false, "/api/foo");
200+
var t3 = summary("3", 404, "reason", "body", true, false, "/api/bar");
201+
List<Map<String, Object>> result = BucketsCalculator.createBuckets(List.of(t1, t2, t3));
202+
var buckets = (List<?>) result.get(0).get("buckets");
203+
var bucket = (Map<?, ?>) buckets.get(0);
204+
var paths = (List<?>) bucket.get("paths");
205+
assertThat(paths).hasSize(2);
206+
var foo = (Map<?, ?>) paths.stream().filter(p -> ((Map<?, ?>) p).get("path").equals("/api/foo")).findFirst().orElseThrow();
207+
assertThat((String) foo.get("testCases")).contains("<a href=\"1.html").contains("<a href=\"2.html");
208+
var bar = (Map<?, ?>) paths.stream().filter(p -> ((Map<?, ?>) p).get("path").equals("/api/bar")).findFirst().orElseThrow();
209+
assertThat((String) bar.get("testCases")).contains("<a href=\"3.html");
210+
}
211+
212+
@Test
213+
void testColorFormat() {
214+
var t1 = summary("1", 404, "reason", "body", true, false, "/a");
215+
List<Map<String, Object>> result = BucketsCalculator.createBuckets(List.of(t1));
216+
var buckets = (List<?>) result.get(0).get("buckets");
217+
var bucket = (Map<?, ?>) buckets.get(0);
218+
String color = (String) bucket.get("borderColor");
219+
assertThat(color).matches(Pattern.compile("#([0-9a-fA-F]{6})"));
220+
}
221+
}

0 commit comments

Comments
 (0)