This is an automated email from the ASF dual-hosted git repository.

jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git


The following commit(s) were added to refs/heads/master by this push:
     new 50edad73a9 READY-253 Cases 1-3 — pin acyclic invariants (BUG-17 
false-positives): PathTraversal.JsonNode comment + Messages parent-chain 
termination test + HttpPartSchema deep-nesting finiteness test. No wire change.
50edad73a9 is described below

commit 50edad73a9f010ba60caa4fa61b1f80a85295b95
Author: James Bognar <[email protected]>
AuthorDate: Fri Jul 17 09:52:25 2026 -0400

    READY-253 Cases 1-3 — pin acyclic invariants (BUG-17 false-positives): 
PathTraversal.JsonNode comment + Messages parent-chain termination test + 
HttpPartSchema deep-nesting finiteness test. No wire change.
    
    Co-authored-by: Cursor <[email protected]>
---
 .../juneau/marshall/objecttools/PathTraversal.java |  1 +
 .../java/org/apache/juneau/cp/Messages_Test.java   | 22 +++++++++++++
 .../httppart/HttpPartSchema_Validation_Test.java   | 37 ++++++++++++++++++++++
 3 files changed, 60 insertions(+)

diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/objecttools/PathTraversal.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/objecttools/PathTraversal.java
index f34bb958ca..58fe8af031 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/objecttools/PathTraversal.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/objecttools/PathTraversal.java
@@ -139,6 +139,7 @@ import org.apache.juneau.marshall.parser.*;
        "rawtypes"   // Raw types necessary for generic type handling
 })
 public class PathTraversal {
+       // Internal traversal node — never serialized; 'parent' is an upward 
tree pointer used only to walk back up the model during PUT/POST/DELETE 
rewrites.
        class JsonNode {
                Object o;
                ClassMeta cm;
diff --git 
a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/cp/Messages_Test.java
 
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/cp/Messages_Test.java
index 1849eb05b3..e3cf85eb64 100644
--- 
a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/cp/Messages_Test.java
+++ 
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/cp/Messages_Test.java
@@ -165,4 +165,26 @@ class Messages_Test extends TestBase {
                assertEquals("foo bar", x.getString("foo", "bar"));
                assertEquals("fooja bar", x.forLocale(JAPAN).getString("foo", 
"bar"));
        }
+
+       @Test void a15_parentChainTerminationAndFlatness() {
+               // Invariant anchor: a Messages parent chain is a finite, 
null-terminated singly-linked list, and
+               // toString() emits a FLAT key/value view (parent keys are 
folded into the child keyMap at
+               // construction) rather than recursively stringifying the 
parent2 back-reference.  Serialization/
+               // toString() therefore terminates regardless of chain depth.
+               var leaf = Messages.of(Test2.class);                            
         // deepest link: file, yyy
+               var mid = 
Messages.create(MessageBundleTest1.class).parent(leaf).build(); // + 
foo/bar/xx...
+               var top = Messages.chain(Messages.of(Test2.class), mid);        
         // 3-link chain
+
+               // (b) toString() returns finitely (no depth blow-up / no 
infinite recursion into parent2).
+               var s = assertDoesNotThrow(top::toString);
+
+               // (a) The flattened view resolves keys contributed by every 
level of the chain.
+               assertTrue(s.contains("yyy="), "Expected key contributed by the 
deepest link (Test2)");
+               assertTrue(s.contains("bar="), "Expected key contributed by the 
middle link (MessageBundleTest1)");
+               assertTrue(s.contains("foo="), "Expected key contributed by the 
middle link (MessageBundleTest1)");
+               assertTrue(s.contains("file="), "Expected shared 'file' key");
+
+               // Flattening is not nested — the private parent2 
back-reference is never exposed as a bean value.
+               assertFalse(s.contains("parent2"), "toString() must not expose 
the parent2 back-reference");
+       }
 }
\ No newline at end of file
diff --git 
a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/httppart/HttpPartSchema_Validation_Test.java
 
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/httppart/HttpPartSchema_Validation_Test.java
index 0c27908aed..d035174179 100644
--- 
a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/httppart/HttpPartSchema_Validation_Test.java
+++ 
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/httppart/HttpPartSchema_Validation_Test.java
@@ -1385,4 +1385,41 @@ class HttpPartSchema_Validation_Test extends TestBase {
                s.validateOutput(l("abc", "defghi"));
                assertThrowsWithMessage(SchemaValidationException.class, 
"Minimum length of value not met.", ()->s.validateOutput(l("a", "b")));
        }
+
+       
//-----------------------------------------------------------------------------------------------------------------
+       // Nested-schema finiteness invariant
+       
//-----------------------------------------------------------------------------------------------------------------
+
+       @Test void t01_deepNesting_buildAndToStringFinite() {
+               // Invariant anchor: the nested items/additionalProperties 
schema graph is an immutable DAG assembled
+               // bottom-up (each child is fully built before the parent's 
final field is assigned), so build() and
+               // toString() over a deeply-nested schema are finite.  The only 
way to manufacture a cycle is a
+               // self-referential builder, which would 
infinite-loop/StackOverflow at construction time and never
+               // reach here.
+               var depth = 100;
+               var b = HttpPartSchema.create().tString();
+               for (var i = 0; i < depth; i++) {
+                       b = (i % 2 == 0)
+                               ? HttpPartSchema.create().tArray().items(b)
+                               : 
HttpPartSchema.create().tObject().additionalProperties(b);
+               }
+               var built = b.build();
+
+               // toString()/serialize over the full nested graph returns 
finitely (no depth blow-up).
+               var str = assertDoesNotThrow(built::toString);
+               assertNotNull(str);
+               assertFalse(str.isEmpty());
+
+               // The graph is fully materialized to the requested depth 
(acyclic DAG, not truncated by a cycle guard).
+               var levels = 0;
+               var cur = built;
+               while (cur != null && levels <= depth + 5) {
+                       levels++;
+                       var next = cur.getItems();
+                       if (next == null)
+                               next = cur.getProperty("anyKey");  // 
additionalProperties fallback
+                       cur = next;
+               }
+               assertTrue(levels >= depth, "Expected the nested schema to be 
reachable to full depth");
+       }
 }

Reply via email to