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 3b3009d2d9 Add coverage tests for MarshalledNode, JsonPointer, and
PathTraversal.
3b3009d2d9 is described below
commit 3b3009d2d9beadc340398e1ecbb5ba01a36bb142
Author: James Bognar <[email protected]>
AuthorDate: Tue Jun 16 10:56:55 2026 -0400
Add coverage tests for MarshalledNode, JsonPointer, and PathTraversal.
Fill line/branch coverage gaps on the TODO-175b classes (typed-tree façade,
RFC 6901 JSON-Pointer, relocated traversal engine, PathTraversalException).
Co-authored-by: Cursor <[email protected]>
---
.../marshall/collections/JsonPointer_Test.java | 81 ++++++++++++++++++++++
.../marshall/collections/MarshalledNode_Test.java | 9 +++
.../marshall/objecttools/PathTraversal_Test.java | 69 ++++++++++++++++++
3 files changed, 159 insertions(+)
diff --git
a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/collections/JsonPointer_Test.java
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/collections/JsonPointer_Test.java
index c6bb2bfe3a..0deccfcce9 100644
---
a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/collections/JsonPointer_Test.java
+++
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/collections/JsonPointer_Test.java
@@ -204,6 +204,39 @@ class JsonPointer_Test extends TestBase {
assertEquals("v", JsonPointer.of("").set(JsonMap.of("a", 1),
"v"));
}
+ @Test void e06_setIndexOutOfRangeThrows() {
+ // Setting at an index beyond the end of an existing list (not
the append slot) is an error.
+ var root = JsonMap.of("a", JsonList.of(1, 2));
+ assertThrows(IllegalArgumentException.class, () ->
JsonPointer.of("/a/5").set(root, 9));
+ }
+
+ @Test void e07_setReplaceAtExistingIndex() {
+ var root = JsonMap.of("a", JsonList.of(1, 2, 3));
+ JsonPointer.of("/a/1").set(root, 9);
+ assertEquals("{\"a\":[1,9,3]}", Json.of(root));
+ }
+
+ @Test void e08_vivifyListViaDashToken() {
+ // A '-' next token makes the auto-vivified intermediate
container a list, and appends to it.
+ var root = new JsonMap();
+ JsonPointer.of("/a/-").set(root, "x");
+ assertEquals("{\"a\":[\"x\"]}", Json.of(root));
+ assertInstanceOf(JsonList.class, root.get("a"));
+ }
+
+ @Test void e09_descendThroughExistingObject() {
+ // navigateToParent descends through an already-present
intermediate map without re-vivifying it.
+ var root = JsonMap.of("a", new JsonMap());
+ JsonPointer.of("/a/b").set(root, 1);
+ assertEquals("{\"a\":{\"b\":1}}", Json.of(root));
+ }
+
+ @Test void e10_setNonNumericListTokenThrows() {
+ // A non-numeric, non-'-' token on a list is an invalid array
index on write.
+ var root = JsonMap.of("a", JsonList.of(1, 2));
+ assertThrows(IllegalArgumentException.class, () ->
JsonPointer.of("/a/foo").set(root, 9));
+ }
+
//
-----------------------------------------------------------------------------------------------------------------
// f - Remove
//
-----------------------------------------------------------------------------------------------------------------
@@ -224,4 +257,52 @@ class JsonPointer_Test extends TestBase {
assertNull(JsonPointer.of("/missing").remove(JsonMap.of("a",
1)));
assertNull(JsonPointer.of("/a/5").remove(JsonMap.of("a",
JsonList.of("x"))));
}
+
+ @Test void f04_removeRootReturnsNull() {
+ // The root pointer addresses the whole document, which cannot
be removed in place.
+ assertNull(JsonPointer.of("").remove(JsonMap.of("a", 1)));
+ }
+
+ @Test void f05_removeMissingIntermediateReturnsNull() {
+ assertNull(JsonPointer.of("/a/b/c").remove(new JsonMap()));
+ }
+
+ @Test void f06_removeNonNumericListTokenReturnsNull() {
+ var root = JsonMap.of("a", JsonList.of("x", "y"));
+ assertNull(JsonPointer.of("/a/-").remove(root));
+ assertNull(JsonPointer.of("/a/foo").remove(root));
+ // The list is left unchanged.
+ assertEquals("{\"a\":[\"x\",\"y\"]}", Json.of(root));
+ }
+
+ @Test void f07_removeThroughScalarReturnsNull() {
+ assertNull(JsonPointer.of("/a/b").remove(JsonMap.of("a",
"scalar")));
+ }
+
+ //
-----------------------------------------------------------------------------------------------------------------
+ // g - Array index token parsing
+ //
-----------------------------------------------------------------------------------------------------------------
+
+ @Test void g01_emptyTokenOnArrayMisses() {
+ // An empty reference token is not a valid array index, so it
resolves to a miss.
+ assertNull(JsonPointer.of("/").eval(JsonList.of("x")));
+ }
+
+ @Test void g02_leadingZeroIndexRejected() {
+ // RFC 6901 forbids leading zeros in array indices, so they
resolve to a miss.
+ assertNull(JsonPointer.of("/01").eval(JsonList.of("a", "b")));
+ assertNull(JsonPointer.of("/00").eval(JsonList.of("a", "b")));
+ }
+
+ @Test void g03_multiDigitIndex() {
+ var l = new JsonList();
+ for (var i = 0; i < 12; i++)
+ l.add(i);
+ assertEquals(10, JsonPointer.of("/10").eval(l));
+ }
+
+ @Test void g04_overflowingIndexMisses() {
+ // An all-digit token too large to fit in an int is treated as
a non-index (miss), not an error.
+
assertNull(JsonPointer.of("/99999999999999999999").eval(JsonList.of("x")));
+ }
}
diff --git
a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/collections/MarshalledNode_Test.java
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/collections/MarshalledNode_Test.java
index 7b82ab49ca..d924bbf7af 100644
---
a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/collections/MarshalledNode_Test.java
+++
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/collections/MarshalledNode_Test.java
@@ -142,6 +142,15 @@ class MarshalledNode_Test extends TestBase {
assertNull(MarshalledNode.of(null).value());
}
+ @Test void b09_asNullAndConversionFailureReturnNull() {
+ // as(Class) on a null node short-circuits to null (no
conversion attempted).
+ assertNull(MarshalledNode.of(null).as(B06_Bean.class));
+ // as(Class) swallows InvalidDataConversionException and
returns null for a non-convertible scalar...
+ assertNull(MarshalledNode.of("notanumber").as(Integer.class));
+ // ...and for a container that cannot convert to the requested
scalar type.
+ assertNull(MarshalledNode.of(JsonList.of(1,
2)).as(Integer.class));
+ }
+
//
-----------------------------------------------------------------------------------------------------------------
// c - Navigation
//
-----------------------------------------------------------------------------------------------------------------
diff --git
a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/objecttools/PathTraversal_Test.java
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/objecttools/PathTraversal_Test.java
index 7507893dbe..1ed01c2bd6 100644
---
a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/objecttools/PathTraversal_Test.java
+++
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/objecttools/PathTraversal_Test.java
@@ -896,6 +896,14 @@ class PathTraversal_Test extends TestBase {
assertEquals("C2", p.addresses[0].city);
}
+ @Test void h12_put_intoArray_parentNotMapOrBean_throws() {
+ // Array nested directly inside a List: the array's parent is a
Collection (not Map/Bean) -> PUT error.
+ var root = new Json5List();
+ root.add(new String[]{"a","b"});
+ var model = PathTraversal.create(root);
+ assertThrowsWithMessage(PathTraversalException.class, "Cannot
perform PUT on '0/1' with parent node type", ()->model.put("0/1", "B"));
+ }
+
//====================================================================================================
// i - POST operations
//====================================================================================================
@@ -976,6 +984,22 @@ class PathTraversal_Test extends TestBase {
assertThrowsWithMessage(PathTraversalException.class, "Cannot
perform POST on", ()->model.post("name", "v"));
}
+ @Test void i09_post_toArray_parentNotMapOrBean_throws() {
+ // Array nested directly inside a List: the array's parent is a
Collection (not Map/Bean) -> POST error.
+ var root = new Json5List();
+ root.add(new String[]{"a","b"});
+ var model = PathTraversal.create(root);
+ assertThrowsWithMessage(PathTraversalException.class, "Cannot
perform POST on '0' with parent node type", ()->model.post("0", "c"));
+ }
+
+ @Test void i10_post_nodeNotFound_404() {
+ // POST where the target URL resolves through a null
intermediate -> getNode returns null -> 404.
+ var m = new HashMap<String,Object>();
+ m.put("a", null);
+ var model = PathTraversal.create(m);
+ assertThrowsWithMessage(PathTraversalException.class, "Node at
URL 'a/b' not found.", ()->model.post("a/b", "v"));
+ }
+
//====================================================================================================
// j - DELETE operations
//====================================================================================================
@@ -1055,6 +1079,28 @@ class PathTraversal_Test extends TestBase {
assertThrowsWithMessage(PathTraversalException.class, "Cannot
perform PUT on", ()->model.delete("name/x"));
}
+ @Test void j10_delete_fromArray_parentNotMapOrBean_throws() {
+ // Array nested directly inside a List: the array's parent is a
Collection (not Map/Bean) -> DELETE error.
+ var root = new Json5List();
+ root.add(new String[]{"a","b"});
+ var model = PathTraversal.create(root);
+ assertThrowsWithMessage(PathTraversalException.class, "Cannot
perform POST on '0/1' with parent node type", ()->model.delete("0/1"));
+ }
+
+ @Test void j11_delete_parentMissing_404() {
+ // DELETE where the parent URL resolves to a missing key
(o==null, Object meta) -> 404.
+ var model = PathTraversal.create(new Json5Map());
+ assertThrowsWithMessage(PathTraversalException.class, "Node at
URL 'missing' not found.", ()->model.delete("missing/x"));
+ }
+
+ @Test void j12_delete_parentNotFound_404() {
+ // DELETE where the parent URL resolves through a null
intermediate -> getNode returns null -> 404.
+ var m = new HashMap<String,Object>();
+ m.put("a", null);
+ var model = PathTraversal.create(m);
+ assertThrowsWithMessage(PathTraversalException.class, "Node at
URL 'a/b' not found.", ()->model.delete("a/b/c"));
+ }
+
//====================================================================================================
// k - getNode navigation: null o, list/array bounds, bean unknown
property
//====================================================================================================
@@ -1180,4 +1226,27 @@ class PathTraversal_Test extends TestBase {
assertEquals("editor", roles.get(0));
assertEquals("viewer", roles.get(1));
}
+
+
//====================================================================================================
+ // p - PathTraversalException
+
//====================================================================================================
+ @Test void p01_getStatus() {
+ assertEquals(404, new PathTraversalException(404,
"msg").getStatus());
+ }
+
+ @Test void p02_causeConstructor() {
+ var cause = new RuntimeException("boom");
+ var x = new PathTraversalException(cause, 500, "Failed {0}",
"x");
+ assertSame(cause, x.getCause());
+ assertEquals(500, x.getStatus());
+ assertEquals("Failed x", x.getMessage());
+ }
+
+ @Test void p03_covariantSetMessage() {
+ var x = new PathTraversalException(400, "orig");
+ // setMessage is covariantly typed to return
PathTraversalException and returns the same instance.
+ PathTraversalException y = x.setMessage("new {0}", "msg");
+ assertSame(x, y);
+ assertEquals("new msg", x.getMessage());
+ }
}