1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.juneau.junit.bct;
18
19 import static org.apache.juneau.junit.bct.BctAssertions.*;
20 import static org.junit.jupiter.api.Assertions.*;
21
22 import java.text.*;
23 import java.time.*;
24 import java.util.*;
25 import java.util.function.*;
26
27 import org.apache.juneau.*;
28 import org.junit.jupiter.api.*;
29
30
31
32
33
34
35
36 class AssertionArgs_Test extends TestBase {
37
38
39 static class TestBean {
40 private String name;
41 private int age;
42 private boolean active;
43
44 public TestBean(String name, int age, boolean active) {
45 this.name = name;
46 this.age = age;
47 this.active = active;
48 }
49
50 public String getName() { return name; }
51 public int getAge() { return age; }
52 public boolean isActive() { return active; }
53 }
54
55 static class CustomObject {
56 private String value;
57
58 public CustomObject(String value) {
59 this.value = value;
60 }
61
62 public String getValue() { return value; }
63
64 @Override
65 public String toString() {
66 return "CustomObject[" + value + "]";
67 }
68 }
69
70 @Test
71 void a01_defaultConstruction() {
72 var args = new AssertionArgs();
73
74
75 assertTrue(args.getBeanConverter().isEmpty());
76
77
78 assertNull(args.getMessage());
79 }
80
81 @Test
82 void a02_fluentAPIReturnsThis() {
83 var args = new AssertionArgs();
84 var mockConverter = createMockConverter();
85
86
87 assertSame(args, args.setBeanConverter(mockConverter));
88 assertSame(args, args.setMessage("test message"));
89 assertSame(args, args.setMessage(() -> "dynamic message"));
90 }
91
92 @Test
93 void b01_beanConverterConfiguration() {
94 var args = new AssertionArgs();
95 var mockConverter = createMockConverter();
96
97
98 assertTrue(args.getBeanConverter().isEmpty());
99
100
101 args.setBeanConverter(mockConverter);
102 assertTrue(args.getBeanConverter().isPresent());
103 assertSame(mockConverter, args.getBeanConverter().get());
104
105
106 args.setBeanConverter(null);
107 assertTrue(args.getBeanConverter().isEmpty());
108 }
109
110 @Test
111 void b02_customConverterInAssertion() {
112
113 var customConverter = createCustomConverter();
114
115 var args = args().setBeanConverter(customConverter);
116 var obj = new TestBeanWithCustomObject("test", new CustomObject("value"));
117
118
119 assertBean(args, obj, "custom", "CUSTOM:value");
120 }
121
122 static class TestBeanWithCustomObject {
123 private String name;
124 private CustomObject custom;
125
126 public TestBeanWithCustomObject(String name, CustomObject custom) {
127 this.name = name;
128 this.custom = custom;
129 }
130
131 public String getName() { return name; }
132 public CustomObject getCustom() { return custom; }
133 }
134
135 @Test
136 void c01_messageSupplierConfiguration() {
137 var args = new AssertionArgs();
138
139
140 assertNull(args.getMessage());
141
142
143 Supplier<String> supplier = () -> "test message";
144 args.setMessage(supplier);
145 assertNotNull(args.getMessage());
146 assertEquals("test message", args.getMessage().get());
147
148
149 args.setMessage(() -> "different message");
150 assertEquals("different message", args.getMessage().get());
151 }
152
153 @Test
154 void c02_parameterizedMessageConfiguration() {
155 var args = new AssertionArgs();
156
157
158 args.setMessage("Hello {0}", "World");
159 assertEquals("Hello World", args.getMessage().get());
160
161
162 args.setMessage("User {0} has {1} points", "John", 100);
163 assertEquals("User John has 100 points", args.getMessage().get());
164
165
166 args.setMessage("Value: {0,number,#.##}", 123.456);
167 assertEquals("Value: 123.46", args.getMessage().get());
168 }
169
170 @Test
171 void c03_dynamicMessageSupplier() {
172 var counter = new int[1];
173 var args = new AssertionArgs();
174
175 args.setMessage(() -> "Call #" + (++counter[0]));
176
177
178 assertEquals("Call #1", args.getMessage().get());
179 assertEquals("Call #2", args.getMessage().get());
180 assertEquals("Call #3", args.getMessage().get());
181 }
182
183 @Test
184 void d01_messageCompositionWithoutCustomMessage() {
185 var args = new AssertionArgs();
186
187
188 var composedMessage = args.getMessage("Bean assertion failed");
189 assertEquals("Bean assertion failed", composedMessage.get());
190
191
192 var composedWithParams = args.getMessage("Element at index {0} did not match", 5);
193 assertEquals("Element at index 5 did not match", composedWithParams.get());
194 }
195
196 @Test
197 void d02_messageCompositionWithCustomMessage() {
198 var args = new AssertionArgs();
199 args.setMessage("User validation failed");
200
201
202 var composedMessage = args.getMessage("Bean assertion failed");
203 assertEquals("User validation failed, Caused by: Bean assertion failed", composedMessage.get());
204
205
206 var composedWithParams = args.getMessage("Element at index {0} did not match", 3);
207 assertEquals("User validation failed, Caused by: Element at index 3 did not match", composedWithParams.get());
208 }
209
210 @Test
211 void d03_messageCompositionWithParameterizedCustomMessage() {
212 var args = new AssertionArgs();
213 args.setMessage("Test {0} failed on iteration {1}", "UserValidation", 42);
214
215 var composedMessage = args.getMessage("Bean assertion failed");
216 assertEquals("Test UserValidation failed on iteration 42, Caused by: Bean assertion failed", composedMessage.get());
217 }
218
219 @Test
220 void d04_messageCompositionWithDynamicCustomMessage() {
221 var timestamp = Instant.now().toString();
222 var args = new AssertionArgs();
223 args.setMessage(() -> "Test failed at " + timestamp);
224
225 var composedMessage = args.getMessage("Bean assertion failed");
226 assertEquals("Test failed at " + timestamp + ", Caused by: Bean assertion failed", composedMessage.get());
227 }
228
229 @Test
230 void e01_fluentConfigurationChaining() {
231 var converter = createMockConverter();
232
233
234 var args = new AssertionArgs()
235 .setBeanConverter(converter)
236 .setMessage("Integration test failed for module {0}", "AuthModule");
237
238
239 assertTrue(args.getBeanConverter().isPresent());
240 assertSame(converter, args.getBeanConverter().get());
241 assertEquals("Integration test failed for module AuthModule", args.getMessage().get());
242 }
243
244 @Test
245 void e02_configurationOverwriting() {
246 var args = new AssertionArgs();
247 var converter1 = createMockConverter();
248 var converter2 = createMockConverter();
249
250
251 args.setBeanConverter(converter1).setMessage("First message");
252
253
254 args.setBeanConverter(converter2).setMessage("Second message");
255
256
257 assertSame(converter2, args.getBeanConverter().get());
258 assertEquals("Second message", args.getMessage().get());
259 }
260
261 @Test
262 void f01_integrationWithAssertBean() {
263 var bean = new TestBean("John", 30, true);
264 var args = args().setMessage("User test failed");
265
266
267 assertBean(args, bean, "name,age,active", "John,30,true");
268
269
270 var exception = assertThrows(AssertionError.class, () -> {
271 assertBean(args, bean, "name", "Jane");
272 });
273
274 assertTrue(exception.getMessage().contains("User test failed"));
275 assertTrue(exception.getMessage().contains("Caused by:"));
276 }
277
278 @Test
279 void f02_integrationWithAssertBeans() {
280 var beans = List.of(
281 new TestBean("Alice", 25, true),
282 new TestBean("Bob", 35, false)
283 );
284 var args = args().setMessage("Batch validation failed");
285
286
287 assertBeans(args, beans, "name,age", "Alice,25", "Bob,35");
288
289
290 var exception = assertThrows(AssertionError.class, () -> {
291 assertBeans(args, beans, "name", "Charlie", "David");
292 });
293
294 assertTrue(exception.getMessage().contains("Batch validation failed"));
295 assertTrue(exception.getMessage().contains("Caused by:"));
296 }
297
298 @Test
299 void f03_integrationWithAssertList() {
300 var list = List.of("apple", "banana", "cherry");
301 var args = args().setMessage("List validation failed");
302
303
304 assertList(args, list, "apple", "banana", "cherry");
305
306
307 var exception = assertThrows(AssertionError.class, () -> {
308 assertList(args, list, "orange", "banana", "cherry");
309 });
310
311 assertTrue(exception.getMessage().contains("List validation failed"));
312 assertTrue(exception.getMessage().contains("Caused by:"));
313 }
314
315 @Test
316 void g01_edgeCaseNullValues() {
317 var args = new AssertionArgs();
318
319
320 args.setBeanConverter(null);
321 assertTrue(args.getBeanConverter().isEmpty());
322
323
324 args.setMessage((Supplier<String>) null);
325 assertNull(args.getMessage());
326 }
327
328 @Test
329 void g02_edgeCaseEmptyMessages() {
330 var args = new AssertionArgs();
331
332
333 args.setMessage("");
334 assertEquals("", args.getMessage().get());
335
336
337 args.setMessage(() -> "");
338 assertEquals("", args.getMessage().get());
339
340
341 var composedMessage = args.getMessage("Bean assertion failed");
342 assertEquals(", Caused by: Bean assertion failed", composedMessage.get());
343 }
344
345 @Test
346 void g03_edgeCaseComplexParameterFormatting() {
347 var args = new AssertionArgs();
348 var date = new Date();
349
350
351 args.setMessage("Test executed on {0,date,short}", date);
352 var expectedDatePart = DateFormat.getDateInstance(DateFormat.SHORT).format(date);
353 assertTrue(args.getMessage().get().contains(expectedDatePart));
354
355
356 args.setMessage("Processing {0,number,percent} complete", 0.85);
357 assertTrue(args.getMessage().get().contains("85%"));
358 }
359
360 @Test
361 void h01_threadSafetyDocumentationCompliance() {
362
363
364
365 var sharedArgs = new AssertionArgs();
366 var results = Collections.synchronizedList(new ArrayList<String>());
367
368
369 var threads = new Thread[5];
370 for (int i = 0; i < threads.length; i++) {
371 final int threadId = i;
372 threads[i] = new Thread(() -> {
373 sharedArgs.setMessage("Thread " + threadId + " message");
374
375 try { Thread.sleep(1); } catch (InterruptedException e) {}
376 results.add(sharedArgs.getMessage().get());
377 });
378 }
379
380
381 for (var thread : threads) {
382 thread.start();
383 }
384
385
386 for (var thread : threads) {
387 try { thread.join(); } catch (InterruptedException e) {}
388 }
389
390
391
392 assertEquals(5, results.size());
393
394 }
395
396 @Test
397 void h02_recommendedUsagePattern() {
398
399
400
401 var userArgs = args().setMessage("User validation test");
402 var user = new TestBean("Alice", 25, true);
403 assertBean(userArgs, user, "name,active", "Alice,true");
404
405
406 var productArgs = args().setMessage("Product validation test");
407 var products = List.of("Laptop", "Phone", "Tablet");
408 assertList(productArgs, products, "Laptop", "Phone", "Tablet");
409
410
411 assertEquals("User validation test", userArgs.getMessage().get());
412 assertEquals("Product validation test", productArgs.getMessage().get());
413 }
414
415
416 private BeanConverter createMockConverter() {
417 return new BeanConverter() {
418 @Override
419 public String stringify(Object o) {
420 return String.valueOf(o);
421 }
422
423 @Override
424 public List<Object> listify(Object o) {
425 if (o instanceof List) return (List<Object>) o;
426 return List.of(o);
427 }
428
429 @Override
430 public boolean canListify(Object o) {
431 return true;
432 }
433
434 @Override
435 public Object swap(Object o) {
436 return o;
437 }
438
439 @Override
440 public Object getProperty(Object object, String name) {
441
442 if ("name".equals(name) && object instanceof TestBean) {
443 return ((TestBean) object).getName();
444 }
445 if ("custom".equals(name) && object instanceof TestBeanWithCustomObject) {
446 return ((TestBeanWithCustomObject) object).getCustom();
447 }
448 return null;
449 }
450
451 @Override
452 public <T> T getSetting(String key, T defaultValue) {
453 return defaultValue;
454 }
455
456 @Override
457 public String getNested(Object o, NestedTokenizer.Token token) {
458 var propValue = getProperty(o, token.getValue());
459 return stringify(propValue);
460 }
461 };
462 }
463
464
465 private BeanConverter createCustomConverter() {
466 return new BeanConverter() {
467 @Override
468 public String stringify(Object o) {
469 if (o instanceof CustomObject) {
470 return "CUSTOM:" + ((CustomObject) o).getValue();
471 }
472 return String.valueOf(o);
473 }
474
475 @Override
476 public List<Object> listify(Object o) {
477 if (o instanceof List) return (List<Object>) o;
478 return List.of(o);
479 }
480
481 @Override
482 public boolean canListify(Object o) {
483 return true;
484 }
485
486 @Override
487 public Object swap(Object o) {
488 return o;
489 }
490
491 @Override
492 public Object getProperty(Object object, String name) {
493 if ("custom".equals(name) && object instanceof TestBeanWithCustomObject) {
494 return ((TestBeanWithCustomObject) object).getCustom();
495 }
496 return null;
497 }
498
499 @Override
500 public <T> T getSetting(String key, T defaultValue) {
501 return defaultValue;
502 }
503
504 @Override
505 public String getNested(Object o, NestedTokenizer.Token token) {
506 var propValue = getProperty(o, token.getValue());
507 return stringify(propValue);
508 }
509 };
510 }
511 }