@@ -82,7 +82,7 @@ public void testShowLogWithLongMessage() {
8282 public void testGetSHAFromStringWithValidInput () {
8383 String input = "test_string" ;
8484 String sha = sdkUtil .getSHAFromString (input );
85-
85+
8686 assertNotNull (sha );
8787 assertFalse (sha .isEmpty ());
8888 }
@@ -92,23 +92,23 @@ public void testGetSHAFromStringConsistency() {
9292 String input = "consistent_input" ;
9393 String sha1 = sdkUtil .getSHAFromString (input );
9494 String sha2 = sdkUtil .getSHAFromString (input );
95-
95+
9696 assertEquals ("Same input should produce same SHA" , sha1 , sha2 );
9797 }
9898
9999 @ Test
100100 public void testGetSHAFromStringDifferentInputs () {
101101 String sha1 = sdkUtil .getSHAFromString ("input1" );
102102 String sha2 = sdkUtil .getSHAFromString ("input2" );
103-
103+
104104 assertNotEquals ("Different inputs should produce different SHAs" , sha1 , sha2 );
105105 }
106106
107107 @ Test
108108 public void testGetSHAFromStringWithSpecialCharacters () {
109109 String input = "!@#$%^&*()_+-={}[]|\\ :;<>?,./~`" ;
110110 String sha = sdkUtil .getSHAFromString (input );
111-
111+
112112 assertNotNull (sha );
113113 assertFalse (sha .isEmpty ());
114114 }
@@ -117,7 +117,7 @@ public void testGetSHAFromStringWithSpecialCharacters() {
117117 public void testGetSHAFromStringWithUnicode () {
118118 String input = "Hello 世界 مرحبا мир" ;
119119 String sha = sdkUtil .getSHAFromString (input );
120-
120+
121121 assertNotNull (sha );
122122 assertFalse (sha .isEmpty ());
123123 }
@@ -126,7 +126,7 @@ public void testGetSHAFromStringWithUnicode() {
126126 public void testGetSHAFromStringWithNumbers () {
127127 String input = "1234567890" ;
128128 String sha = sdkUtil .getSHAFromString (input );
129-
129+
130130 assertNotNull (sha );
131131 assertFalse (sha .isEmpty ());
132132 }
@@ -138,7 +138,7 @@ public void testGetSHAFromStringWithLongInput() {
138138 longInput .append ("a" );
139139 }
140140 String sha = sdkUtil .getSHAFromString (longInput .toString ());
141-
141+
142142 assertNotNull (sha );
143143 assertFalse (sha .isEmpty ());
144144 }
@@ -149,7 +149,7 @@ public void testGetSHAFromStringWithLongInput() {
149149 public void testParseDateWithValidISO8601 () {
150150 String date = "2023-01-15T10:30:00.000Z" ;
151151 Calendar calendar = SDKUtil .parseDate (date , TimeZone .getTimeZone ("UTC" ));
152-
152+
153153 assertNotNull (calendar );
154154 assertEquals (2023 , calendar .get (Calendar .YEAR ));
155155 assertEquals (0 , calendar .get (Calendar .MONTH )); // January is 0
@@ -171,10 +171,10 @@ public void testParseDateWithInvalidFormat() {
171171 @ Test
172172 public void testParseDateWithDifferentTimezones () {
173173 String date = "2023-06-15T12:00:00.000Z" ;
174-
174+
175175 Calendar utc = SDKUtil .parseDate (date , TimeZone .getTimeZone ("UTC" ));
176176 Calendar pst = SDKUtil .parseDate (date , TimeZone .getTimeZone ("PST" ));
177-
177+
178178 assertNotNull (utc );
179179 assertNotNull (pst );
180180 }
@@ -183,7 +183,7 @@ public void testParseDateWithDifferentTimezones() {
183183 public void testParseDateWithMilliseconds () {
184184 String date = "2023-12-31T23:59:59.999Z" ;
185185 Calendar calendar = SDKUtil .parseDate (date , TimeZone .getTimeZone ("UTC" ));
186-
186+
187187 assertNotNull (calendar );
188188 assertEquals (2023 , calendar .get (Calendar .YEAR ));
189189 assertEquals (11 , calendar .get (Calendar .MONTH )); // December is 11
@@ -196,7 +196,7 @@ public void testParseDateWithMilliseconds() {
196196 public void testGetResponseTimeFromCacheFileWithZeroTime () {
197197 File file = new File ("test.txt" );
198198 boolean result = sdkUtil .getResponseTimeFromCacheFile (file , 0 );
199-
199+
200200 // With 0 time, should consider cache expired
201201 assertNotNull (result );
202202 }
@@ -213,7 +213,7 @@ public void testGetJsonFromCacheFileWithNullFile() {
213213 public void testGetJsonFromCacheFileWithNonExistentFile () {
214214 File nonExistentFile = new File ("/non/existent/path/file.json" );
215215 JSONObject result = SDKUtil .getJsonFromCacheFile (nonExistentFile );
216-
216+
217217 assertNull ("Should return null for non-existent file" , result );
218218 }
219219
@@ -234,11 +234,11 @@ public void testGetSHAFromStringNullHandling() {
234234 @ Test
235235 public void testParseDateWithVariousISO8601Formats () {
236236 String [] dates = {
237- "2023-01-01T00:00:00.000Z" ,
238- "2023-06-15T12:30:45.123Z" ,
239- "2023-12-31T23:59:59.999Z"
237+ "2023-01-01T00:00:00.000Z" ,
238+ "2023-06-15T12:30:45.123Z" ,
239+ "2023-12-31T23:59:59.999Z"
240240 };
241-
241+
242242 for (String date : dates ) {
243243 Calendar calendar = SDKUtil .parseDate (date , TimeZone .getTimeZone ("UTC" ));
244244 assertNotNull ("Date should be parsed: " + date , calendar );
@@ -256,15 +256,15 @@ public void testShowLogWithSpecialCharacters() {
256256 public void testGetSHAFromStringWithWhitespace () {
257257 String sha1 = sdkUtil .getSHAFromString ("no spaces" );
258258 String sha2 = sdkUtil .getSHAFromString ("no spaces" );
259-
259+
260260 assertEquals (sha1 , sha2 );
261261 }
262262
263263 @ Test
264264 public void testGetSHAFromStringDifferentCasing () {
265265 String sha1 = sdkUtil .getSHAFromString ("Test" );
266266 String sha2 = sdkUtil .getSHAFromString ("test" );
267-
267+
268268 assertNotEquals ("Different casing should produce different SHAs" , sha1 , sha2 );
269269 }
270270
@@ -286,7 +286,7 @@ public void testMultipleDateParses() {
286286 for (int i = 0 ; i < 10 ; i ++) {
287287 dates [i ] = "2023-01-" + String .format ("%02d" , (i + 1 )) + "T00:00:00.000Z" ;
288288 }
289-
289+
290290 for (String date : dates ) {
291291 Calendar calendar = SDKUtil .parseDate (date , TimeZone .getTimeZone ("UTC" ));
292292 assertNotNull (calendar );
@@ -301,10 +301,10 @@ public void testStaticMethodConcurrency() {
301301 SDKUtil .showLog ("Tag1" , "Message1" );
302302 SDKUtil .showLog ("Tag2" , "Message2" );
303303 SDKUtil .showLog ("Tag3" , "Message3" );
304-
304+
305305 String sha1 = sdkUtil .getSHAFromString ("input1" );
306306 String sha2 = sdkUtil .getSHAFromString ("input2" );
307-
307+
308308 assertNotEquals (sha1 , sha2 );
309309 }
310310
@@ -313,13 +313,210 @@ public void testMultipleSDKUtilInstances() {
313313 SDKUtil util1 = new SDKUtil ();
314314 SDKUtil util2 = new SDKUtil ();
315315 SDKUtil util3 = new SDKUtil ();
316-
316+
317317 String sha1 = util1 .getSHAFromString ("test" );
318318 String sha2 = util2 .getSHAFromString ("test" );
319319 String sha3 = util3 .getSHAFromString ("test" );
320-
320+
321321 assertEquals ("All instances should produce same SHA for same input" , sha1 , sha2 );
322322 assertEquals ("All instances should produce same SHA for same input" , sha2 , sha3 );
323323 }
324- }
325324
325+ @ Test
326+ public void testJsonToHTML_ObjectEntry_TextAndAssetFallback () throws JSONException {
327+
328+ JSONObject doc = new JSONObject ();
329+ doc .put ("type" , "doc" );
330+
331+ JSONArray children = new JSONArray ();
332+
333+ // Child 1: plain text node (no "type", has "text")
334+ JSONObject textNode = new JSONObject ();
335+ textNode .put ("text" , "Hello " );
336+ children .put (textNode );
337+
338+ // Child 2: reference node to an ASSET (no embedded match → asset fallback)
339+ JSONObject refNode = new JSONObject ();
340+ refNode .put ("type" , "reference" );
341+
342+ JSONObject attrs = new JSONObject ();
343+ attrs .put ("type" , "asset" );
344+ attrs .put ("text" , "My Image" );
345+ attrs .put ("asset-uid" , "asset123" );
346+ attrs .put ("display-type" , "display" );
347+ refNode .put ("attrs" , attrs );
348+
349+ // children for the reference node: MUST be a JSONArray of JSONObject
350+ JSONArray refChildren = new JSONArray ();
351+ JSONObject refChild = new JSONObject ();
352+ refChild .put ("text" , "inner-text" );
353+ refChildren .put (refChild );
354+ refNode .put ("children" , refChildren );
355+
356+ children .put (refNode );
357+
358+ // Child 3: a non-reference block (e.g. paragraph) to exercise renderNode for
359+ // non-reference
360+ JSONObject paraNode = new JSONObject ();
361+ paraNode .put ("type" , "paragraph" );
362+
363+ JSONArray paraChildren = new JSONArray ();
364+ JSONObject paraText = new JSONObject ();
365+ paraText .put ("text" , "More text" );
366+ paraChildren .put (paraText );
367+ paraNode .put ("children" , paraChildren );
368+
369+ children .put (paraNode );
370+
371+ doc .put ("children" , children );
372+
373+ // Entry object with field "rte_field" pointing to this doc
374+ JSONObject entry = new JSONObject ();
375+ entry .put ("rte_field" , doc );
376+
377+ Option option = new Option () {
378+ @ Override
379+ public String renderNode (String nodeType , JSONObject nodeJson , NodeCallback nodeCallback ) {
380+ if ("img" .equalsIgnoreCase (nodeType )) {
381+ // asset-fallback path
382+ return "<img/>" ;
383+ }
384+ // For non-asset nodes, we can prove this was called:
385+ return "<node:" + nodeType + ">" ;
386+ }
387+
388+ @ Override
389+ public String renderOptions (JSONObject contentToPass , Metadata metadata ) {
390+ // Would be used if an embedded item is found; here we don't match any,
391+ // and we don't even provide _embedded_items.
392+ return "<embedded:" + metadata .getItemUid () + ">" ;
393+ }
394+
395+ @ Override
396+ public String renderMark (MarkType markType , String text ) {
397+ return text ;
398+ }
399+ };
400+
401+ String [] keyPath = new String [] { "rte_field" };
402+ SDKUtil .jsonToHTML (entry , keyPath , option );
403+
404+ // After transformation, the field "rte_field" will have been updated
405+ Object transformed = entry .opt ("rte_field" );
406+ assertNotNull (transformed );
407+
408+ // transformed will likely be a String or JSONArray; just check for markers.
409+ String asString = transformed .toString ();
410+ assertTrue (asString .contains ("<img/>" ));
411+ assertTrue (asString .contains ("<node:paragraph>" ));
412+ }
413+
414+ @ Test
415+ public void testJsonToHTML_ArrayEntry_DelegatesToObjectVersion () throws JSONException {
416+ // Build an entry array with one object having an rte field
417+ JSONObject singleEntry = new JSONObject ();
418+ JSONObject doc = new JSONObject ();
419+ doc .put ("type" , "doc" );
420+ doc .put ("children" , new JSONArray ());
421+ singleEntry .put ("rte_field" , doc );
422+
423+ JSONArray entryArray = new JSONArray ();
424+ entryArray .put (singleEntry );
425+
426+ Option option = new Option () {
427+ @ Override
428+ public String renderNode (String nodeType , JSONObject jsonNode , NodeCallback nodeCallback ) {
429+ // Just indicate we've visited this node
430+ return "<node:" + nodeType + ">" ;
431+ }
432+
433+ @ Override
434+ public String renderOptions (JSONObject contentToPass , Metadata metadata ) {
435+ return "<embedded>" ;
436+ }
437+
438+ @ Override
439+ public String renderMark (MarkType markType , String text ) {
440+ return text ;
441+ }
442+ };
443+
444+ String [] keyPath = new String [] { "rte_field" };
445+ SDKUtil .jsonToHTML (entryArray , keyPath , option );
446+
447+ JSONObject after = entryArray .getJSONObject (0 );
448+ assertNotNull (after .opt ("rte_field" ));
449+ }
450+
451+ @ Test
452+ public void testJsonToHTML_EmbeddedItemsAndEnumerateContents () throws JSONException {
453+ JSONObject doc = new JSONObject ();
454+ doc .put ("type" , "doc" );
455+
456+ JSONArray rootChildren = new JSONArray ();
457+
458+ // Reference node with attrs that should match an embedded entry
459+ JSONObject refNode = new JSONObject ();
460+ refNode .put ("type" , "reference" );
461+
462+ JSONObject attrs = new JSONObject ();
463+ attrs .put ("type" , "entry" ); // not "asset"
464+ attrs .put ("text" , "Linked Entry" );
465+ attrs .put ("entry-uid" , "entry123" ); // must match embedded uid
466+ attrs .put ("content-type-uid" , "blog" ); // example
467+ attrs .put ("display-type" , "inline" );
468+ refNode .put ("attrs" , attrs );
469+
470+ // children for the reference node (array of JSONObject)
471+ JSONArray refChildren = new JSONArray ();
472+ JSONObject refChild = new JSONObject ();
473+ refChild .put ("text" , "child text" );
474+ refChildren .put (refChild );
475+ refNode .put ("children" , refChildren );
476+
477+ rootChildren .put (refNode );
478+ doc .put ("children" , rootChildren );
479+
480+ // 2) Put doc into a field that will be traversed by keyPath
481+ JSONObject entry = new JSONObject ();
482+ entry .put ("rte_field_array" , rootChildren );
483+ JSONObject embeddedItems = new JSONObject ();
484+ JSONArray entryArray = new JSONArray ();
485+
486+ JSONObject embeddedEntry = new JSONObject ();
487+ embeddedEntry .put ("uid" , "entry123" );
488+ embeddedEntry .put ("title" , "Embedded Title" );
489+ entryArray .put (embeddedEntry );
490+
491+ embeddedItems .put ("entry" , entryArray );
492+ entry .put ("_embedded_items" , embeddedItems );
493+
494+ // 4) Custom Option to make behavior observable
495+ Option option = new Option () {
496+ @ Override
497+ public String renderNode (String nodeType , JSONObject nodeJson , NodeCallback nodeCallback ) {
498+ return "<node:" + nodeType + ">" ;
499+ }
500+
501+ @ Override
502+ public String renderOptions (JSONObject contentToPass , Metadata metadata ) {
503+ return "<embedded:" + contentToPass .optString ("uid" ) + ">" ;
504+ }
505+
506+ @ Override
507+ public String renderMark (MarkType markType , String text ) {
508+ return text ;
509+ }
510+ };
511+
512+ String [] keyPath = new String [] { "rte_field_array" };
513+ SDKUtil .jsonToHTML (entry , keyPath , option );
514+
515+ Object transformed = entry .opt ("rte_field_array" );
516+ assertNotNull (transformed );
517+ assertTrue (transformed instanceof JSONArray );
518+ JSONArray resultArray = (JSONArray ) transformed ;
519+
520+ assertTrue (resultArray .length () >= 1 );
521+ }
522+ }
0 commit comments