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.rests.services.impl;
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertThrows;
12 import static org.mockito.ArgumentMatchers.any;
13 import static org.mockito.Mockito.doReturn;
14 import static org.mockito.Mockito.mock;
16 import com.google.common.collect.ImmutableClassToInstanceMap;
17 import java.io.FileNotFoundException;
19 import java.net.URISyntaxException;
21 import javax.ws.rs.core.MultivaluedHashMap;
22 import javax.ws.rs.core.UriInfo;
23 import org.junit.AfterClass;
24 import org.junit.Before;
25 import org.junit.BeforeClass;
26 import org.junit.Test;
27 import org.junit.runner.RunWith;
28 import org.mockito.Mock;
29 import org.mockito.junit.MockitoJUnitRunner;
30 import org.opendaylight.mdsal.common.api.CommitInfo;
31 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
32 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeService;
33 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
34 import org.opendaylight.mdsal.dom.api.DOMNotificationService;
35 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
36 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
37 import org.opendaylight.restconf.nb.rfc8040.TestRestconfUtils;
38 import org.opendaylight.restconf.nb.rfc8040.handlers.SchemaContextHandler;
39 import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload;
40 import org.opendaylight.restconf.nb.rfc8040.streams.Configuration;
41 import org.opendaylight.restconf.nb.rfc8040.streams.listeners.ListenerAdapter;
42 import org.opendaylight.restconf.nb.rfc8040.streams.listeners.ListenersBroker;
43 import org.opendaylight.restconf.nb.rfc8040.utils.RestconfConstants;
44 import org.opendaylight.restconf.nb.rfc8040.utils.parser.IdentifierCodec;
45 import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
46 import org.opendaylight.yangtools.concepts.ListenerRegistration;
47 import org.opendaylight.yangtools.yang.common.ErrorTag;
48 import org.opendaylight.yangtools.yang.common.ErrorType;
49 import org.opendaylight.yangtools.yang.common.QName;
50 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
51 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
52 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
54 @RunWith(MockitoJUnitRunner.StrictStubs.class)
55 public class RestconfStreamsSubscriptionServiceImplTest {
57 private static final String URI = "/rests/data/ietf-restconf-monitoring:restconf-state/streams/stream/"
58 + "toaster:toaster/toasterStatus/datastore=OPERATIONAL/scope=ONE";
61 private DOMDataBroker dataBroker;
63 private UriInfo uriInfo;
65 private DOMNotificationService notificationService;
67 private Configuration configurationWs;
68 private Configuration configurationSse;
70 private SchemaContextHandler schemaHandler;
73 public void setUp() throws FileNotFoundException, URISyntaxException {
74 final DOMDataTreeWriteTransaction wTx = mock(DOMDataTreeWriteTransaction.class);
75 doReturn(wTx).when(dataBroker).newWriteOnlyTransaction();
76 doReturn(CommitInfo.emptyFluentFuture()).when(wTx).commit();
78 schemaHandler = new SchemaContextHandler(dataBroker, mock(DOMSchemaService.class));
80 DOMDataTreeChangeService dataTreeChangeService = mock(DOMDataTreeChangeService.class);
81 doReturn(mock(ListenerRegistration.class)).when(dataTreeChangeService)
82 .registerDataTreeChangeListener(any(), any());
84 doReturn(ImmutableClassToInstanceMap.of(DOMDataTreeChangeService.class, dataTreeChangeService))
85 .when(dataBroker).getExtensions();
87 doReturn(new MultivaluedHashMap<>()).when(uriInfo).getQueryParameters();
88 // FIXME: just mock UriInfo here
89 doReturn(new LocalUriInfo().getBaseUriBuilder()).when(uriInfo).getBaseUriBuilder();
90 doReturn(new URI("http://127.0.0.1/" + URI)).when(uriInfo).getAbsolutePath();
91 schemaHandler.onModelContextUpdated(
92 YangParserTestUtils.parseYangFiles(TestRestconfUtils.loadFiles("/notifications")));
93 configurationWs = new Configuration(0, 100, 10, false);
94 configurationSse = new Configuration(0, 100, 10, true);
98 public static void setUpBeforeTest() {
100 "data-change-event-subscription/toaster:toaster/toasterStatus/datastore=OPERATIONAL/scope=ONE";
101 final ListenerAdapter adapter = new ListenerAdapter(YangInstanceIdentifier.create(new NodeIdentifier(
102 QName.create("http://netconfcentral.org/ns/toaster", "2009-11-20", "toaster"))),
103 name, NotificationOutputType.JSON);
104 ListenersBroker.getInstance().setDataChangeListeners(Map.of(name, adapter));
108 public static void setUpAfterTest() {
109 ListenersBroker.getInstance().setDataChangeListeners(Map.of());
113 public void testSubscribeToStreamSSE() {
114 ListenersBroker.getInstance().registerDataChangeListener(
115 IdentifierCodec.deserialize("toaster:toaster/toasterStatus", schemaHandler.get()),
116 "data-change-event-subscription/toaster:toaster/toasterStatus/datastore=OPERATIONAL/scope=ONE",
117 NotificationOutputType.XML);
118 final RestconfStreamsSubscriptionServiceImpl streamsSubscriptionService =
119 new RestconfStreamsSubscriptionServiceImpl(dataBroker, notificationService,
120 schemaHandler, configurationSse);
121 final NormalizedNodePayload response = streamsSubscriptionService.subscribeToStream(
122 "data-change-event-subscription/toaster:toaster/toasterStatus/datastore=OPERATIONAL/scope=ONE", uriInfo);
123 assertEquals("http://localhost:8181/" + RestconfConstants.BASE_URI_PATTERN
124 + "/" + RestconfConstants.NOTIF
125 + "/data-change-event-subscription/toaster:toaster/toasterStatus/"
126 + "datastore=OPERATIONAL/scope=ONE", response.getNewHeaders().get("Location").toString());
130 public void testSubscribeToStreamWS() {
131 ListenersBroker.getInstance().registerDataChangeListener(
132 IdentifierCodec.deserialize("toaster:toaster/toasterStatus", schemaHandler.get()),
133 "data-change-event-subscription/toaster:toaster/toasterStatus/datastore=OPERATIONAL/scope=ONE",
134 NotificationOutputType.XML);
135 final RestconfStreamsSubscriptionServiceImpl streamsSubscriptionService =
136 new RestconfStreamsSubscriptionServiceImpl(dataBroker, notificationService,
137 schemaHandler, configurationWs);
138 final NormalizedNodePayload response = streamsSubscriptionService.subscribeToStream(
139 "data-change-event-subscription/toaster:toaster/toasterStatus/datastore=OPERATIONAL/scope=ONE", uriInfo);
140 assertEquals("ws://localhost:8181/" + RestconfConstants.BASE_URI_PATTERN
141 + "/data-change-event-subscription/toaster:toaster/toasterStatus/"
142 + "datastore=OPERATIONAL/scope=ONE", response.getNewHeaders().get("Location").toString());
146 public void testSubscribeToStreamMissingDatastoreInPath() {
147 final RestconfStreamsSubscriptionServiceImpl streamsSubscriptionService =
148 new RestconfStreamsSubscriptionServiceImpl(dataBroker, notificationService,
149 schemaHandler, configurationWs);
150 final var errors = assertThrows(RestconfDocumentedException.class,
151 () -> streamsSubscriptionService.subscribeToStream("toaster:toaster/toasterStatus/scope=ONE", uriInfo))
153 assertEquals(1, errors.size());
154 final var error = errors.get(0);
155 assertEquals(ErrorType.APPLICATION, error.getErrorType());
156 assertEquals(ErrorTag.OPERATION_FAILED, error.getErrorTag());
157 assertEquals("Bad type of notification of sal-remote", error.getErrorMessage());
161 public void testSubscribeToStreamMissingScopeInPath() {
162 final RestconfStreamsSubscriptionServiceImpl streamsSubscriptionService =
163 new RestconfStreamsSubscriptionServiceImpl(dataBroker, notificationService,
164 schemaHandler, configurationWs);
165 final var errors = assertThrows(RestconfDocumentedException.class,
166 () -> streamsSubscriptionService.subscribeToStream("toaster:toaster/toasterStatus/datastore=OPERATIONAL",
167 uriInfo)).getErrors();
168 assertEquals(1, errors.size());
169 final var error = errors.get(0);
170 assertEquals(ErrorType.APPLICATION, error.getErrorType());
171 assertEquals(ErrorTag.OPERATION_FAILED, error.getErrorTag());
172 assertEquals("Bad type of notification of sal-remote", error.getErrorMessage());