blob: a00edc8be143a6db7869c84c554fd5510e900fc7 [file] [log] [blame]
Jian Li0c451802016-02-24 22:39:25 +09001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2016-present Open Networking Laboratory
Jian Li0c451802016-02-24 22:39:25 +09003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Jian Li8ae91202016-03-24 14:36:16 -070016package org.onosproject.rest.resources;
Jian Li0c451802016-02-24 22:39:25 +090017
18import com.eclipsesource.json.Json;
19import com.eclipsesource.json.JsonArray;
20import com.eclipsesource.json.JsonObject;
21import com.google.common.collect.ImmutableList;
22import com.google.common.collect.ImmutableSet;
23import com.google.common.collect.Sets;
Jian Lic2a542b2016-05-10 11:48:19 -070024import org.glassfish.jersey.client.ClientProperties;
Jian Li0c451802016-02-24 22:39:25 +090025import org.hamcrest.Description;
26import org.hamcrest.TypeSafeMatcher;
27import org.junit.Before;
28import org.junit.Test;
29import org.onlab.osgi.ServiceDirectory;
30import org.onlab.osgi.TestServiceDirectory;
31import org.onlab.rest.BaseResource;
32import org.onosproject.cluster.NodeId;
33import org.onosproject.codec.CodecService;
34import org.onosproject.codec.impl.CodecManager;
35import org.onosproject.net.DeviceId;
36import org.onosproject.net.region.Region;
37import org.onosproject.net.region.RegionAdminService;
38import org.onosproject.net.region.RegionId;
39import org.onosproject.net.region.RegionService;
Jian Li0c451802016-02-24 22:39:25 +090040
Jian Li9d616492016-03-09 10:52:49 -080041import javax.ws.rs.client.Entity;
42import javax.ws.rs.client.WebTarget;
Jian Li0c451802016-02-24 22:39:25 +090043import javax.ws.rs.core.MediaType;
Jian Li9d616492016-03-09 10:52:49 -080044import javax.ws.rs.core.Response;
Jian Li0c451802016-02-24 22:39:25 +090045import java.io.InputStream;
46import java.net.HttpURLConnection;
47import java.util.List;
48import java.util.Set;
49
50import static org.easymock.EasyMock.anyObject;
51import static org.easymock.EasyMock.createMock;
52import static org.easymock.EasyMock.expect;
53import static org.easymock.EasyMock.expectLastCall;
54import static org.easymock.EasyMock.replay;
55import static org.easymock.EasyMock.verify;
56import static org.hamcrest.Matchers.hasSize;
57import static org.hamcrest.Matchers.is;
58import static org.hamcrest.Matchers.notNullValue;
59import static org.junit.Assert.assertThat;
60
61/**
62 * Unit tests for region REST APIs.
63 */
64public class RegionsResourceTest extends ResourceTest {
65
66 final RegionService mockRegionService = createMock(RegionService.class);
67 final RegionAdminService mockRegionAdminService = createMock(RegionAdminService.class);
68
69 final RegionId regionId1 = RegionId.regionId("1");
70 final RegionId regionId2 = RegionId.regionId("2");
71 final RegionId regionId3 = RegionId.regionId("3");
72
73 final MockRegion region1 = new MockRegion(regionId1, "r1", Region.Type.RACK);
74 final MockRegion region2 = new MockRegion(regionId2, "r2", Region.Type.ROOM);
75 final MockRegion region3 = new MockRegion(regionId3, "r3", Region.Type.CAMPUS);
76
Jian Li0c451802016-02-24 22:39:25 +090077 /**
78 * Mock class for a region.
79 */
80 private static class MockRegion implements Region {
81
82 private final RegionId id;
83 private final String name;
84 private final Type type;
85 private final List<Set<NodeId>> masters;
86
87 public MockRegion(RegionId id, String name, Type type) {
88 this.id = id;
89 this.name = name;
90 this.type = type;
91
92 final NodeId nodeId1 = NodeId.nodeId("1");
93 final NodeId nodeId2 = NodeId.nodeId("2");
94 final NodeId nodeId3 = NodeId.nodeId("3");
95 final NodeId nodeId4 = NodeId.nodeId("4");
96
97 Set<NodeId> nodeIds1 = ImmutableSet.of(nodeId1);
98 Set<NodeId> nodeIds2 = ImmutableSet.of(nodeId1, nodeId2);
99 Set<NodeId> nodeIds3 = ImmutableSet.of(nodeId1, nodeId2, nodeId3);
100 Set<NodeId> nodeIds4 = ImmutableSet.of(nodeId1, nodeId2, nodeId3, nodeId4);
101
102 this.masters = ImmutableList.of(nodeIds1, nodeIds2, nodeIds3, nodeIds4);
103 }
104
105 @Override
106 public RegionId id() {
107 return this.id;
108 }
109
110 @Override
111 public String name() {
112 return this.name;
113 }
114
115 @Override
116 public Type type() {
117 return this.type;
118 }
119
120 @Override
121 public List<Set<NodeId>> masters() {
122 return this.masters;
123 }
124 }
125
126 /**
127 * Sets up the global values for all the tests.
128 */
129 @Before
130 public void setupTest() {
131 final CodecManager codecService = new CodecManager();
132 codecService.activate();
133 ServiceDirectory testDirectory =
134 new TestServiceDirectory()
135 .add(RegionService.class, mockRegionService)
136 .add(RegionAdminService.class, mockRegionAdminService)
137 .add(CodecService.class, codecService);
138 BaseResource.setServiceDirectory(testDirectory);
139 }
140
141 /**
Jian Li7011bdd2016-03-23 16:05:53 -0700142 * Hamcrest matcher to check that a region representation in JSON matches
143 * the actual region.
Jian Li0c451802016-02-24 22:39:25 +0900144 */
145 public static class RegionJsonMatcher extends TypeSafeMatcher<JsonObject> {
146 private final Region region;
147 private String reason = "";
148
149 public RegionJsonMatcher(Region regionValue) {
150 this.region = regionValue;
151 }
152
153 @Override
154 protected boolean matchesSafely(JsonObject jsonRegion) {
155
156 // check id
157 String jsonRegionId = jsonRegion.get("id").asString();
158 String regionId = region.id().toString();
159 if (!jsonRegionId.equals(regionId)) {
160 reason = "region id was " + jsonRegionId;
161 return false;
162 }
163
164 // check type
165 String jsonType = jsonRegion.get("type").asString();
166 String type = region.type().toString();
167 if (!jsonType.equals(type)) {
168 reason = "type was " + jsonType;
169 return false;
170 }
171
172 // check name
173 String jsonName = jsonRegion.get("name").asString();
174 String name = region.name();
175 if (!jsonName.equals(name)) {
176 reason = "name was " + jsonName;
177 return false;
178 }
179
180 // check size of master array
181 JsonArray jsonMasters = jsonRegion.get("masters").asArray();
182 if (jsonMasters.size() != region.masters().size()) {
183 reason = "masters size was " + jsonMasters.size();
184 return false;
185 }
186
187 // check master
188 for (Set<NodeId> set : region.masters()) {
189 boolean masterFound = false;
190 for (int masterIndex = 0; masterIndex < jsonMasters.size(); masterIndex++) {
191 masterFound = checkEquality(jsonMasters.get(masterIndex).asArray(), set);
192 }
193
194 if (!masterFound) {
195 reason = "master not found " + set.toString();
196 return false;
197 }
198 }
199
200 return true;
201 }
202
203 @Override
204 public void describeTo(Description description) {
205 description.appendText(reason);
206 }
207
208 private Set<NodeId> jsonToSet(JsonArray nodes) {
209 final Set<NodeId> nodeIds = Sets.newHashSet();
210 nodes.forEach(node -> nodeIds.add(NodeId.nodeId(node.asString())));
211 return nodeIds;
212 }
213
214 private boolean checkEquality(JsonArray nodes, Set<NodeId> nodeIds) {
215 Set<NodeId> jsonSet = jsonToSet(nodes);
216 if (jsonSet.size() == nodes.size()) {
217 return jsonSet.containsAll(nodeIds);
218 }
219 return false;
220 }
221 }
222
223 private static RegionJsonMatcher matchesRegion(Region region) {
224 return new RegionJsonMatcher(region);
225 }
226
227 /**
228 * Hamcrest matcher to check that a region is represented properly in a JSON
229 * array of regions.
230 */
231 public static class RegionJsonArrayMatcher extends TypeSafeMatcher<JsonArray> {
232 private final Region region;
233 private String reason = "";
234
235 public RegionJsonArrayMatcher(Region regionValue) {
236 this.region = regionValue;
237 }
238
239 @Override
240 protected boolean matchesSafely(JsonArray json) {
241 boolean regionFound = false;
242 for (int jsonRegionIndex = 0; jsonRegionIndex < json.size(); jsonRegionIndex++) {
243 final JsonObject jsonRegion = json.get(jsonRegionIndex).asObject();
244
245 final String regionId = region.id().toString();
246 final String jsonRegionId = jsonRegion.get("id").asString();
247 if (jsonRegionId.equals(regionId)) {
248 regionFound = true;
249 assertThat(jsonRegion, matchesRegion(region));
250 }
251 }
252
253 if (!regionFound) {
254 reason = "Region with id " + region.id().toString() + " not found";
255 return false;
256 } else {
257 return true;
258 }
259 }
260
261 @Override
262 public void describeTo(Description description) {
263 description.appendText(reason);
264 }
265 }
266
267 /**
268 * Factory to allocate a region array matcher.
269 *
270 * @param region region object we are looking for
271 * @return matcher
272 */
273 private static RegionJsonArrayMatcher hasRegion(Region region) {
274 return new RegionJsonArrayMatcher(region);
275 }
276
277 @Test
278 public void testRegionEmptyArray() {
279 expect(mockRegionService.getRegions()).andReturn(ImmutableSet.of()).anyTimes();
280 replay((mockRegionService));
Jian Li9d616492016-03-09 10:52:49 -0800281 final WebTarget wt = target();
282 final String response = wt.path("regions").request().get(String.class);
Jian Li0c451802016-02-24 22:39:25 +0900283 assertThat(response, is("{\"regions\":[]}"));
284
285 verify(mockRegionService);
286 }
287
288 /**
289 * Tests the results of the REST API GET when there are active regions.
290 */
291 @Test
292 public void testRegionsPopulatedArray() {
293 final Set<Region> regions = ImmutableSet.of(region1, region2, region3);
294 expect(mockRegionService.getRegions()).andReturn(regions).anyTimes();
295 replay(mockRegionService);
296
Jian Li9d616492016-03-09 10:52:49 -0800297 final WebTarget wt = target();
298 final String response = wt.path("regions").request().get(String.class);
Jian Li0c451802016-02-24 22:39:25 +0900299 final JsonObject result = Json.parse(response).asObject();
300 assertThat(result, notNullValue());
301
302 assertThat(result.names(), hasSize(1));
303 assertThat(result.names().get(0), is("regions"));
304 final JsonArray jsonRegions = result.get("regions").asArray();
305 assertThat(jsonRegions, notNullValue());
306 assertThat(jsonRegions, hasRegion(region1));
307 assertThat(jsonRegions, hasRegion(region2));
308 assertThat(jsonRegions, hasRegion(region3));
309
310 verify(mockRegionService);
311
312 }
313
314 /**
315 * Tests the result of a REST API GET for a region with region id.
316 */
317 @Test
318 public void testGetRegionById() {
319 expect(mockRegionService.getRegion(anyObject())).andReturn(region1).anyTimes();
320 replay(mockRegionService);
321
Jian Li9d616492016-03-09 10:52:49 -0800322 final WebTarget wt = target();
323 final String response = wt.path("regions/" + regionId1.toString()).request().get(String.class);
Jian Li0c451802016-02-24 22:39:25 +0900324 final JsonObject result = Json.parse(response).asObject();
325 assertThat(result, notNullValue());
326 assertThat(result, matchesRegion(region1));
327
328 verify(mockRegionService);
329 }
330
331 /**
332 * Tests creating a region with POST.
333 */
334 @Test
335 public void testRegionPost() {
336 mockRegionAdminService.createRegion(anyObject(), anyObject(),
337 anyObject(), anyObject());
338 expectLastCall().andReturn(region2).anyTimes();
339 replay(mockRegionAdminService);
340
Jian Li9d616492016-03-09 10:52:49 -0800341 WebTarget wt = target();
Jian Li7011bdd2016-03-23 16:05:53 -0700342 InputStream jsonStream = RegionsResourceTest.class
Jian Li0c451802016-02-24 22:39:25 +0900343 .getResourceAsStream("post-region.json");
344
Jian Li9d616492016-03-09 10:52:49 -0800345 Response response = wt.path("regions")
346 .request(MediaType.APPLICATION_JSON_TYPE)
347 .post(Entity.json(jsonStream));
Jian Li0c451802016-02-24 22:39:25 +0900348 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
349
350 verify(mockRegionAdminService);
351 }
352
353 /**
354 * Tests updating a region with PUT.
355 */
356 @Test
357 public void testRegionPut() {
358 mockRegionAdminService.updateRegion(anyObject(), anyObject(),
359 anyObject(), anyObject());
360 expectLastCall().andReturn(region1).anyTimes();
361 replay(mockRegionAdminService);
362
Jian Li9d616492016-03-09 10:52:49 -0800363 WebTarget wt = target();
Jian Li7011bdd2016-03-23 16:05:53 -0700364 InputStream jsonStream = RegionsResourceTest.class
Jian Li0c451802016-02-24 22:39:25 +0900365 .getResourceAsStream("post-region.json");
366
Jian Li9d616492016-03-09 10:52:49 -0800367 Response response = wt.path("regions/" + region1.id().toString())
368 .request(MediaType.APPLICATION_JSON_TYPE)
369 .put(Entity.json(jsonStream));
Jian Li0c451802016-02-24 22:39:25 +0900370 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_OK));
371
372 verify(mockRegionAdminService);
373 }
374
375 /**
376 * Tests deleting a region with DELETE.
377 */
378 @Test
379 public void testRegionDelete() {
380 mockRegionAdminService.removeRegion(anyObject());
381 expectLastCall();
382 replay(mockRegionAdminService);
383
Jian Li9d616492016-03-09 10:52:49 -0800384 WebTarget wt = target();
385 Response response = wt.path("regions/" + region1.id().toString())
386 .request().delete();
Jian Lic2a542b2016-05-10 11:48:19 -0700387 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_NO_CONTENT));
Jian Li0c451802016-02-24 22:39:25 +0900388
389 verify(mockRegionAdminService);
390 }
391
392 /**
393 * Tests retrieving device ids that are associated with the given region.
394 */
395 @Test
396 public void testGetRegionDevices() {
397 final DeviceId deviceId1 = DeviceId.deviceId("1");
398 final DeviceId deviceId2 = DeviceId.deviceId("2");
399 final DeviceId deviceId3 = DeviceId.deviceId("3");
400
401 final Set<DeviceId> deviceIds = ImmutableSet.of(deviceId1, deviceId2, deviceId3);
402
403 expect(mockRegionService.getRegionDevices(anyObject()))
404 .andReturn(deviceIds).anyTimes();
405 replay(mockRegionService);
406
Jian Li9d616492016-03-09 10:52:49 -0800407 final WebTarget wt = target();
408 final String response = wt.path("regions/" +
409 region1.id().toString() + "/devices").request().get(String.class);
Jian Li0c451802016-02-24 22:39:25 +0900410 final JsonObject result = Json.parse(response).asObject();
411 assertThat(result, notNullValue());
412
413 assertThat(result.names(), hasSize(1));
414 assertThat(result.names().get(0), is("deviceIds"));
415 final JsonArray jsonDeviceIds = result.get("deviceIds").asArray();
416 assertThat(jsonDeviceIds.size(), is(3));
417 assertThat(jsonDeviceIds.get(0).asString(), is("1"));
418 assertThat(jsonDeviceIds.get(1).asString(), is("2"));
419 assertThat(jsonDeviceIds.get(2).asString(), is("3"));
420
421 verify(mockRegionService);
422 }
423
424 /**
Jayasree Ghoshfc72e2e2016-11-08 19:55:47 +0530425 * Tests creating a flow with POST.
426 */
427 @Test
428 public void testAddDevicesPostWithoutRegion() {
429 expect(mockRegionService.getRegion(anyObject())).andReturn(null).anyTimes();
430 replay(mockRegionService);
431
432 WebTarget wt = target();
433 InputStream jsonStream = RegionsResourceTest.class
434 .getResourceAsStream("region-deviceIds.json");
435
436 Response response = wt.path("regions/" + region1.id() + "/devices")
437 .request(MediaType.APPLICATION_JSON_TYPE)
438 .post(Entity.json(jsonStream));
439 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_NOT_FOUND));
440
441 verify(mockRegionService);
442 }
443
444 /**
Jian Li0c451802016-02-24 22:39:25 +0900445 * Tests adding a set of devices in region with POST.
446 */
447 @Test
448 public void testAddDevicesPost() {
449 mockRegionAdminService.addDevices(anyObject(), anyObject());
450 expectLastCall();
451 replay(mockRegionAdminService);
452
Jayasree Ghoshfc72e2e2016-11-08 19:55:47 +0530453 expect(mockRegionService.getRegion(anyObject())).andReturn(region1).anyTimes();
454 replay(mockRegionService);
455
Jian Li9d616492016-03-09 10:52:49 -0800456 WebTarget wt = target();
Jian Li7011bdd2016-03-23 16:05:53 -0700457 InputStream jsonStream = RegionsResourceTest.class
Jian Li0c451802016-02-24 22:39:25 +0900458 .getResourceAsStream("region-deviceIds.json");
459
Jayasree Ghoshfc72e2e2016-11-08 19:55:47 +0530460 Response response = wt.path("regions/" + region1.id().toString() + "/devices")
Jian Li9d616492016-03-09 10:52:49 -0800461 .request(MediaType.APPLICATION_JSON_TYPE)
462 .post(Entity.json(jsonStream));
Jian Li0c451802016-02-24 22:39:25 +0900463 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
464
465 verify(mockRegionAdminService);
466 }
467
468 /**
469 * Tests deleting a set of devices contained in the given region with DELETE.
470 */
471 @Test
472 public void testRemoveDevicesDelete() {
473 mockRegionAdminService.removeDevices(anyObject(), anyObject());
474 expectLastCall();
475 replay(mockRegionAdminService);
476
Jayasree Ghoshfc72e2e2016-11-08 19:55:47 +0530477 expect(mockRegionService.getRegion(anyObject())).andReturn(region1).anyTimes();
478 replay(mockRegionService);
Jian Li9d616492016-03-09 10:52:49 -0800479
Jian Lic2a542b2016-05-10 11:48:19 -0700480 WebTarget wt = target()
481 .property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true);
Jian Li7011bdd2016-03-23 16:05:53 -0700482 InputStream jsonStream = RegionsResourceTest.class
Jian Li0c451802016-02-24 22:39:25 +0900483 .getResourceAsStream("region-deviceIds.json");
484
Jian Li9d616492016-03-09 10:52:49 -0800485 // FIXME: need to consider whether to use jsonStream for entry deletion
Jayasree Ghoshfc72e2e2016-11-08 19:55:47 +0530486 Response response = wt.path("regions/" + region1.id().toString() + "/devices")
Jian Lic2a542b2016-05-10 11:48:19 -0700487 .request().method("DELETE", Entity.json(jsonStream));
488 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_NO_CONTENT));
489 verify(mockRegionAdminService);
Jian Li0c451802016-02-24 22:39:25 +0900490 }
491}