2 * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.restconf.nb.rfc8040.jersey.providers.patch;
10 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
11 import static org.junit.Assert.assertEquals;
12 import static org.junit.Assert.assertThrows;
14 import java.io.ByteArrayInputStream;
15 import java.io.InputStream;
16 import java.nio.charset.StandardCharsets;
17 import javax.ws.rs.core.MediaType;
18 import org.junit.BeforeClass;
19 import org.junit.Test;
20 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
21 import org.opendaylight.restconf.common.patch.PatchContext;
22 import org.opendaylight.restconf.nb.rfc8040.jersey.providers.test.AbstractBodyReaderTest;
23 import org.opendaylight.restconf.nb.rfc8040.jersey.providers.test.JsonBodyReaderTest;
24 import org.opendaylight.yangtools.yang.common.ErrorTag;
25 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
26 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
27 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
28 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
29 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
30 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
32 public class JsonPatchBodyReaderTest extends AbstractBodyReaderTest {
34 private final JsonPatchBodyReader jsonToPatchBodyReader;
35 private static EffectiveModelContext schemaContext;
37 public JsonPatchBodyReaderTest() throws Exception {
39 jsonToPatchBodyReader = new JsonPatchBodyReader(databindProvider, mountPointService);
43 protected MediaType getMediaType() {
44 return new MediaType(APPLICATION_JSON, null);
48 public static void initialization() {
49 schemaContext = schemaContextLoader("/instanceidentifier/yang", schemaContext);
53 public void modulePatchDataTest() throws Exception {
54 final String uri = "instance-identifier-patch-module:patch-cont/my-list1=leaf1";
55 mockBodyReader(uri, jsonToPatchBodyReader, false);
57 final InputStream inputStream = JsonBodyReaderTest.class.getResourceAsStream(
58 "/instanceidentifier/json/jsonPATCHdata.json");
60 final PatchContext returnValue = jsonToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream);
61 checkPatchContext(returnValue);
65 * Test of successful Patch consisting of create and delete Patch operations.
68 public void modulePatchCreateAndDeleteTest() throws Exception {
69 final String uri = "instance-identifier-patch-module:patch-cont/my-list1=leaf1";
70 mockBodyReader(uri, jsonToPatchBodyReader, false);
72 final InputStream inputStream = JsonBodyReaderTest.class.getResourceAsStream(
73 "/instanceidentifier/json/jsonPATCHdataCreateAndDelete.json");
75 final PatchContext returnValue = jsonToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream);
76 checkPatchContext(returnValue);
80 * Test trying to use Patch create operation which requires value without value. Test should fail with
81 * {@link RestconfDocumentedException} with error code 400.
84 public void modulePatchValueMissingNegativeTest() throws Exception {
85 final String uri = "instance-identifier-patch-module:patch-cont/my-list1=leaf1";
86 mockBodyReader(uri, jsonToPatchBodyReader, false);
88 final InputStream inputStream = JsonBodyReaderTest.class.getResourceAsStream(
89 "/instanceidentifier/json/jsonPATCHdataValueMissing.json");
91 final RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class,
92 () -> jsonToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream));
93 assertEquals(ErrorTag.MALFORMED_MESSAGE, ex.getErrors().get(0).getErrorTag());
97 * Test trying to use value with Patch delete operation which does not support value. Test should fail with
98 * {@link RestconfDocumentedException} with error code 400.
101 public void modulePatchValueNotSupportedNegativeTest() throws Exception {
102 final String uri = "instance-identifier-patch-module:patch-cont/my-list1=leaf1";
103 mockBodyReader(uri, jsonToPatchBodyReader, false);
105 final InputStream inputStream = JsonBodyReaderTest.class.getResourceAsStream(
106 "/instanceidentifier/json/jsonPATCHdataValueNotSupported.json");
108 final RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class,
109 () -> jsonToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream));
110 assertEquals(ErrorTag.MALFORMED_MESSAGE, ex.getErrors().get(0).getErrorTag());
114 * Test using Patch when target is completely specified in request URI and thus target leaf contains only '/' sign.
117 public void modulePatchCompleteTargetInURITest() throws Exception {
118 final String uri = "instance-identifier-patch-module:patch-cont";
119 mockBodyReader(uri, jsonToPatchBodyReader, false);
121 final InputStream inputStream = JsonBodyReaderTest.class.getResourceAsStream(
122 "/instanceidentifier/json/jsonPATCHdataCompleteTargetInURI.json");
124 final PatchContext returnValue = jsonToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream);
125 checkPatchContext(returnValue);
129 * Test of Yang Patch merge operation on list. Test consists of two edit operations - replace and merge.
132 public void modulePatchMergeOperationOnListTest() throws Exception {
133 final String uri = "instance-identifier-patch-module:patch-cont/my-list1=leaf1";
134 mockBodyReader(uri, jsonToPatchBodyReader, false);
136 final InputStream inputStream = JsonBodyReaderTest.class.getResourceAsStream(
137 "/instanceidentifier/json/jsonPATCHMergeOperationOnList.json");
139 final PatchContext returnValue = jsonToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream);
140 checkPatchContext(returnValue);
144 * Test of Yang Patch merge operation on container. Test consists of two edit operations - create and merge.
147 public void modulePatchMergeOperationOnContainerTest() throws Exception {
148 final String uri = "instance-identifier-patch-module:patch-cont";
149 mockBodyReader(uri, jsonToPatchBodyReader, false);
151 final InputStream inputStream = JsonBodyReaderTest.class.getResourceAsStream(
152 "/instanceidentifier/json/jsonPATCHMergeOperationOnContainer.json");
154 final PatchContext returnValue = jsonToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream);
155 checkPatchContext(returnValue);
159 * Test reading simple leaf value.
162 public void modulePatchSimpleLeafValueTest() throws Exception {
163 final String uri = "instance-identifier-patch-module:patch-cont/my-list1=leaf1";
164 mockBodyReader(uri, jsonToPatchBodyReader, false);
166 final InputStream inputStream = JsonBodyReaderTest.class.getResourceAsStream(
167 "/instanceidentifier/json/jsonPATCHSimpleLeafValue.json");
169 final PatchContext returnValue = jsonToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream);
170 checkPatchContext(returnValue);
174 * Test of Yang Patch on the top-level container with empty URI for data root.
177 public void modulePatchTargetTopLevelContainerWithEmptyURITest() throws Exception {
178 final String uri = "";
179 mockBodyReader(uri, jsonToPatchBodyReader, false);
181 final InputStream inputStream = JsonBodyReaderTest.class.getResourceAsStream(
182 "/instanceidentifier/json/jsonPATCHTargetTopLevelContainerWithEmptyURI.json");
184 final PatchContext returnValue = jsonToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream);
185 checkPatchContext(returnValue);
189 * Test of Yang Patch on the system map node element.
192 public void modulePatchTargetMapNodeTest() throws Exception {
193 mockBodyReader("", jsonToPatchBodyReader, false);
194 final var inputStream = new ByteArrayInputStream("""
196 "ietf-yang-patch:yang-patch": {
197 "patch-id": "map-patch",
198 "comment": "comment",
202 "operation": "replace",
203 "target": "/map-model:cont-root/map-model:cont1/map-model:my-map=key",
214 """.getBytes(StandardCharsets.UTF_8));
215 final var expectedData = Builders.mapBuilder()
216 .withNodeIdentifier(new NodeIdentifier(MAP_CONT_QNAME))
217 .withChild(Builders.mapEntryBuilder()
218 .withNodeIdentifier(NodeIdentifierWithPredicates.of(MAP_CONT_QNAME, KEY_LEAF_QNAME, "key"))
219 .withChild(ImmutableNodes.leafNode(KEY_LEAF_QNAME, "key"))
220 .withChild(ImmutableNodes.leafNode(DATA_LEAF_QNAME, "data"))
223 final var returnValue = jsonToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream);
224 checkPatchContext(returnValue);
225 final var data = returnValue.getData().get(0).getNode();
226 assertEquals(MAP_CONT_QNAME, data.getIdentifier().getNodeType());
227 assertEquals(expectedData, data);
231 * Test of Yang Patch on the leaf set node element.
234 public void modulePatchTargetLeafSetNodeTest() throws Exception {
235 mockBodyReader("", jsonToPatchBodyReader, false);
236 final var inputStream = new ByteArrayInputStream("""
238 "ietf-yang-patch:yang-patch": {
239 "patch-id": "set-patch",
240 "comment": "comment",
244 "operation": "replace",
245 "target": "/set-model:cont-root/set-model:cont1/set-model:my-set=data1",
247 "my-set": [ "data1" ]
253 """.getBytes(StandardCharsets.UTF_8));
254 final var expectedData = Builders.leafSetBuilder()
255 .withNodeIdentifier(new NodeIdentifier(LEAF_SET_QNAME))
256 .withChild(Builders.leafSetEntryBuilder()
257 .withNodeIdentifier(new NodeWithValue(LEAF_SET_QNAME, "data1"))
261 final var returnValue = jsonToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream);
262 checkPatchContext(returnValue);
263 final var data = returnValue.getData().get(0).getNode();
264 assertEquals(LEAF_SET_QNAME, data.getIdentifier().getNodeType());
265 assertEquals(expectedData, data);
269 * Test of Yang Patch on the unkeyed list node element.
272 public void modulePatchTargetUnkeyedListNodeTest() throws Exception {
273 mockBodyReader("", jsonToPatchBodyReader, false);
274 final var inputStream = new ByteArrayInputStream("""
276 "ietf-yang-patch:yang-patch": {
277 "patch-id": "list-patch",
278 "comment": "comment",
282 "operation": "replace",
283 "target": "/list-model:cont-root/list-model:cont1/list-model:unkeyed-list",
294 """.getBytes(StandardCharsets.UTF_8));
295 final var expectedData = Builders.unkeyedListBuilder()
296 .withNodeIdentifier(new NodeIdentifier(LIST_QNAME))
297 .withChild(Builders.unkeyedListEntryBuilder()
298 .withNodeIdentifier(new NodeIdentifier(LIST_QNAME))
299 .withChild(ImmutableNodes.leafNode(LIST_LEAF1_QNAME, "data1"))
300 .withChild(ImmutableNodes.leafNode(LIST_LEAF2_QNAME, "data2"))
303 final var returnValue = jsonToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream);
304 checkPatchContext(returnValue);
305 final var data = returnValue.getData().get(0).getNode();
306 assertEquals(LIST_QNAME, data.getIdentifier().getNodeType());
307 assertEquals(expectedData, data);
311 * Test of Yang Patch on the case node element.
314 public void modulePatchTargetCaseNodeTest() throws Exception {
315 mockBodyReader("", jsonToPatchBodyReader, false);
316 final var inputStream = new ByteArrayInputStream("""
318 "ietf-yang-patch:yang-patch": {
319 "patch-id": "choice-patch",
320 "comment": "comment",
324 "operation": "replace",
325 "target": "/choice-model:cont-root/choice-model:cont1/choice-model:case-cont1",
335 """.getBytes(StandardCharsets.UTF_8));
336 final var expectedData = Builders.containerBuilder()
337 .withNodeIdentifier(new NodeIdentifier(CHOICE_CONT_QNAME))
338 .withChild(ImmutableNodes.leafNode(CASE_LEAF1_QNAME, "data"))
340 final var returnValue = jsonToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream);
341 checkPatchContext(returnValue);
342 final var data = returnValue.getData().get(0).getNode();
343 assertEquals(CHOICE_CONT_QNAME, data.getIdentifier().getNodeType());
344 assertEquals(expectedData, data);