blob: 6f3c179e9c3090a73eeda00499756a5e1041bfae [file] [log] [blame]
Jian Li0c451802016-02-24 22:39:25 +09001/*
2 * Copyright 2016 Open Networking Laboratory
3 *
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 */
16package org.onosproject.rest;
17
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 Li0c451802016-02-24 22:39:25 +090024import org.hamcrest.Description;
25import org.hamcrest.TypeSafeMatcher;
26import org.junit.Before;
27import org.junit.Test;
28import org.onlab.osgi.ServiceDirectory;
29import org.onlab.osgi.TestServiceDirectory;
30import org.onlab.rest.BaseResource;
31import org.onosproject.cluster.NodeId;
32import org.onosproject.codec.CodecService;
33import org.onosproject.codec.impl.CodecManager;
34import org.onosproject.net.DeviceId;
35import org.onosproject.net.region.Region;
36import org.onosproject.net.region.RegionAdminService;
37import org.onosproject.net.region.RegionId;
38import org.onosproject.net.region.RegionService;
Jian Li0c451802016-02-24 22:39:25 +090039
Jian Li9d616492016-03-09 10:52:49 -080040import javax.ws.rs.client.Entity;
41import javax.ws.rs.client.WebTarget;
Jian Li0c451802016-02-24 22:39:25 +090042import javax.ws.rs.core.MediaType;
Jian Li9d616492016-03-09 10:52:49 -080043import javax.ws.rs.core.Response;
Jian Li0c451802016-02-24 22:39:25 +090044import java.io.InputStream;
45import java.net.HttpURLConnection;
46import java.util.List;
47import java.util.Set;
48
49import static org.easymock.EasyMock.anyObject;
50import static org.easymock.EasyMock.createMock;
51import static org.easymock.EasyMock.expect;
52import static org.easymock.EasyMock.expectLastCall;
53import static org.easymock.EasyMock.replay;
54import static org.easymock.EasyMock.verify;
55import static org.hamcrest.Matchers.hasSize;
56import static org.hamcrest.Matchers.is;
57import static org.hamcrest.Matchers.notNullValue;
58import static org.junit.Assert.assertThat;
59
60/**
61 * Unit tests for region REST APIs.
62 */
63public class RegionsResourceTest extends ResourceTest {
64
65 final RegionService mockRegionService = createMock(RegionService.class);
66 final RegionAdminService mockRegionAdminService = createMock(RegionAdminService.class);
67
68 final RegionId regionId1 = RegionId.regionId("1");
69 final RegionId regionId2 = RegionId.regionId("2");
70 final RegionId regionId3 = RegionId.regionId("3");
71
72 final MockRegion region1 = new MockRegion(regionId1, "r1", Region.Type.RACK);
73 final MockRegion region2 = new MockRegion(regionId2, "r2", Region.Type.ROOM);
74 final MockRegion region3 = new MockRegion(regionId3, "r3", Region.Type.CAMPUS);
75
Jian Li0c451802016-02-24 22:39:25 +090076 /**
77 * Mock class for a region.
78 */
79 private static class MockRegion implements Region {
80
81 private final RegionId id;
82 private final String name;
83 private final Type type;
84 private final List<Set<NodeId>> masters;
85
86 public MockRegion(RegionId id, String name, Type type) {
87 this.id = id;
88 this.name = name;
89 this.type = type;
90
91 final NodeId nodeId1 = NodeId.nodeId("1");
92 final NodeId nodeId2 = NodeId.nodeId("2");
93 final NodeId nodeId3 = NodeId.nodeId("3");
94 final NodeId nodeId4 = NodeId.nodeId("4");
95
96 Set<NodeId> nodeIds1 = ImmutableSet.of(nodeId1);
97 Set<NodeId> nodeIds2 = ImmutableSet.of(nodeId1, nodeId2);
98 Set<NodeId> nodeIds3 = ImmutableSet.of(nodeId1, nodeId2, nodeId3);
99 Set<NodeId> nodeIds4 = ImmutableSet.of(nodeId1, nodeId2, nodeId3, nodeId4);
100
101 this.masters = ImmutableList.of(nodeIds1, nodeIds2, nodeIds3, nodeIds4);
102 }
103
104 @Override
105 public RegionId id() {
106 return this.id;
107 }
108
109 @Override
110 public String name() {
111 return this.name;
112 }
113
114 @Override
115 public Type type() {
116 return this.type;
117 }
118
119 @Override
120 public List<Set<NodeId>> masters() {
121 return this.masters;
122 }
123 }
124
125 /**
126 * Sets up the global values for all the tests.
127 */
128 @Before
129 public void setupTest() {
130 final CodecManager codecService = new CodecManager();
131 codecService.activate();
132 ServiceDirectory testDirectory =
133 new TestServiceDirectory()
134 .add(RegionService.class, mockRegionService)
135 .add(RegionAdminService.class, mockRegionAdminService)
136 .add(CodecService.class, codecService);
137 BaseResource.setServiceDirectory(testDirectory);
138 }
139
140 /**
Jian Li7011bdd2016-03-23 16:05:53 -0700141 * Hamcrest matcher to check that a region representation in JSON matches
142 * the actual region.
Jian Li0c451802016-02-24 22:39:25 +0900143 */
144 public static class RegionJsonMatcher extends TypeSafeMatcher<JsonObject> {
145 private final Region region;
146 private String reason = "";
147
148 public RegionJsonMatcher(Region regionValue) {
149 this.region = regionValue;
150 }
151
152 @Override
153 protected boolean matchesSafely(JsonObject jsonRegion) {
154
155 // check id
156 String jsonRegionId = jsonRegion.get("id").asString();
157 String regionId = region.id().toString();
158 if (!jsonRegionId.equals(regionId)) {
159 reason = "region id was " + jsonRegionId;
160 return false;
161 }
162
163 // check type
164 String jsonType = jsonRegion.get("type").asString();
165 String type = region.type().toString();
166 if (!jsonType.equals(type)) {
167 reason = "type was " + jsonType;
168 return false;
169 }
170
171 // check name
172 String jsonName = jsonRegion.get("name").asString();
173 String name = region.name();
174 if (!jsonName.equals(name)) {
175 reason = "name was " + jsonName;
176 return false;
177 }
178
179 // check size of master array
180 JsonArray jsonMasters = jsonRegion.get("masters").asArray();
181 if (jsonMasters.size() != region.masters().size()) {
182 reason = "masters size was " + jsonMasters.size();
183 return false;
184 }
185
186 // check master
187 for (Set<NodeId> set : region.masters()) {
188 boolean masterFound = false;
189 for (int masterIndex = 0; masterIndex < jsonMasters.size(); masterIndex++) {
190 masterFound = checkEquality(jsonMasters.get(masterIndex).asArray(), set);
191 }
192
193 if (!masterFound) {
194 reason = "master not found " + set.toString();
195 return false;
196 }
197 }
198
199 return true;
200 }
201
202 @Override
203 public void describeTo(Description description) {
204 description.appendText(reason);
205 }
206
207 private Set<NodeId> jsonToSet(JsonArray nodes) {
208 final Set<NodeId> nodeIds = Sets.newHashSet();
209 nodes.forEach(node -> nodeIds.add(NodeId.nodeId(node.asString())));
210 return nodeIds;
211 }
212
213 private boolean checkEquality(JsonArray nodes, Set<NodeId> nodeIds) {
214 Set<NodeId> jsonSet = jsonToSet(nodes);
215 if (jsonSet.size() == nodes.size()) {
216 return jsonSet.containsAll(nodeIds);
217 }
218 return false;
219 }
220 }
221
222 private static RegionJsonMatcher matchesRegion(Region region) {
223 return new RegionJsonMatcher(region);
224 }
225
226 /**
227 * Hamcrest matcher to check that a region is represented properly in a JSON
228 * array of regions.
229 */
230 public static class RegionJsonArrayMatcher extends TypeSafeMatcher<JsonArray> {
231 private final Region region;
232 private String reason = "";
233
234 public RegionJsonArrayMatcher(Region regionValue) {
235 this.region = regionValue;
236 }
237
238 @Override
239 protected boolean matchesSafely(JsonArray json) {
240 boolean regionFound = false;
241 for (int jsonRegionIndex = 0; jsonRegionIndex < json.size(); jsonRegionIndex++) {
242 final JsonObject jsonRegion = json.get(jsonRegionIndex).asObject();
243
244 final String regionId = region.id().toString();
245 final String jsonRegionId = jsonRegion.get("id").asString();
246 if (jsonRegionId.equals(regionId)) {
247 regionFound = true;
248 assertThat(jsonRegion, matchesRegion(region));
249 }
250 }
251
252 if (!regionFound) {
253 reason = "Region with id " + region.id().toString() + " not found";
254 return false;
255 } else {
256 return true;
257 }
258 }
259
260 @Override
261 public void describeTo(Description description) {
262 description.appendText(reason);
263 }
264 }
265
266 /**
267 * Factory to allocate a region array matcher.
268 *
269 * @param region region object we are looking for
270 * @return matcher
271 */
272 private static RegionJsonArrayMatcher hasRegion(Region region) {
273 return new RegionJsonArrayMatcher(region);
274 }
275
276 @Test
277 public void testRegionEmptyArray() {
278 expect(mockRegionService.getRegions()).andReturn(ImmutableSet.of()).anyTimes();
279 replay((mockRegionService));
Jian Li9d616492016-03-09 10:52:49 -0800280 final WebTarget wt = target();
281 final String response = wt.path("regions").request().get(String.class);
Jian Li0c451802016-02-24 22:39:25 +0900282 assertThat(response, is("{\"regions\":[]}"));
283
284 verify(mockRegionService);
285 }
286
287 /**
288 * Tests the results of the REST API GET when there are active regions.
289 */
290 @Test
291 public void testRegionsPopulatedArray() {
292 final Set<Region> regions = ImmutableSet.of(region1, region2, region3);
293 expect(mockRegionService.getRegions()).andReturn(regions).anyTimes();
294 replay(mockRegionService);
295
Jian Li9d616492016-03-09 10:52:49 -0800296 final WebTarget wt = target();
297 final String response = wt.path("regions").request().get(String.class);
Jian Li0c451802016-02-24 22:39:25 +0900298 final JsonObject result = Json.parse(response).asObject();
299 assertThat(result, notNullValue());
300
301 assertThat(result.names(), hasSize(1));
302 assertThat(result.names().get(0), is("regions"));
303 final JsonArray jsonRegions = result.get("regions").asArray();
304 assertThat(jsonRegions, notNullValue());
305 assertThat(jsonRegions, hasRegion(region1));
306 assertThat(jsonRegions, hasRegion(region2));
307 assertThat(jsonRegions, hasRegion(region3));
308
309 verify(mockRegionService);
310
311 }
312
313 /**
314 * Tests the result of a REST API GET for a region with region id.
315 */
316 @Test
317 public void testGetRegionById() {
318 expect(mockRegionService.getRegion(anyObject())).andReturn(region1).anyTimes();
319 replay(mockRegionService);
320
Jian Li9d616492016-03-09 10:52:49 -0800321 final WebTarget wt = target();
322 final String response = wt.path("regions/" + regionId1.toString()).request().get(String.class);
Jian Li0c451802016-02-24 22:39:25 +0900323 final JsonObject result = Json.parse(response).asObject();
324 assertThat(result, notNullValue());
325 assertThat(result, matchesRegion(region1));
326
327 verify(mockRegionService);
328 }
329
330 /**
331 * Tests creating a region with POST.
332 */
333 @Test
334 public void testRegionPost() {
335 mockRegionAdminService.createRegion(anyObject(), anyObject(),
336 anyObject(), anyObject());
337 expectLastCall().andReturn(region2).anyTimes();
338 replay(mockRegionAdminService);
339
Jian Li9d616492016-03-09 10:52:49 -0800340 WebTarget wt = target();
Jian Li7011bdd2016-03-23 16:05:53 -0700341 InputStream jsonStream = RegionsResourceTest.class
Jian Li0c451802016-02-24 22:39:25 +0900342 .getResourceAsStream("post-region.json");
343
Jian Li9d616492016-03-09 10:52:49 -0800344 Response response = wt.path("regions")
345 .request(MediaType.APPLICATION_JSON_TYPE)
346 .post(Entity.json(jsonStream));
Jian Li0c451802016-02-24 22:39:25 +0900347 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
348
349 verify(mockRegionAdminService);
350 }
351
352 /**
353 * Tests updating a region with PUT.
354 */
355 @Test
356 public void testRegionPut() {
357 mockRegionAdminService.updateRegion(anyObject(), anyObject(),
358 anyObject(), anyObject());
359 expectLastCall().andReturn(region1).anyTimes();
360 replay(mockRegionAdminService);
361
Jian Li9d616492016-03-09 10:52:49 -0800362 WebTarget wt = target();
Jian Li7011bdd2016-03-23 16:05:53 -0700363 InputStream jsonStream = RegionsResourceTest.class
Jian Li0c451802016-02-24 22:39:25 +0900364 .getResourceAsStream("post-region.json");
365
Jian Li9d616492016-03-09 10:52:49 -0800366 Response response = wt.path("regions/" + region1.id().toString())
367 .request(MediaType.APPLICATION_JSON_TYPE)
368 .put(Entity.json(jsonStream));
Jian Li0c451802016-02-24 22:39:25 +0900369 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_OK));
370
371 verify(mockRegionAdminService);
372 }
373
374 /**
375 * Tests deleting a region with DELETE.
376 */
377 @Test
378 public void testRegionDelete() {
379 mockRegionAdminService.removeRegion(anyObject());
380 expectLastCall();
381 replay(mockRegionAdminService);
382
Jian Li9d616492016-03-09 10:52:49 -0800383 WebTarget wt = target();
384 Response response = wt.path("regions/" + region1.id().toString())
385 .request().delete();
Jian Li0c451802016-02-24 22:39:25 +0900386 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_OK));
387
388 verify(mockRegionAdminService);
389 }
390
391 /**
392 * Tests retrieving device ids that are associated with the given region.
393 */
394 @Test
395 public void testGetRegionDevices() {
396 final DeviceId deviceId1 = DeviceId.deviceId("1");
397 final DeviceId deviceId2 = DeviceId.deviceId("2");
398 final DeviceId deviceId3 = DeviceId.deviceId("3");
399
400 final Set<DeviceId> deviceIds = ImmutableSet.of(deviceId1, deviceId2, deviceId3);
401
402 expect(mockRegionService.getRegionDevices(anyObject()))
403 .andReturn(deviceIds).anyTimes();
404 replay(mockRegionService);
405
Jian Li9d616492016-03-09 10:52:49 -0800406 final WebTarget wt = target();
407 final String response = wt.path("regions/" +
408 region1.id().toString() + "/devices").request().get(String.class);
Jian Li0c451802016-02-24 22:39:25 +0900409 final JsonObject result = Json.parse(response).asObject();
410 assertThat(result, notNullValue());
411
412 assertThat(result.names(), hasSize(1));
413 assertThat(result.names().get(0), is("deviceIds"));
414 final JsonArray jsonDeviceIds = result.get("deviceIds").asArray();
415 assertThat(jsonDeviceIds.size(), is(3));
416 assertThat(jsonDeviceIds.get(0).asString(), is("1"));
417 assertThat(jsonDeviceIds.get(1).asString(), is("2"));
418 assertThat(jsonDeviceIds.get(2).asString(), is("3"));
419
420 verify(mockRegionService);
421 }
422
423 /**
424 * Tests adding a set of devices in region with POST.
425 */
426 @Test
427 public void testAddDevicesPost() {
428 mockRegionAdminService.addDevices(anyObject(), anyObject());
429 expectLastCall();
430 replay(mockRegionAdminService);
431
Jian Li9d616492016-03-09 10:52:49 -0800432 WebTarget wt = target();
Jian Li7011bdd2016-03-23 16:05:53 -0700433 InputStream jsonStream = RegionsResourceTest.class
Jian Li0c451802016-02-24 22:39:25 +0900434 .getResourceAsStream("region-deviceIds.json");
435
Jian Li9d616492016-03-09 10:52:49 -0800436 Response response = wt.path("regions/" +
Jian Li0c451802016-02-24 22:39:25 +0900437 region1.id().toString() + "/devices")
Jian Li9d616492016-03-09 10:52:49 -0800438 .request(MediaType.APPLICATION_JSON_TYPE)
439 .post(Entity.json(jsonStream));
Jian Li0c451802016-02-24 22:39:25 +0900440 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
441
442 verify(mockRegionAdminService);
443 }
444
445 /**
446 * Tests deleting a set of devices contained in the given region with DELETE.
447 */
448 @Test
449 public void testRemoveDevicesDelete() {
450 mockRegionAdminService.removeDevices(anyObject(), anyObject());
451 expectLastCall();
452 replay(mockRegionAdminService);
453
Jian Li9d616492016-03-09 10:52:49 -0800454
455 WebTarget wt = target();
Jian Li7011bdd2016-03-23 16:05:53 -0700456 InputStream jsonStream = RegionsResourceTest.class
Jian Li0c451802016-02-24 22:39:25 +0900457 .getResourceAsStream("region-deviceIds.json");
458
Jian Li9d616492016-03-09 10:52:49 -0800459 // FIXME: need to consider whether to use jsonStream for entry deletion
460 Response response = wt.path("regions/" +
Jian Li0c451802016-02-24 22:39:25 +0900461 region1.id().toString() + "/devices")
Jian Li9d616492016-03-09 10:52:49 -0800462 .request(MediaType.APPLICATION_JSON_TYPE)
463 .delete();
464 // assertThat(response.getStatus(), is(HttpURLConnection.HTTP_OK));
465 // verify(mockRegionAdminService);
Jian Li0c451802016-02-24 22:39:25 +0900466 }
467}