blob: 69894e44a483f7efcfa206d31fb50fca7b36ccbf [file] [log] [blame]
Jian Li0c451802016-02-24 22:39:25 +09001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
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;
Jian Li0c451802016-02-24 22:39:25 +090031import org.onosproject.cluster.NodeId;
32import org.onosproject.codec.CodecService;
33import org.onosproject.codec.impl.CodecManager;
Simon Hunt53612212016-12-04 17:19:52 -080034import org.onosproject.net.Annotations;
35import org.onosproject.net.DefaultAnnotations;
Jian Li0c451802016-02-24 22:39:25 +090036import org.onosproject.net.DeviceId;
37import org.onosproject.net.region.Region;
38import org.onosproject.net.region.RegionAdminService;
39import org.onosproject.net.region.RegionId;
40import org.onosproject.net.region.RegionService;
Jian Li0c451802016-02-24 22:39:25 +090041
Jian Li9d616492016-03-09 10:52:49 -080042import javax.ws.rs.client.Entity;
43import javax.ws.rs.client.WebTarget;
Jian Li0c451802016-02-24 22:39:25 +090044import javax.ws.rs.core.MediaType;
Jian Li9d616492016-03-09 10:52:49 -080045import javax.ws.rs.core.Response;
Jian Li0c451802016-02-24 22:39:25 +090046import java.io.InputStream;
47import java.net.HttpURLConnection;
48import java.util.List;
49import java.util.Set;
50
51import static org.easymock.EasyMock.anyObject;
52import static org.easymock.EasyMock.createMock;
53import static org.easymock.EasyMock.expect;
54import static org.easymock.EasyMock.expectLastCall;
55import static org.easymock.EasyMock.replay;
56import static org.easymock.EasyMock.verify;
57import static org.hamcrest.Matchers.hasSize;
58import static org.hamcrest.Matchers.is;
59import static org.hamcrest.Matchers.notNullValue;
60import static org.junit.Assert.assertThat;
61
62/**
63 * Unit tests for region REST APIs.
64 */
65public class RegionsResourceTest extends ResourceTest {
66
67 final RegionService mockRegionService = createMock(RegionService.class);
68 final RegionAdminService mockRegionAdminService = createMock(RegionAdminService.class);
69
70 final RegionId regionId1 = RegionId.regionId("1");
71 final RegionId regionId2 = RegionId.regionId("2");
72 final RegionId regionId3 = RegionId.regionId("3");
73
74 final MockRegion region1 = new MockRegion(regionId1, "r1", Region.Type.RACK);
75 final MockRegion region2 = new MockRegion(regionId2, "r2", Region.Type.ROOM);
76 final MockRegion region3 = new MockRegion(regionId3, "r3", Region.Type.CAMPUS);
77
Jian Li0c451802016-02-24 22:39:25 +090078 /**
79 * Mock class for a region.
80 */
81 private static class MockRegion implements Region {
82
83 private final RegionId id;
84 private final String name;
85 private final Type type;
86 private final List<Set<NodeId>> masters;
87
88 public MockRegion(RegionId id, String name, Type type) {
89 this.id = id;
90 this.name = name;
91 this.type = type;
92
93 final NodeId nodeId1 = NodeId.nodeId("1");
94 final NodeId nodeId2 = NodeId.nodeId("2");
95 final NodeId nodeId3 = NodeId.nodeId("3");
96 final NodeId nodeId4 = NodeId.nodeId("4");
97
98 Set<NodeId> nodeIds1 = ImmutableSet.of(nodeId1);
99 Set<NodeId> nodeIds2 = ImmutableSet.of(nodeId1, nodeId2);
100 Set<NodeId> nodeIds3 = ImmutableSet.of(nodeId1, nodeId2, nodeId3);
101 Set<NodeId> nodeIds4 = ImmutableSet.of(nodeId1, nodeId2, nodeId3, nodeId4);
102
103 this.masters = ImmutableList.of(nodeIds1, nodeIds2, nodeIds3, nodeIds4);
104 }
105
106 @Override
107 public RegionId id() {
108 return this.id;
109 }
110
111 @Override
112 public String name() {
113 return this.name;
114 }
115
116 @Override
117 public Type type() {
118 return this.type;
119 }
120
121 @Override
122 public List<Set<NodeId>> masters() {
123 return this.masters;
124 }
Simon Hunt53612212016-12-04 17:19:52 -0800125
126 @Override
127 public Annotations annotations() {
128 return DefaultAnnotations.EMPTY;
129 }
Jian Li0c451802016-02-24 22:39:25 +0900130 }
131
132 /**
133 * Sets up the global values for all the tests.
134 */
135 @Before
136 public void setupTest() {
137 final CodecManager codecService = new CodecManager();
138 codecService.activate();
139 ServiceDirectory testDirectory =
140 new TestServiceDirectory()
141 .add(RegionService.class, mockRegionService)
142 .add(RegionAdminService.class, mockRegionAdminService)
143 .add(CodecService.class, codecService);
Ray Milkey094a1352018-01-22 14:03:54 -0800144 setServiceDirectory(testDirectory);
Jian Li0c451802016-02-24 22:39:25 +0900145 }
146
147 /**
Jian Li7011bdd2016-03-23 16:05:53 -0700148 * Hamcrest matcher to check that a region representation in JSON matches
149 * the actual region.
Jian Li0c451802016-02-24 22:39:25 +0900150 */
151 public static class RegionJsonMatcher extends TypeSafeMatcher<JsonObject> {
152 private final Region region;
153 private String reason = "";
154
155 public RegionJsonMatcher(Region regionValue) {
156 this.region = regionValue;
157 }
158
159 @Override
160 protected boolean matchesSafely(JsonObject jsonRegion) {
161
162 // check id
163 String jsonRegionId = jsonRegion.get("id").asString();
164 String regionId = region.id().toString();
165 if (!jsonRegionId.equals(regionId)) {
166 reason = "region id was " + jsonRegionId;
167 return false;
168 }
169
170 // check type
171 String jsonType = jsonRegion.get("type").asString();
172 String type = region.type().toString();
173 if (!jsonType.equals(type)) {
174 reason = "type was " + jsonType;
175 return false;
176 }
177
178 // check name
179 String jsonName = jsonRegion.get("name").asString();
180 String name = region.name();
181 if (!jsonName.equals(name)) {
182 reason = "name was " + jsonName;
183 return false;
184 }
185
186 // check size of master array
187 JsonArray jsonMasters = jsonRegion.get("masters").asArray();
188 if (jsonMasters.size() != region.masters().size()) {
189 reason = "masters size was " + jsonMasters.size();
190 return false;
191 }
192
193 // check master
194 for (Set<NodeId> set : region.masters()) {
195 boolean masterFound = false;
196 for (int masterIndex = 0; masterIndex < jsonMasters.size(); masterIndex++) {
197 masterFound = checkEquality(jsonMasters.get(masterIndex).asArray(), set);
198 }
199
200 if (!masterFound) {
201 reason = "master not found " + set.toString();
202 return false;
203 }
204 }
205
206 return true;
207 }
208
209 @Override
210 public void describeTo(Description description) {
211 description.appendText(reason);
212 }
213
214 private Set<NodeId> jsonToSet(JsonArray nodes) {
215 final Set<NodeId> nodeIds = Sets.newHashSet();
216 nodes.forEach(node -> nodeIds.add(NodeId.nodeId(node.asString())));
217 return nodeIds;
218 }
219
220 private boolean checkEquality(JsonArray nodes, Set<NodeId> nodeIds) {
221 Set<NodeId> jsonSet = jsonToSet(nodes);
222 if (jsonSet.size() == nodes.size()) {
223 return jsonSet.containsAll(nodeIds);
224 }
225 return false;
226 }
227 }
228
229 private static RegionJsonMatcher matchesRegion(Region region) {
230 return new RegionJsonMatcher(region);
231 }
232
233 /**
234 * Hamcrest matcher to check that a region is represented properly in a JSON
235 * array of regions.
236 */
237 public static class RegionJsonArrayMatcher extends TypeSafeMatcher<JsonArray> {
238 private final Region region;
239 private String reason = "";
240
241 public RegionJsonArrayMatcher(Region regionValue) {
242 this.region = regionValue;
243 }
244
245 @Override
246 protected boolean matchesSafely(JsonArray json) {
247 boolean regionFound = false;
248 for (int jsonRegionIndex = 0; jsonRegionIndex < json.size(); jsonRegionIndex++) {
249 final JsonObject jsonRegion = json.get(jsonRegionIndex).asObject();
250
251 final String regionId = region.id().toString();
252 final String jsonRegionId = jsonRegion.get("id").asString();
253 if (jsonRegionId.equals(regionId)) {
254 regionFound = true;
255 assertThat(jsonRegion, matchesRegion(region));
256 }
257 }
258
259 if (!regionFound) {
260 reason = "Region with id " + region.id().toString() + " not found";
261 return false;
262 } else {
263 return true;
264 }
265 }
266
267 @Override
268 public void describeTo(Description description) {
269 description.appendText(reason);
270 }
271 }
272
273 /**
274 * Factory to allocate a region array matcher.
275 *
276 * @param region region object we are looking for
277 * @return matcher
278 */
279 private static RegionJsonArrayMatcher hasRegion(Region region) {
280 return new RegionJsonArrayMatcher(region);
281 }
282
283 @Test
284 public void testRegionEmptyArray() {
285 expect(mockRegionService.getRegions()).andReturn(ImmutableSet.of()).anyTimes();
286 replay((mockRegionService));
Jian Li9d616492016-03-09 10:52:49 -0800287 final WebTarget wt = target();
288 final String response = wt.path("regions").request().get(String.class);
Jian Li0c451802016-02-24 22:39:25 +0900289 assertThat(response, is("{\"regions\":[]}"));
290
291 verify(mockRegionService);
292 }
293
294 /**
295 * Tests the results of the REST API GET when there are active regions.
296 */
297 @Test
298 public void testRegionsPopulatedArray() {
299 final Set<Region> regions = ImmutableSet.of(region1, region2, region3);
300 expect(mockRegionService.getRegions()).andReturn(regions).anyTimes();
301 replay(mockRegionService);
302
Jian Li9d616492016-03-09 10:52:49 -0800303 final WebTarget wt = target();
304 final String response = wt.path("regions").request().get(String.class);
Jian Li0c451802016-02-24 22:39:25 +0900305 final JsonObject result = Json.parse(response).asObject();
306 assertThat(result, notNullValue());
307
308 assertThat(result.names(), hasSize(1));
309 assertThat(result.names().get(0), is("regions"));
310 final JsonArray jsonRegions = result.get("regions").asArray();
311 assertThat(jsonRegions, notNullValue());
312 assertThat(jsonRegions, hasRegion(region1));
313 assertThat(jsonRegions, hasRegion(region2));
314 assertThat(jsonRegions, hasRegion(region3));
315
316 verify(mockRegionService);
317
318 }
319
320 /**
321 * Tests the result of a REST API GET for a region with region id.
322 */
323 @Test
324 public void testGetRegionById() {
325 expect(mockRegionService.getRegion(anyObject())).andReturn(region1).anyTimes();
326 replay(mockRegionService);
327
Jian Li9d616492016-03-09 10:52:49 -0800328 final WebTarget wt = target();
329 final String response = wt.path("regions/" + regionId1.toString()).request().get(String.class);
Jian Li0c451802016-02-24 22:39:25 +0900330 final JsonObject result = Json.parse(response).asObject();
331 assertThat(result, notNullValue());
332 assertThat(result, matchesRegion(region1));
333
334 verify(mockRegionService);
335 }
336
337 /**
338 * Tests creating a region with POST.
339 */
340 @Test
341 public void testRegionPost() {
342 mockRegionAdminService.createRegion(anyObject(), anyObject(),
343 anyObject(), anyObject());
344 expectLastCall().andReturn(region2).anyTimes();
345 replay(mockRegionAdminService);
346
Jian Li9d616492016-03-09 10:52:49 -0800347 WebTarget wt = target();
Jian Li7011bdd2016-03-23 16:05:53 -0700348 InputStream jsonStream = RegionsResourceTest.class
Jian Li0c451802016-02-24 22:39:25 +0900349 .getResourceAsStream("post-region.json");
350
Jian Li9d616492016-03-09 10:52:49 -0800351 Response response = wt.path("regions")
352 .request(MediaType.APPLICATION_JSON_TYPE)
353 .post(Entity.json(jsonStream));
Jian Li0c451802016-02-24 22:39:25 +0900354 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
355
356 verify(mockRegionAdminService);
357 }
358
359 /**
360 * Tests updating a region with PUT.
361 */
362 @Test
363 public void testRegionPut() {
364 mockRegionAdminService.updateRegion(anyObject(), anyObject(),
365 anyObject(), anyObject());
366 expectLastCall().andReturn(region1).anyTimes();
367 replay(mockRegionAdminService);
368
Jian Li9d616492016-03-09 10:52:49 -0800369 WebTarget wt = target();
Jian Li7011bdd2016-03-23 16:05:53 -0700370 InputStream jsonStream = RegionsResourceTest.class
Jian Li0c451802016-02-24 22:39:25 +0900371 .getResourceAsStream("post-region.json");
372
Jian Li9d616492016-03-09 10:52:49 -0800373 Response response = wt.path("regions/" + region1.id().toString())
374 .request(MediaType.APPLICATION_JSON_TYPE)
375 .put(Entity.json(jsonStream));
Jian Li0c451802016-02-24 22:39:25 +0900376 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_OK));
377
378 verify(mockRegionAdminService);
379 }
380
381 /**
382 * Tests deleting a region with DELETE.
383 */
384 @Test
385 public void testRegionDelete() {
386 mockRegionAdminService.removeRegion(anyObject());
387 expectLastCall();
388 replay(mockRegionAdminService);
389
Jian Li9d616492016-03-09 10:52:49 -0800390 WebTarget wt = target();
391 Response response = wt.path("regions/" + region1.id().toString())
392 .request().delete();
Jian Lic2a542b2016-05-10 11:48:19 -0700393 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_NO_CONTENT));
Jian Li0c451802016-02-24 22:39:25 +0900394
395 verify(mockRegionAdminService);
396 }
397
398 /**
399 * Tests retrieving device ids that are associated with the given region.
400 */
401 @Test
402 public void testGetRegionDevices() {
403 final DeviceId deviceId1 = DeviceId.deviceId("1");
404 final DeviceId deviceId2 = DeviceId.deviceId("2");
405 final DeviceId deviceId3 = DeviceId.deviceId("3");
406
407 final Set<DeviceId> deviceIds = ImmutableSet.of(deviceId1, deviceId2, deviceId3);
408
409 expect(mockRegionService.getRegionDevices(anyObject()))
410 .andReturn(deviceIds).anyTimes();
411 replay(mockRegionService);
412
Jian Li9d616492016-03-09 10:52:49 -0800413 final WebTarget wt = target();
414 final String response = wt.path("regions/" +
415 region1.id().toString() + "/devices").request().get(String.class);
Jian Li0c451802016-02-24 22:39:25 +0900416 final JsonObject result = Json.parse(response).asObject();
417 assertThat(result, notNullValue());
418
419 assertThat(result.names(), hasSize(1));
420 assertThat(result.names().get(0), is("deviceIds"));
421 final JsonArray jsonDeviceIds = result.get("deviceIds").asArray();
422 assertThat(jsonDeviceIds.size(), is(3));
423 assertThat(jsonDeviceIds.get(0).asString(), is("1"));
424 assertThat(jsonDeviceIds.get(1).asString(), is("2"));
425 assertThat(jsonDeviceIds.get(2).asString(), is("3"));
426
427 verify(mockRegionService);
428 }
429
430 /**
Jayasree Ghoshfc72e2e2016-11-08 19:55:47 +0530431 * Tests creating a flow with POST.
432 */
433 @Test
434 public void testAddDevicesPostWithoutRegion() {
435 expect(mockRegionService.getRegion(anyObject())).andReturn(null).anyTimes();
436 replay(mockRegionService);
437
438 WebTarget wt = target();
439 InputStream jsonStream = RegionsResourceTest.class
440 .getResourceAsStream("region-deviceIds.json");
441
442 Response response = wt.path("regions/" + region1.id() + "/devices")
443 .request(MediaType.APPLICATION_JSON_TYPE)
444 .post(Entity.json(jsonStream));
445 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_NOT_FOUND));
446
447 verify(mockRegionService);
448 }
449
450 /**
Jian Li0c451802016-02-24 22:39:25 +0900451 * Tests adding a set of devices in region with POST.
452 */
453 @Test
454 public void testAddDevicesPost() {
455 mockRegionAdminService.addDevices(anyObject(), anyObject());
456 expectLastCall();
457 replay(mockRegionAdminService);
458
Jayasree Ghoshfc72e2e2016-11-08 19:55:47 +0530459 expect(mockRegionService.getRegion(anyObject())).andReturn(region1).anyTimes();
460 replay(mockRegionService);
461
Jian Li9d616492016-03-09 10:52:49 -0800462 WebTarget wt = target();
Jian Li7011bdd2016-03-23 16:05:53 -0700463 InputStream jsonStream = RegionsResourceTest.class
Jian Li0c451802016-02-24 22:39:25 +0900464 .getResourceAsStream("region-deviceIds.json");
465
Jayasree Ghoshfc72e2e2016-11-08 19:55:47 +0530466 Response response = wt.path("regions/" + region1.id().toString() + "/devices")
Jian Li9d616492016-03-09 10:52:49 -0800467 .request(MediaType.APPLICATION_JSON_TYPE)
468 .post(Entity.json(jsonStream));
Jian Li0c451802016-02-24 22:39:25 +0900469 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
470
471 verify(mockRegionAdminService);
472 }
473
474 /**
475 * Tests deleting a set of devices contained in the given region with DELETE.
476 */
477 @Test
478 public void testRemoveDevicesDelete() {
479 mockRegionAdminService.removeDevices(anyObject(), anyObject());
480 expectLastCall();
481 replay(mockRegionAdminService);
482
Jayasree Ghoshfc72e2e2016-11-08 19:55:47 +0530483 expect(mockRegionService.getRegion(anyObject())).andReturn(region1).anyTimes();
484 replay(mockRegionService);
Jian Li9d616492016-03-09 10:52:49 -0800485
Jian Lic2a542b2016-05-10 11:48:19 -0700486 WebTarget wt = target()
487 .property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true);
Jian Li7011bdd2016-03-23 16:05:53 -0700488 InputStream jsonStream = RegionsResourceTest.class
Jian Li0c451802016-02-24 22:39:25 +0900489 .getResourceAsStream("region-deviceIds.json");
490
Jian Li9d616492016-03-09 10:52:49 -0800491 // FIXME: need to consider whether to use jsonStream for entry deletion
Jayasree Ghoshfc72e2e2016-11-08 19:55:47 +0530492 Response response = wt.path("regions/" + region1.id().toString() + "/devices")
Jian Lic2a542b2016-05-10 11:48:19 -0700493 .request().method("DELETE", Entity.json(jsonStream));
494 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_NO_CONTENT));
495 verify(mockRegionAdminService);
Jian Li0c451802016-02-24 22:39:25 +0900496 }
497}