blob: f387140044844ab8d152f37d2281069ef11554d5 [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;
Simon Hunt53612212016-12-04 17:19:52 -080035import org.onosproject.net.Annotations;
36import org.onosproject.net.DefaultAnnotations;
Jian Li0c451802016-02-24 22:39:25 +090037import org.onosproject.net.DeviceId;
38import org.onosproject.net.region.Region;
39import org.onosproject.net.region.RegionAdminService;
40import org.onosproject.net.region.RegionId;
41import org.onosproject.net.region.RegionService;
Jian Li0c451802016-02-24 22:39:25 +090042
Jian Li9d616492016-03-09 10:52:49 -080043import javax.ws.rs.client.Entity;
44import javax.ws.rs.client.WebTarget;
Jian Li0c451802016-02-24 22:39:25 +090045import javax.ws.rs.core.MediaType;
Jian Li9d616492016-03-09 10:52:49 -080046import javax.ws.rs.core.Response;
Jian Li0c451802016-02-24 22:39:25 +090047import java.io.InputStream;
48import java.net.HttpURLConnection;
49import java.util.List;
50import java.util.Set;
51
52import static org.easymock.EasyMock.anyObject;
53import static org.easymock.EasyMock.createMock;
54import static org.easymock.EasyMock.expect;
55import static org.easymock.EasyMock.expectLastCall;
56import static org.easymock.EasyMock.replay;
57import static org.easymock.EasyMock.verify;
58import static org.hamcrest.Matchers.hasSize;
59import static org.hamcrest.Matchers.is;
60import static org.hamcrest.Matchers.notNullValue;
61import static org.junit.Assert.assertThat;
62
63/**
64 * Unit tests for region REST APIs.
65 */
66public class RegionsResourceTest extends ResourceTest {
67
68 final RegionService mockRegionService = createMock(RegionService.class);
69 final RegionAdminService mockRegionAdminService = createMock(RegionAdminService.class);
70
71 final RegionId regionId1 = RegionId.regionId("1");
72 final RegionId regionId2 = RegionId.regionId("2");
73 final RegionId regionId3 = RegionId.regionId("3");
74
75 final MockRegion region1 = new MockRegion(regionId1, "r1", Region.Type.RACK);
76 final MockRegion region2 = new MockRegion(regionId2, "r2", Region.Type.ROOM);
77 final MockRegion region3 = new MockRegion(regionId3, "r3", Region.Type.CAMPUS);
78
Jian Li0c451802016-02-24 22:39:25 +090079 /**
80 * Mock class for a region.
81 */
82 private static class MockRegion implements Region {
83
84 private final RegionId id;
85 private final String name;
86 private final Type type;
87 private final List<Set<NodeId>> masters;
88
89 public MockRegion(RegionId id, String name, Type type) {
90 this.id = id;
91 this.name = name;
92 this.type = type;
93
94 final NodeId nodeId1 = NodeId.nodeId("1");
95 final NodeId nodeId2 = NodeId.nodeId("2");
96 final NodeId nodeId3 = NodeId.nodeId("3");
97 final NodeId nodeId4 = NodeId.nodeId("4");
98
99 Set<NodeId> nodeIds1 = ImmutableSet.of(nodeId1);
100 Set<NodeId> nodeIds2 = ImmutableSet.of(nodeId1, nodeId2);
101 Set<NodeId> nodeIds3 = ImmutableSet.of(nodeId1, nodeId2, nodeId3);
102 Set<NodeId> nodeIds4 = ImmutableSet.of(nodeId1, nodeId2, nodeId3, nodeId4);
103
104 this.masters = ImmutableList.of(nodeIds1, nodeIds2, nodeIds3, nodeIds4);
105 }
106
107 @Override
108 public RegionId id() {
109 return this.id;
110 }
111
112 @Override
113 public String name() {
114 return this.name;
115 }
116
117 @Override
118 public Type type() {
119 return this.type;
120 }
121
122 @Override
123 public List<Set<NodeId>> masters() {
124 return this.masters;
125 }
Simon Hunt53612212016-12-04 17:19:52 -0800126
127 @Override
128 public Annotations annotations() {
129 return DefaultAnnotations.EMPTY;
130 }
Jian Li0c451802016-02-24 22:39:25 +0900131 }
132
133 /**
134 * Sets up the global values for all the tests.
135 */
136 @Before
137 public void setupTest() {
138 final CodecManager codecService = new CodecManager();
139 codecService.activate();
140 ServiceDirectory testDirectory =
141 new TestServiceDirectory()
142 .add(RegionService.class, mockRegionService)
143 .add(RegionAdminService.class, mockRegionAdminService)
144 .add(CodecService.class, codecService);
145 BaseResource.setServiceDirectory(testDirectory);
146 }
147
148 /**
Jian Li7011bdd2016-03-23 16:05:53 -0700149 * Hamcrest matcher to check that a region representation in JSON matches
150 * the actual region.
Jian Li0c451802016-02-24 22:39:25 +0900151 */
152 public static class RegionJsonMatcher extends TypeSafeMatcher<JsonObject> {
153 private final Region region;
154 private String reason = "";
155
156 public RegionJsonMatcher(Region regionValue) {
157 this.region = regionValue;
158 }
159
160 @Override
161 protected boolean matchesSafely(JsonObject jsonRegion) {
162
163 // check id
164 String jsonRegionId = jsonRegion.get("id").asString();
165 String regionId = region.id().toString();
166 if (!jsonRegionId.equals(regionId)) {
167 reason = "region id was " + jsonRegionId;
168 return false;
169 }
170
171 // check type
172 String jsonType = jsonRegion.get("type").asString();
173 String type = region.type().toString();
174 if (!jsonType.equals(type)) {
175 reason = "type was " + jsonType;
176 return false;
177 }
178
179 // check name
180 String jsonName = jsonRegion.get("name").asString();
181 String name = region.name();
182 if (!jsonName.equals(name)) {
183 reason = "name was " + jsonName;
184 return false;
185 }
186
187 // check size of master array
188 JsonArray jsonMasters = jsonRegion.get("masters").asArray();
189 if (jsonMasters.size() != region.masters().size()) {
190 reason = "masters size was " + jsonMasters.size();
191 return false;
192 }
193
194 // check master
195 for (Set<NodeId> set : region.masters()) {
196 boolean masterFound = false;
197 for (int masterIndex = 0; masterIndex < jsonMasters.size(); masterIndex++) {
198 masterFound = checkEquality(jsonMasters.get(masterIndex).asArray(), set);
199 }
200
201 if (!masterFound) {
202 reason = "master not found " + set.toString();
203 return false;
204 }
205 }
206
207 return true;
208 }
209
210 @Override
211 public void describeTo(Description description) {
212 description.appendText(reason);
213 }
214
215 private Set<NodeId> jsonToSet(JsonArray nodes) {
216 final Set<NodeId> nodeIds = Sets.newHashSet();
217 nodes.forEach(node -> nodeIds.add(NodeId.nodeId(node.asString())));
218 return nodeIds;
219 }
220
221 private boolean checkEquality(JsonArray nodes, Set<NodeId> nodeIds) {
222 Set<NodeId> jsonSet = jsonToSet(nodes);
223 if (jsonSet.size() == nodes.size()) {
224 return jsonSet.containsAll(nodeIds);
225 }
226 return false;
227 }
228 }
229
230 private static RegionJsonMatcher matchesRegion(Region region) {
231 return new RegionJsonMatcher(region);
232 }
233
234 /**
235 * Hamcrest matcher to check that a region is represented properly in a JSON
236 * array of regions.
237 */
238 public static class RegionJsonArrayMatcher extends TypeSafeMatcher<JsonArray> {
239 private final Region region;
240 private String reason = "";
241
242 public RegionJsonArrayMatcher(Region regionValue) {
243 this.region = regionValue;
244 }
245
246 @Override
247 protected boolean matchesSafely(JsonArray json) {
248 boolean regionFound = false;
249 for (int jsonRegionIndex = 0; jsonRegionIndex < json.size(); jsonRegionIndex++) {
250 final JsonObject jsonRegion = json.get(jsonRegionIndex).asObject();
251
252 final String regionId = region.id().toString();
253 final String jsonRegionId = jsonRegion.get("id").asString();
254 if (jsonRegionId.equals(regionId)) {
255 regionFound = true;
256 assertThat(jsonRegion, matchesRegion(region));
257 }
258 }
259
260 if (!regionFound) {
261 reason = "Region with id " + region.id().toString() + " not found";
262 return false;
263 } else {
264 return true;
265 }
266 }
267
268 @Override
269 public void describeTo(Description description) {
270 description.appendText(reason);
271 }
272 }
273
274 /**
275 * Factory to allocate a region array matcher.
276 *
277 * @param region region object we are looking for
278 * @return matcher
279 */
280 private static RegionJsonArrayMatcher hasRegion(Region region) {
281 return new RegionJsonArrayMatcher(region);
282 }
283
284 @Test
285 public void testRegionEmptyArray() {
286 expect(mockRegionService.getRegions()).andReturn(ImmutableSet.of()).anyTimes();
287 replay((mockRegionService));
Jian Li9d616492016-03-09 10:52:49 -0800288 final WebTarget wt = target();
289 final String response = wt.path("regions").request().get(String.class);
Jian Li0c451802016-02-24 22:39:25 +0900290 assertThat(response, is("{\"regions\":[]}"));
291
292 verify(mockRegionService);
293 }
294
295 /**
296 * Tests the results of the REST API GET when there are active regions.
297 */
298 @Test
299 public void testRegionsPopulatedArray() {
300 final Set<Region> regions = ImmutableSet.of(region1, region2, region3);
301 expect(mockRegionService.getRegions()).andReturn(regions).anyTimes();
302 replay(mockRegionService);
303
Jian Li9d616492016-03-09 10:52:49 -0800304 final WebTarget wt = target();
305 final String response = wt.path("regions").request().get(String.class);
Jian Li0c451802016-02-24 22:39:25 +0900306 final JsonObject result = Json.parse(response).asObject();
307 assertThat(result, notNullValue());
308
309 assertThat(result.names(), hasSize(1));
310 assertThat(result.names().get(0), is("regions"));
311 final JsonArray jsonRegions = result.get("regions").asArray();
312 assertThat(jsonRegions, notNullValue());
313 assertThat(jsonRegions, hasRegion(region1));
314 assertThat(jsonRegions, hasRegion(region2));
315 assertThat(jsonRegions, hasRegion(region3));
316
317 verify(mockRegionService);
318
319 }
320
321 /**
322 * Tests the result of a REST API GET for a region with region id.
323 */
324 @Test
325 public void testGetRegionById() {
326 expect(mockRegionService.getRegion(anyObject())).andReturn(region1).anyTimes();
327 replay(mockRegionService);
328
Jian Li9d616492016-03-09 10:52:49 -0800329 final WebTarget wt = target();
330 final String response = wt.path("regions/" + regionId1.toString()).request().get(String.class);
Jian Li0c451802016-02-24 22:39:25 +0900331 final JsonObject result = Json.parse(response).asObject();
332 assertThat(result, notNullValue());
333 assertThat(result, matchesRegion(region1));
334
335 verify(mockRegionService);
336 }
337
338 /**
339 * Tests creating a region with POST.
340 */
341 @Test
342 public void testRegionPost() {
343 mockRegionAdminService.createRegion(anyObject(), anyObject(),
344 anyObject(), anyObject());
345 expectLastCall().andReturn(region2).anyTimes();
346 replay(mockRegionAdminService);
347
Jian Li9d616492016-03-09 10:52:49 -0800348 WebTarget wt = target();
Jian Li7011bdd2016-03-23 16:05:53 -0700349 InputStream jsonStream = RegionsResourceTest.class
Jian Li0c451802016-02-24 22:39:25 +0900350 .getResourceAsStream("post-region.json");
351
Jian Li9d616492016-03-09 10:52:49 -0800352 Response response = wt.path("regions")
353 .request(MediaType.APPLICATION_JSON_TYPE)
354 .post(Entity.json(jsonStream));
Jian Li0c451802016-02-24 22:39:25 +0900355 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
356
357 verify(mockRegionAdminService);
358 }
359
360 /**
361 * Tests updating a region with PUT.
362 */
363 @Test
364 public void testRegionPut() {
365 mockRegionAdminService.updateRegion(anyObject(), anyObject(),
366 anyObject(), anyObject());
367 expectLastCall().andReturn(region1).anyTimes();
368 replay(mockRegionAdminService);
369
Jian Li9d616492016-03-09 10:52:49 -0800370 WebTarget wt = target();
Jian Li7011bdd2016-03-23 16:05:53 -0700371 InputStream jsonStream = RegionsResourceTest.class
Jian Li0c451802016-02-24 22:39:25 +0900372 .getResourceAsStream("post-region.json");
373
Jian Li9d616492016-03-09 10:52:49 -0800374 Response response = wt.path("regions/" + region1.id().toString())
375 .request(MediaType.APPLICATION_JSON_TYPE)
376 .put(Entity.json(jsonStream));
Jian Li0c451802016-02-24 22:39:25 +0900377 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_OK));
378
379 verify(mockRegionAdminService);
380 }
381
382 /**
383 * Tests deleting a region with DELETE.
384 */
385 @Test
386 public void testRegionDelete() {
387 mockRegionAdminService.removeRegion(anyObject());
388 expectLastCall();
389 replay(mockRegionAdminService);
390
Jian Li9d616492016-03-09 10:52:49 -0800391 WebTarget wt = target();
392 Response response = wt.path("regions/" + region1.id().toString())
393 .request().delete();
Jian Lic2a542b2016-05-10 11:48:19 -0700394 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_NO_CONTENT));
Jian Li0c451802016-02-24 22:39:25 +0900395
396 verify(mockRegionAdminService);
397 }
398
399 /**
400 * Tests retrieving device ids that are associated with the given region.
401 */
402 @Test
403 public void testGetRegionDevices() {
404 final DeviceId deviceId1 = DeviceId.deviceId("1");
405 final DeviceId deviceId2 = DeviceId.deviceId("2");
406 final DeviceId deviceId3 = DeviceId.deviceId("3");
407
408 final Set<DeviceId> deviceIds = ImmutableSet.of(deviceId1, deviceId2, deviceId3);
409
410 expect(mockRegionService.getRegionDevices(anyObject()))
411 .andReturn(deviceIds).anyTimes();
412 replay(mockRegionService);
413
Jian Li9d616492016-03-09 10:52:49 -0800414 final WebTarget wt = target();
415 final String response = wt.path("regions/" +
416 region1.id().toString() + "/devices").request().get(String.class);
Jian Li0c451802016-02-24 22:39:25 +0900417 final JsonObject result = Json.parse(response).asObject();
418 assertThat(result, notNullValue());
419
420 assertThat(result.names(), hasSize(1));
421 assertThat(result.names().get(0), is("deviceIds"));
422 final JsonArray jsonDeviceIds = result.get("deviceIds").asArray();
423 assertThat(jsonDeviceIds.size(), is(3));
424 assertThat(jsonDeviceIds.get(0).asString(), is("1"));
425 assertThat(jsonDeviceIds.get(1).asString(), is("2"));
426 assertThat(jsonDeviceIds.get(2).asString(), is("3"));
427
428 verify(mockRegionService);
429 }
430
431 /**
Jayasree Ghoshfc72e2e2016-11-08 19:55:47 +0530432 * Tests creating a flow with POST.
433 */
434 @Test
435 public void testAddDevicesPostWithoutRegion() {
436 expect(mockRegionService.getRegion(anyObject())).andReturn(null).anyTimes();
437 replay(mockRegionService);
438
439 WebTarget wt = target();
440 InputStream jsonStream = RegionsResourceTest.class
441 .getResourceAsStream("region-deviceIds.json");
442
443 Response response = wt.path("regions/" + region1.id() + "/devices")
444 .request(MediaType.APPLICATION_JSON_TYPE)
445 .post(Entity.json(jsonStream));
446 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_NOT_FOUND));
447
448 verify(mockRegionService);
449 }
450
451 /**
Jian Li0c451802016-02-24 22:39:25 +0900452 * Tests adding a set of devices in region with POST.
453 */
454 @Test
455 public void testAddDevicesPost() {
456 mockRegionAdminService.addDevices(anyObject(), anyObject());
457 expectLastCall();
458 replay(mockRegionAdminService);
459
Jayasree Ghoshfc72e2e2016-11-08 19:55:47 +0530460 expect(mockRegionService.getRegion(anyObject())).andReturn(region1).anyTimes();
461 replay(mockRegionService);
462
Jian Li9d616492016-03-09 10:52:49 -0800463 WebTarget wt = target();
Jian Li7011bdd2016-03-23 16:05:53 -0700464 InputStream jsonStream = RegionsResourceTest.class
Jian Li0c451802016-02-24 22:39:25 +0900465 .getResourceAsStream("region-deviceIds.json");
466
Jayasree Ghoshfc72e2e2016-11-08 19:55:47 +0530467 Response response = wt.path("regions/" + region1.id().toString() + "/devices")
Jian Li9d616492016-03-09 10:52:49 -0800468 .request(MediaType.APPLICATION_JSON_TYPE)
469 .post(Entity.json(jsonStream));
Jian Li0c451802016-02-24 22:39:25 +0900470 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
471
472 verify(mockRegionAdminService);
473 }
474
475 /**
476 * Tests deleting a set of devices contained in the given region with DELETE.
477 */
478 @Test
479 public void testRemoveDevicesDelete() {
480 mockRegionAdminService.removeDevices(anyObject(), anyObject());
481 expectLastCall();
482 replay(mockRegionAdminService);
483
Jayasree Ghoshfc72e2e2016-11-08 19:55:47 +0530484 expect(mockRegionService.getRegion(anyObject())).andReturn(region1).anyTimes();
485 replay(mockRegionService);
Jian Li9d616492016-03-09 10:52:49 -0800486
Jian Lic2a542b2016-05-10 11:48:19 -0700487 WebTarget wt = target()
488 .property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true);
Jian Li7011bdd2016-03-23 16:05:53 -0700489 InputStream jsonStream = RegionsResourceTest.class
Jian Li0c451802016-02-24 22:39:25 +0900490 .getResourceAsStream("region-deviceIds.json");
491
Jian Li9d616492016-03-09 10:52:49 -0800492 // FIXME: need to consider whether to use jsonStream for entry deletion
Jayasree Ghoshfc72e2e2016-11-08 19:55:47 +0530493 Response response = wt.path("regions/" + region1.id().toString() + "/devices")
Jian Lic2a542b2016-05-10 11:48:19 -0700494 .request().method("DELETE", Entity.json(jsonStream));
495 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_NO_CONTENT));
496 verify(mockRegionAdminService);
Jian Li0c451802016-02-24 22:39:25 +0900497 }
498}