1 package emissary.util;
2
3 import emissary.core.DataObjectFactory;
4 import emissary.core.Family;
5 import emissary.core.Form;
6 import emissary.core.IBaseDataObject;
7 import emissary.test.core.junit5.UnitTest;
8
9 import org.junit.jupiter.api.AfterAll;
10 import org.junit.jupiter.api.BeforeAll;
11 import org.junit.jupiter.api.Test;
12
13 import java.time.Instant;
14 import java.util.ArrayList;
15 import java.util.HashSet;
16 import java.util.List;
17 import java.util.Locale;
18 import java.util.Random;
19 import java.util.Set;
20
21 import static org.junit.jupiter.api.Assertions.assertEquals;
22 import static org.junit.jupiter.api.Assertions.assertFalse;
23 import static org.junit.jupiter.api.Assertions.assertTrue;
24
25 class PayloadUtilTest extends UnitTest {
26
27 @SuppressWarnings("NonFinalStaticField")
28 private static String timezone = "GMT";
29 private static final String validFormCharsString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-)(/+";
30 private static final Set<Character> validFormChars = new HashSet<>();
31
32 @BeforeAll
33 public static void setup() {
34
35 timezone = System.getProperty("user.timezone");
36 System.setProperty("user.timezone", "GMT");
37
38 validFormCharsString.chars().forEach(character -> validFormChars.add((char) character));
39 }
40
41 @AfterAll
42 public static void teardown() {
43 System.setProperty("user.timezone", timezone);
44 }
45
46 @Test
47 void testOneLineString() {
48
49 Instant now = Instant.now();
50
51
52 final String fn = "fname" + Family.SEP + "4";
53 final IBaseDataObject d = DataObjectFactory.getInstance("abc".getBytes(), fn, Form.UNKNOWN);
54 d.appendTransformHistory("FOO.UNKNOWN.FOOPLACE.http://example.com:1234/FooPlace");
55 d.appendTransformHistory("BAR.UNKNOWN.BARPLACE.http://example.com:1234/BarPlace");
56 d.appendTransformHistory("BAR.BURP.BURPPLACE.http://example.com:1234/BurpPlace");
57 d.setCreationTimestamp(now);
58 final String expected = "att-4 FOO,BAR>>[UNKNOWN]//UNKNOWN//" + now;
59
60
61 final String answer = PayloadUtil.getPayloadDisplayString(d, true);
62
63
64 assertFalse(answer.contains("\n"), "Must be one line string");
65 assertEquals(expected, answer, "Answer string did not equal the expected string");
66 }
67
68 @Test
69 void testOneLineStringOnTLD() {
70 Instant now = Instant.now();
71
72
73 final String fn = "fname";
74 final IBaseDataObject d = DataObjectFactory.getInstance("abc".getBytes(), fn, Form.UNKNOWN);
75 d.setCreationTimestamp(now);
76 d.appendTransformHistory("BOGUSKEYELEMENT");
77 final String expected = ">>[UNKNOWN]//UNKNOWN//" + now;
78
79
80 final String answer = PayloadUtil.getPayloadDisplayString(d, true);
81
82
83 assertFalse(answer.contains("\n"), "Must be one line string");
84 assertEquals(expected, answer, "Answer string did not equal the expected string");
85 }
86
87 @Test
88 void testMultiLineString() {
89
90 Instant now = Instant.now();
91
92
93 final String fn = "fname" + Family.SEP + "4";
94 final IBaseDataObject d = DataObjectFactory.getInstance("abc".getBytes(), fn, Form.UNKNOWN);
95 d.appendTransformHistory("FOO.UNKNOWN.FOOPLACE.http://example.com:1234/FooPlace");
96 d.appendTransformHistory("BAR.UNKNOWN.BARPLACE.http://example.com:1234/BarPlace");
97 d.setCreationTimestamp(now);
98
99
100 final String answer = PayloadUtil.getPayloadDisplayString(d, false);
101
102
103 assertTrue(answer.contains("\n"), "Must be multi-line string");
104 assertTrue(answer.contains("filename: fname-att-4"), "Answer did not contain the correct filename");
105 assertTrue(answer.contains("creationTimestamp: " + now), "Answer did not contain the creationTimestamp");
106 assertTrue(answer.contains("currentForms: [UNKNOWN]"), "Answer did not contain the currentForms");
107 assertTrue(answer.contains("filetype: UNKNOWN"), "Answer did not contain the correct filetype");
108 assertTrue(answer.contains("transform history (2)"), "Answer did not contain the transform history number");
109 assertTrue(answer.contains("FOO.UNKNOWN.FOOPLACE.http://example.com:1234/FooPlace"),
110 "Answer did not contain the correct transform history entry");
111 assertTrue(answer.contains("BAR.UNKNOWN.BARPLACE.http://example.com:1234/BarPlace"),
112 "Answer did not contain the correct transform history entry");
113 }
114
115 @Test
116 void testReducedHistory() {
117
118 final String fn = "testReducedHistory";
119 final IBaseDataObject d = DataObjectFactory.getInstance("abc".getBytes(), fn, Form.UNKNOWN);
120 d.appendTransformHistory("FOO.UNKNOWN.FOOPLACE.http://example.com:1234/FooPlace");
121 d.appendTransformHistory("BAR.UNKNOWN.BARPLACE.http://example.com:1234/BarPlace");
122 d.appendTransformHistory("TEST.DROPOFFPLACE.http://example.com:1234/DropOffPlace");
123 d.setCreationTimestamp(Instant.now());
124
125
126 PayloadUtil.historyPreference.put("UNKNOWN", "REDUCED_HISTORY");
127 final String answer = PayloadUtil.getPayloadDisplayString(d, false);
128 PayloadUtil.historyPreference.clear();
129
130
131 assertTrue(answer.contains("\n"), "Must be multi-line string");
132 assertTrue(answer.contains("filename: testReducedHistory"), "Answer did not contain the correct filename");
133 assertTrue(answer.contains("transform history (3)"), "Answer did not contain the transform history number");
134 assertFalse(answer.contains("FOO.UNKNOWN.FOOPLACE.http://example.com:1234/FooPlace"),
135 "Answer should not contain this transform history entry");
136 assertFalse(answer.contains("BAR.UNKNOWN.BARPLACE.http://example.com:1234/BarPlace"),
137 "Answer should not contain this transform history entry");
138 assertTrue(answer.contains("** reduced transform history **"), "Answer should contain 'reduced transform history'");
139 assertTrue(answer.contains("dropOff -> TEST.DROPOFFPLACE.http://example.com:1234/DropOffPlace"), "Answer should show dropoff");
140 }
141
142 @Test
143 void testNoUrlHistory() {
144
145 final String fn = "noUrlHistory";
146 final IBaseDataObject d = DataObjectFactory.getInstance("abc".getBytes(), fn, Form.UNKNOWN);
147 d.appendTransformHistory("FOO.UNKNOWN.FOOPLACE.http://example.com:1234/FooPlace");
148 d.appendTransformHistory("BAR.UNKNOWN.BARPLACE.http://example.com:1234/BarPlace");
149 d.setCreationTimestamp(Instant.now());
150
151
152 PayloadUtil.historyPreference.put("UNKNOWN", "NO_URL");
153 final String answer = PayloadUtil.getPayloadDisplayString(d, false);
154 PayloadUtil.historyPreference.clear();
155
156
157 assertTrue(answer.contains("\n"), "Must be multi-line string");
158 assertTrue(answer.contains("filename: noUrlHistory"), "Answer did not contain the correct filename");
159 assertTrue(answer.contains("FOO.UNKNOWN.FOOPLACE"),
160 "Answer should not contain the URL");
161 assertFalse(answer.contains("FOO.UNKNOWN.FOOPLACE.http://example.com:1234/FooPlace"), "Answer should not contain full URL");
162 assertTrue(answer.contains("BAR.UNKNOWN.BARPLACE"),
163 "Answer should not contain the URL");
164 assertFalse(answer.contains("BAR.UNKNOWN.BARPLACE.http://example.com:1234/BarPlace"), "Answer should not contain full URL");
165 }
166
167 @Test
168 void testHistoryDoesNotReduce() {
169
170 final String fn = "noMatch";
171 final IBaseDataObject d = DataObjectFactory.getInstance("abc".getBytes(), fn, Form.UNKNOWN);
172 d.appendTransformHistory("FOO.UNKNOWN.FOOPLACE.http://example.com:1234/FooPlace");
173 d.appendTransformHistory("BAR.UNKNOWN.BARPLACE.http://example.com:1234/BarPlace");
174 d.setCreationTimestamp(Instant.now());
175
176
177 PayloadUtil.historyPreference.put("REDUCED", "REDUCED_HISTORY");
178 PayloadUtil.historyPreference.put("NOURL", "NO_URL");
179 final String answer = PayloadUtil.getPayloadDisplayString(d, false);
180 PayloadUtil.historyPreference.clear();
181
182
183 assertTrue(answer.contains("\n"), "Must be multi-line string");
184 assertTrue(answer.contains("filename: noMatch"), "Answer did not contain the correct filename");
185 assertTrue(answer.contains("FOO.UNKNOWN.FOOPLACE.http://example.com:1234/FooPlace"),
186 "Answer should not reduce history due to no matching form");
187 assertTrue(answer.contains("BAR.UNKNOWN.BARPLACE.http://example.com:1234/BarPlace"),
188 "Answer should not reduce history due to no matching form");
189 }
190
191 @Test
192 void testNameOfSimpleObject() {
193 final IBaseDataObject d = DataObjectFactory.getInstance("abc".getBytes(), "ab/fn", Form.UNKNOWN);
194 assertEquals("fn", PayloadUtil.getName(d), "Name of simple payload is shortname");
195 }
196
197 @Test
198 void testNameOfCollection() {
199 final List<IBaseDataObject> list = new ArrayList<>();
200 for (int i = 0; i < 3; i++) {
201 final IBaseDataObject d = DataObjectFactory.getInstance("abc".getBytes(), "ab/fn" + i, Form.UNKNOWN);
202 list.add(d);
203 }
204 assertEquals("fn0(3)", PayloadUtil.getName(list), "Name of collection payload is shortname with count");
205 }
206
207 @Test
208 void testNameOfEmptyCollection() {
209 final List<IBaseDataObject> list = new ArrayList<>();
210 assertEquals("java.util.ArrayList", PayloadUtil.getName(list), "Name of empty collection is class name");
211 }
212
213 @Test
214 void testNameOfBadArgument() {
215 final String s = "foo";
216 assertEquals(s.getClass().getName(), PayloadUtil.getName(s), "Name of unexpected argument is class name");
217 }
218
219 @Test
220 void testXmlSerizliaztion() {
221 final IBaseDataObject d = DataObjectFactory.getInstance("abc".getBytes(), "testfile", Form.UNKNOWN);
222 d.addAlternateView("AV", "def".getBytes());
223 d.putParameter("P", "ghi");
224 d.addProcessingError("jkl");
225 d.setHeader("mno".getBytes());
226 d.setFooter("pqr".getBytes());
227 d.appendTransformHistory("stu");
228
229 final String xml = PayloadUtil.toXmlString(d);
230 assertTrue(xml.contains("abc"), "Xml serialization must include payload data");
231 assertTrue(xml.contains("def"), "Xml serialization must include av data");
232 assertTrue(xml.contains("ghi"), "Xml serialization must include param data");
233 assertTrue(xml.contains("jkl"), "Xml serialization must include error data");
234 assertTrue(xml.contains("mno"), "Xml serialization must include header data");
235 assertTrue(xml.contains("pqr"), "Xml serialization must include footer data");
236 assertTrue(xml.contains("stu"), "Xml serialization must include history data");
237
238 final List<IBaseDataObject> list = new ArrayList<>();
239 list.add(d);
240 final String lxml = PayloadUtil.toXmlString(list);
241 assertTrue(lxml.contains("abc"), "Xml serialization must include payload data");
242 assertTrue(lxml.contains("def"), "Xml serialization must include av data");
243 assertTrue(lxml.contains("ghi"), "Xml serialization must include param data");
244 assertTrue(lxml.contains("jkl"), "Xml serialization must include error data");
245 assertTrue(lxml.contains("mno"), "Xml serialization must include header data");
246 assertTrue(lxml.contains("pqr"), "Xml serialization must include footer data");
247 assertTrue(lxml.contains("stu"), "Xml serialization must include history data");
248 }
249
250 @Test
251 void testIsValidForm() {
252
253 String alphaLow = "abcdefghijklmnopqrstuvwxyz";
254 assertTrue(PayloadUtil.isValidForm(alphaLow), "Lower case alpha characters are expected to be valid");
255 assertTrue(PayloadUtil.isValidForm(alphaLow.toUpperCase(Locale.getDefault())), "Upper case alpha characters are expected to be valid");
256 assertTrue(PayloadUtil.isValidForm("0123456789"), "Numeric characters are expected to be valid");
257 assertTrue(PayloadUtil.isValidForm("-_"), "'-' and '_' are expected to be valid form characters");
258 assertTrue(PayloadUtil.isValidForm("formName-(suffixInParens)"), "Parentheses are expected to be valid form characters");
259 assertTrue(PayloadUtil.isValidForm("formName-(application/xml)"), "'/' is expected to be a valid form character");
260 assertFalse(PayloadUtil.isValidForm("."), "Dot should not be considered a valid form character");
261 assertFalse(PayloadUtil.isValidForm(" "), "Space should not be considered a valid form character");
262 assertTrue(PayloadUtil.isValidForm("+"), "'+' is expected to be a valid form character");
263
264
265 int validChars = 0;
266 for (int i = 0; i < Character.MAX_VALUE; i++) {
267 if (PayloadUtil.isValidForm(Character.toString((char) i))) {
268 validChars++;
269 }
270 }
271 assertEquals(validFormChars.size(), validChars, "Unexpected number of valid characters.");
272
273
274 Set<Character> formChars = new HashSet<>(validFormChars);
275
276 formChars.add('.');
277
278 Character[] formCharArray = new Character[formChars.size()];
279 formChars.toArray(formCharArray);
280
281 Random rand = new Random(0);
282
283 for (int i = 0; i < 4000000; i++) {
284 StringBuilder word = new StringBuilder();
285 int size = rand.nextInt(20);
286 for (int n = 0; n < size; n++) {
287 word.append(formCharArray[rand.nextInt(formChars.size())]);
288 }
289 String form = word.toString();
290 assertEquals(PayloadUtil.isValidForm(form),
291 isValidFormSetImplementation(form),
292 "Regex and Set implementations of form check differ for form \"" + form + "\"");
293 }
294 }
295
296 @Test
297 void testCompactHistory() {
298
299 final String fn = "noMatch";
300 final IBaseDataObject d = DataObjectFactory.getInstance("abc".getBytes(), fn, Form.UNKNOWN);
301 d.appendTransformHistory("FOO.PLACE_ONE.FOOPLACE.http://example.com:1234/FooPlace");
302 d.appendTransformHistory("BAR.PLACE_TWO.BARPLACE.http://example.com:1234/BarPlace");
303 d.appendTransformHistory("BAR.PLACE_THREE.NONEPLACE.http://example.com:1234/NonePlace", true);
304 d.setCreationTimestamp(Instant.now());
305
306
307 PayloadUtil.compactHistory = true;
308 final String answer = PayloadUtil.getPayloadDisplayString(d);
309 PayloadUtil.compactHistory = false;
310
311
312 assertTrue(answer.contains("transform history (3)"), "Answer history count is wrong");
313 assertTrue(answer.contains("FOO.FOOPLACE: FooPlace"), "Answer should have compacted history");
314 assertTrue(answer.contains("BAR.BARPLACE: BarPlace(NonePlace)"), "Answer should have compacted history");
315 }
316
317 @Test
318 void testCompactHistoryFormStack() {
319
320 final String fn = "noMatch";
321 final IBaseDataObject d = DataObjectFactory.getInstance("abc".getBytes(), fn, Form.UNKNOWN);
322 d.appendTransformHistory("FOO.PLACE_ONE.FOOPLACE.http://example.com:1234/FooPlace");
323 d.appendTransformHistory("[BAR-UPDATED].PLACE_TWO.BARPLACE.http://example.com:1234/BarPlace");
324 d.appendTransformHistory("[BAR].PLACE_THREE.NONEPLACE.http://example.com:1234/NonePlace");
325 d.setCreationTimestamp(Instant.now());
326
327
328 PayloadUtil.compactHistory = true;
329 final String answer = PayloadUtil.getPayloadDisplayString(d);
330 PayloadUtil.compactHistory = false;
331
332
333 assertTrue(answer.contains("transform history (3)"), "Answer history count is wrong");
334 assertTrue(answer.contains("FOO.FOOPLACE: FooPlace"), "Answer should have compacted history");
335 assertTrue(answer.contains("[BAR-UPDATED].BARPLACE: BarPlace"), "Answer should have compacted history");
336 assertTrue(answer.contains("[BAR].NONEPLACE: NonePlace"), "Answer should have compacted history");
337 }
338
339 @Test
340 void testCompactHistorySprout() {
341
342 final String fn = "noMatch";
343 final IBaseDataObject d = DataObjectFactory.getInstance("abc".getBytes(), fn, Form.UNKNOWN);
344 d.appendTransformHistory("FOO.PLACE_ONE.FOOPLACE.http://example.com:1234/FooPlace");
345 d.appendTransformHistory("*.*.<SPROUT>.http://example.com:1234/FooPlace");
346 d.appendTransformHistory("BAR.PLACE_TWO.BARPLACE.http://example.com:1234/BarPlace");
347 d.appendTransformHistory("BAR.PLACE_THREE.NONEPLACE.http://example.com:1234/NonePlace", true);
348 d.setCreationTimestamp(Instant.now());
349
350
351 PayloadUtil.compactHistory = true;
352 final String answer = PayloadUtil.getPayloadDisplayString(d);
353 PayloadUtil.compactHistory = false;
354
355
356 assertTrue(answer.contains("transform history (4)"), "Answer history count is wrong");
357 assertTrue(answer.contains("FOO.<SPROUT>: FooPlace"), "Answer should have compacted history");
358 assertTrue(answer.contains("BAR.BARPLACE: BarPlace(NonePlace)"), "Answer should have compacted history");
359 }
360
361
362
363
364
365
366
367
368
369 private static boolean isValidFormSetImplementation(String form) {
370 if (form.length() > 0) {
371 for (int i = 0; i < form.length(); i++) {
372 if (!validFormChars.contains(form.charAt(i))) {
373 return false;
374 }
375 }
376 return true;
377 }
378 return false;
379 }
380 }