2 * Copyright (c) 2023 PANTHEON.tech, s.r.o. 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.rests.services.impl;
10 import static org.junit.jupiter.api.Assertions.assertEquals;
11 import static org.junit.jupiter.api.Assertions.assertFalse;
12 import static org.junit.jupiter.api.Assertions.assertInstanceOf;
13 import static org.junit.jupiter.api.Assertions.assertNull;
14 import static org.junit.jupiter.api.Assertions.assertTrue;
15 import static org.mockito.Mockito.doNothing;
16 import static org.mockito.Mockito.doReturn;
17 import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFalseFluentFuture;
18 import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateTrueFluentFuture;
20 import org.junit.jupiter.api.BeforeEach;
21 import org.junit.jupiter.api.Test;
22 import org.junit.jupiter.api.extension.ExtendWith;
23 import org.mockito.Mock;
24 import org.mockito.junit.jupiter.MockitoExtension;
25 import org.opendaylight.mdsal.common.api.CommitInfo;
26 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
27 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
28 import org.opendaylight.restconf.common.patch.PatchStatusContext;
29 import org.opendaylight.yangtools.yang.common.ErrorTag;
30 import org.opendaylight.yangtools.yang.common.ErrorType;
32 @ExtendWith(MockitoExtension.class)
33 class RestconfDataPatchTest extends AbstractRestconfTest {
35 private DOMDataTreeReadWriteTransaction tx;
39 doReturn(tx).when(dataBroker).newReadWriteTransaction();
43 void testPatchData() {
44 doNothing().when(tx).delete(LogicalDatastoreType.CONFIGURATION, GAP_IID);
45 doReturn(immediateFalseFluentFuture()).when(tx).exists(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID);
46 doReturn(immediateTrueFluentFuture()).when(tx).exists(LogicalDatastoreType.CONFIGURATION, GAP_IID);
47 doReturn(CommitInfo.emptyFluentFuture()).when(tx).commit();
48 doReturn(true).when(asyncResponse).resume(responseCaptor.capture());
49 restconf.dataYangJsonPATCH(stringInputStream("""
51 "ietf-yang-patch:yang-patch" : {
52 "patch-id" : "test patch id",
55 "edit-id" : "create data",
56 "operation" : "create",
57 "target" : "/example-jukebox:jukebox",
67 "edit-id" : "replace data",
68 "operation" : "replace",
69 "target" : "/example-jukebox:jukebox",
79 "edit-id" : "delete data",
80 "operation" : "delete",
81 "target" : "/example-jukebox:jukebox/player/gap"
84 }"""), asyncResponse);
85 final var response = responseCaptor.getValue();
86 assertEquals(200, response.getStatus());
87 final var status = assertInstanceOf(PatchStatusContext.class, response.getEntity());
88 assertTrue(status.ok());
89 final var edits = status.editCollection();
90 assertEquals(3, edits.size());
91 assertTrue(edits.get(0).isOk());
92 assertTrue(edits.get(1).isOk());
93 assertTrue(edits.get(2).isOk());
97 void testPatchDataDeleteNotExist() {
98 doNothing().when(tx).delete(LogicalDatastoreType.CONFIGURATION, GAP_IID);
99 doReturn(immediateFalseFluentFuture()).when(tx).exists(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID);
100 doReturn(immediateFalseFluentFuture()).when(tx).exists(LogicalDatastoreType.CONFIGURATION, GAP_IID);
101 doReturn(true).when(tx).cancel();
103 doReturn(true).when(asyncResponse).resume(responseCaptor.capture());
104 restconf.dataYangJsonPATCH(stringInputStream("""
106 "ietf-yang-patch:yang-patch" : {
107 "patch-id" : "test patch id",
110 "edit-id" : "create data",
111 "operation" : "create",
112 "target" : "/example-jukebox:jukebox",
122 "edit-id" : "remove data",
123 "operation" : "remove",
124 "target" : "/example-jukebox:jukebox/player/gap"
127 "edit-id" : "delete data",
128 "operation" : "delete",
129 "target" : "/example-jukebox:jukebox/player/gap"
133 }"""), asyncResponse);
134 final var response = responseCaptor.getValue();
135 assertEquals(409, response.getStatus());
136 final var status = assertInstanceOf(PatchStatusContext.class, response.getEntity());
137 assertFalse(status.ok());
138 final var edits = status.editCollection();
139 assertEquals(3, edits.size());
140 assertTrue(edits.get(0).isOk());
141 assertTrue(edits.get(1).isOk());
142 final var edit = edits.get(2);
143 assertFalse(edit.isOk());
144 final var errors = edit.getEditErrors();
145 assertEquals(1, errors.size());
146 final var error = errors.get(0);
147 assertEquals("Data does not exist", error.getErrorMessage());
148 assertEquals(ErrorType.PROTOCOL, error.getErrorType());
149 assertEquals(ErrorTag.DATA_MISSING, error.getErrorTag());
150 assertEquals(GAP_IID, error.getErrorPath());
154 void testPatchDataMountPoint() throws Exception {
155 doNothing().when(tx).delete(LogicalDatastoreType.CONFIGURATION, GAP_IID);
156 doReturn(immediateFalseFluentFuture()).when(tx).exists(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID);
157 doReturn(immediateTrueFluentFuture()).when(tx).exists(LogicalDatastoreType.CONFIGURATION, GAP_IID);
158 doReturn(CommitInfo.emptyFluentFuture()).when(tx).commit();
160 doReturn(true).when(asyncResponse).resume(responseCaptor.capture());
161 restconf.dataYangXmlPATCH(stringInputStream("""
162 <yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
163 <patch-id>test patch id</patch-id>
165 <edit-id>create data</edit-id>
166 <operation>create</operation>
167 <target>/example-jukebox:jukebox</target>
169 <jukebox xmlns="http://example.com/ns/example-jukebox">
177 <edit-id>replace data</edit-id>
178 <operation>replace</operation>
179 <target>/example-jukebox:jukebox</target>
181 <jukebox xmlns="http://example.com/ns/example-jukebox">
189 <edit-id>delete data</edit-id>
190 <operation>delete</operation>
191 <target>/example-jukebox:jukebox/player/gap</target>
193 </yang-patch>"""), asyncResponse);
194 final var response = responseCaptor.getValue();
195 assertEquals(200, response.getStatus());
196 final var status = assertInstanceOf(PatchStatusContext.class, response.getEntity());
197 assertTrue(status.ok());
198 assertNull(status.globalErrors());
199 final var edits = status.editCollection();
200 assertEquals(3, edits.size());
201 assertTrue(edits.get(0).isOk());
202 assertTrue(edits.get(1).isOk());
203 assertTrue(edits.get(2).isOk());