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.Utils.*;
20 import static org.junit.jupiter.api.Assertions.*;
21
22 import java.util.*;
23
24 import org.apache.juneau.*;
25 import org.junit.jupiter.api.*;
26
27
28
29
30
31
32
33 class BeanConverter_Test extends TestBase {
34
35
36
37
38
39 @Nested
40 class A_contractCompliance extends TestBase {
41
42 @Test
43 void a01_basicContractVerification() {
44 var converter = BasicBeanConverter.DEFAULT;
45
46
47 assertNotNull(converter, "Default converter should not be null");
48 assertNotNull(converter.stringify(null), "stringify(null) should return non-null");
49 assertThrows(IllegalArgumentException.class, () -> converter.listify(null), "listify(null) should throw IllegalArgumentException");
50 assertNotNull(converter.getNested(new Object(), tokenize("toString").get(0)), "getNested should return non-null");
51 }
52
53 @Test
54 void a02_stringifyContract() {
55 var converter = BasicBeanConverter.DEFAULT;
56
57
58 assertEquals("<null>", converter.stringify(null));
59 assertEquals("test", converter.stringify("test"));
60 assertEquals("42", converter.stringify(42));
61 assertEquals("true", converter.stringify(true));
62
63
64 var list = Arrays.asList("a", "b", "c");
65 var result = converter.stringify(list);
66 assertTrue(result.contains("a") && result.contains("b") && result.contains("c"));
67 }
68
69 @Test
70 void a03_listifyContract() {
71 var converter = BasicBeanConverter.DEFAULT;
72
73
74 assertThrows(IllegalArgumentException.class, () -> converter.listify(null));
75 assertThrows(IllegalArgumentException.class, () -> converter.listify("single"));
76
77
78 var input = Arrays.asList("a", "b", "c");
79 var result = converter.listify(input);
80 assertEquals(3, result.size());
81 assertEquals("a", result.get(0));
82 assertEquals("b", result.get(1));
83 assertEquals("c", result.get(2));
84
85
86 String[] array = {"x", "y", "z"};
87 var arrayResult = converter.listify(array);
88 assertEquals(3, arrayResult.size());
89 assertEquals("x", arrayResult.get(0));
90 }
91
92 @Test
93 void a04_getNestedContract() {
94 var converter = BasicBeanConverter.DEFAULT;
95
96
97 var bean = new TestBean("test", 42);
98 assertEquals("test", converter.getNested(bean, tokenize("name").get(0)));
99 assertEquals("42", converter.getNested(bean, tokenize("value").get(0)));
100
101
102 assertEquals(bean.toString(), converter.getNested(bean, tokenize("toString").get(0)));
103
104
105 assertEquals("{"+bean.getClass().getSimpleName()+"}", converter.getNested(bean, tokenize("class{simpleName}").get(0)));
106 }
107 }
108
109
110
111
112
113 @Nested
114 class B_edgeCases extends TestBase {
115
116 @Test
117 void b01_nullInputHandling() {
118 var converter = BasicBeanConverter.DEFAULT;
119
120
121 assertNotNull(converter.stringify(null));
122 assertThrows(IllegalArgumentException.class, () -> converter.listify(null));
123 assertFalse(converter.canListify(null));
124 assertNotNull(converter.getNested(null, tokenize("anyProperty").get(0)));
125
126
127 assertThrows(IllegalArgumentException.class, () -> converter.getNested(new Object(), null));
128 }
129
130 @Test
131 void b02_emptyInputHandling() {
132 var converter = BasicBeanConverter.DEFAULT;
133
134
135 assertEquals("[]", converter.stringify(new ArrayList<>()));
136 assertEquals(0, converter.listify(new ArrayList<>()).size());
137
138
139 assertEquals("", converter.stringify(""));
140 assertThrows(IllegalArgumentException.class, () -> converter.listify(""));
141
142
143 assertEquals("[]", converter.stringify(new Object[0]));
144 assertEquals(0, converter.listify(new Object[0]).size());
145 }
146
147 @Test
148 void b03_largeInputHandling() {
149 var converter = BasicBeanConverter.DEFAULT;
150
151
152 var largeList = new ArrayList<Integer>();
153 for (int i = 0; i < 1000; i++) {
154 largeList.add(i);
155 }
156
157 var stringResult = converter.stringify(largeList);
158 assertNotNull(stringResult);
159 assertTrue(stringResult.length() > 100);
160
161 var listResult = converter.listify(largeList);
162 assertEquals(1000, listResult.size());
163 }
164
165 @Test
166 void b04_specialCharacterHandling() {
167 var converter = BasicBeanConverter.DEFAULT;
168
169
170 var unicode = "测试 🎉 ñoël";
171 assertEquals(unicode, converter.stringify(unicode));
172
173
174 String special = "tab\there\nnewline\rcarriage";
175 String result = converter.stringify(special);
176 assertNotNull(result);
177
178
179 String nullChar = "before\u0000after";
180 assertNotNull(converter.stringify(nullChar));
181 }
182
183 @Test
184 void b05_circularReferenceHandling() {
185 var converter = BasicBeanConverter.DEFAULT;
186
187
188 var parent = new CircularTestBean("parent");
189 var child = new CircularTestBean("child");
190 parent.child = child;
191 child.parent = parent;
192
193
194 var result = converter.stringify(parent);
195 assertNotNull(result);
196
197
198 assertEquals("{child}", converter.getNested(parent, tokenize("child{name}").get(0)));
199 }
200 }
201
202
203
204
205
206 @Nested
207 class C_implementationVerification extends TestBase {
208
209 @Test
210 void c01_builderPatternVerification() {
211
212 var custom = BasicBeanConverter.builder()
213 .defaultSettings()
214 .addSetting("nullValue", "<empty>")
215 .build();
216
217 assertNotNull(custom);
218 assertEquals("<empty>", custom.stringify(null));
219 }
220
221 @Test
222 void c02_defaultConverterImmutability() {
223 var defaultConverter = BasicBeanConverter.DEFAULT;
224
225
226 var another = BasicBeanConverter.DEFAULT;
227 assertSame(defaultConverter, another);
228 }
229
230 @Test
231 void c03_customSettingsPersistence() {
232 var custom = BasicBeanConverter.builder()
233 .defaultSettings()
234 .addSetting("nullValue", "CUSTOM_NULL")
235 .addSetting("selfValue", "CUSTOM_SELF")
236 .build();
237
238 assertEquals("CUSTOM_NULL", custom.stringify(null));
239
240
241 assertEquals("CUSTOM_NULL", custom.stringify(null));
242 assertEquals("CUSTOM_NULL", custom.stringify(null));
243 }
244
245 @Test
246 void c04_extensibilityVerification() {
247
248 var custom = BasicBeanConverter.builder()
249 .defaultSettings()
250 .addStringifier(TestBean.class, (converter, bean) -> "CUSTOM:" + bean.name)
251 .build();
252
253 var bean = new TestBean("test", 42);
254 assertEquals("CUSTOM:test", custom.stringify(bean));
255 }
256 }
257
258
259
260
261
262 @Nested
263 class D_errorHandling extends TestBase {
264
265 @Test
266 void d01_invalidPropertyAccess_throwsPropertyNotFoundException() {
267 var converter = BasicBeanConverter.DEFAULT;
268 var bean = new TestBean("test", 42);
269
270
271 var ex = assertThrows(PropertyNotFoundException.class, () ->
272 converter.getNested(bean, tokenize("nonExistentProperty").get(0)));
273
274
275 assertTrue(ex.getMessage().contains("nonExistentProperty"));
276 assertTrue(ex.getMessage().contains("TestBean"));
277 }
278
279 @Test
280 void d02_exceptionInPropertyAccess() {
281 var converter = BasicBeanConverter.DEFAULT;
282 var bean = new ExceptionThrowingBean();
283
284
285 assertThrows(RuntimeException.class, () ->
286 converter.getNested(bean, tokenize("throwingProperty").get(0)));
287 }
288
289 @Test
290 void d03_malformedPropertyPath() {
291 var converter = BasicBeanConverter.DEFAULT;
292 var bean = new TestBean("test", 42);
293
294
295 assertThrows(PropertyNotFoundException.class, () -> converter.getNested(bean, tokenize("invalidProperty").get(0)));
296
297 assertThrows(IllegalArgumentException.class, () -> converter.getNested(bean, null));
298 }
299
300 @Test
301 void d04_typeConversionErrors() {
302 var converter = BasicBeanConverter.DEFAULT;
303
304
305 var problematic = new Object() {
306 @Override
307 public String toString() {
308 throw new RuntimeException("Cannot convert to string");
309 }
310 };
311
312
313 String result = converter.stringify(problematic);
314 assertNotNull(result);
315 assertTrue(result.contains("RuntimeException"));
316 assertTrue(result.contains("Cannot convert to string"));
317 }
318 }
319
320
321
322
323
324 static class TestBean {
325 final String name;
326 final int value;
327
328 TestBean(String name, int value) {
329 this.name = name;
330 this.value = value;
331 }
332
333 String getName() { return name; }
334 int getValue() { return value; }
335
336 @Override
337 public String toString() {
338 return "TestBean(name=" + name + ", value=" + value + ")";
339 }
340 }
341
342 static class CircularTestBean {
343 final String name;
344 CircularTestBean parent;
345 CircularTestBean child;
346
347 CircularTestBean(String name) {
348 this.name = name;
349 }
350
351 String getName() { return name; }
352 CircularTestBean getParent() { return parent; }
353 CircularTestBean getChild() { return child; }
354 }
355
356 static class ExceptionThrowingBean {
357 public String getThrowingProperty() {
358 throw new RuntimeException("Intentional test exception");
359 }
360 }
361 }