blob: 0e494ef96bef3167beca5bcc41021420fdcbbbfc [file] [log] [blame]
Jian Liecb3c0f2015-12-15 10:07:49 -08001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2016-present Open Networking Laboratory
Jian Liecb3c0f2015-12-15 10:07:49 -08003 *
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 */
16
Jian Li8ae91202016-03-24 14:36:16 -070017package org.onosproject.rest.resources;
Jian Liecb3c0f2015-12-15 10:07:49 -080018
Jian Li80cfe452016-01-14 16:04:58 -080019import com.eclipsesource.json.Json;
Jian Liecb3c0f2015-12-15 10:07:49 -080020import com.eclipsesource.json.JsonArray;
21import com.eclipsesource.json.JsonObject;
22import com.google.common.collect.ImmutableSet;
Jian Liecb3c0f2015-12-15 10:07:49 -080023import org.hamcrest.Description;
Jian Li9d616492016-03-09 10:52:49 -080024import org.hamcrest.Matchers;
Jian Liecb3c0f2015-12-15 10:07:49 -080025import org.hamcrest.TypeSafeMatcher;
26import org.junit.After;
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.codec.CodecService;
33import org.onosproject.codec.impl.CodecManager;
34import org.onosproject.codec.impl.GroupCodec;
35import org.onosproject.core.ApplicationId;
36import org.onosproject.core.CoreService;
37import org.onosproject.core.DefaultApplicationId;
38import org.onosproject.core.DefaultGroupId;
39import org.onosproject.core.GroupId;
40import org.onosproject.net.DefaultDevice;
41import org.onosproject.net.Device;
42import org.onosproject.net.DeviceId;
43import org.onosproject.net.NetTestTools;
44import org.onosproject.net.device.DeviceService;
45import org.onosproject.net.group.DefaultGroupKey;
46import org.onosproject.net.group.Group;
47import org.onosproject.net.group.GroupBucket;
48import org.onosproject.net.group.GroupBuckets;
49import org.onosproject.net.group.GroupDescription;
50import org.onosproject.net.group.GroupKey;
51import org.onosproject.net.group.GroupService;
52
Jian Li9d616492016-03-09 10:52:49 -080053import javax.ws.rs.client.Entity;
54import javax.ws.rs.client.WebTarget;
Jian Liecb3c0f2015-12-15 10:07:49 -080055import javax.ws.rs.core.MediaType;
Jian Li9d616492016-03-09 10:52:49 -080056import javax.ws.rs.core.Response;
Jian Liecb3c0f2015-12-15 10:07:49 -080057import java.io.InputStream;
58import java.net.HttpURLConnection;
59import java.util.ArrayList;
60import java.util.HashMap;
61import java.util.HashSet;
62import java.util.List;
63import java.util.Set;
64
65import static org.easymock.EasyMock.anyObject;
66import static org.easymock.EasyMock.anyShort;
67import static org.easymock.EasyMock.createMock;
68import static org.easymock.EasyMock.expect;
69import static org.easymock.EasyMock.expectLastCall;
70import static org.easymock.EasyMock.replay;
71import static org.easymock.EasyMock.verify;
72import static org.hamcrest.Matchers.hasSize;
73import static org.hamcrest.Matchers.is;
74import static org.hamcrest.Matchers.notNullValue;
Jian Lia7f86ce2015-12-20 13:42:10 -080075import static org.junit.Assert.assertEquals;
Jian Li9d616492016-03-09 10:52:49 -080076import static org.junit.Assert.assertThat;
Jian Liecb3c0f2015-12-15 10:07:49 -080077import static org.onosproject.net.NetTestTools.APP_ID;
78
79/**
80 * Unit tests for Groups REST APIs.
81 */
82public class GroupsResourceTest extends ResourceTest {
83 final GroupService mockGroupService = createMock(GroupService.class);
84 CoreService mockCoreService = createMock(CoreService.class);
85 final DeviceService mockDeviceService = createMock(DeviceService.class);
86
87 final HashMap<DeviceId, Set<Group>> groups = new HashMap<>();
88
89
90 final DeviceId deviceId1 = DeviceId.deviceId("1");
91 final DeviceId deviceId2 = DeviceId.deviceId("2");
92 final DeviceId deviceId3 = DeviceId.deviceId("3");
93 final Device device1 = new DefaultDevice(null, deviceId1, Device.Type.OTHER,
94 "", "", "", "", null);
95 final Device device2 = new DefaultDevice(null, deviceId2, Device.Type.OTHER,
96 "", "", "", "", null);
97
Varun Sharma1853b3f2016-07-18 14:37:13 +053098 final MockGroup group1 = new MockGroup(deviceId1, 1, "0x111", 1);
99 final MockGroup group2 = new MockGroup(deviceId1, 2, "0x222", 2);
Jian Liecb3c0f2015-12-15 10:07:49 -0800100
Varun Sharma1853b3f2016-07-18 14:37:13 +0530101 final MockGroup group3 = new MockGroup(deviceId2, 3, "0x333", 3);
102 final MockGroup group4 = new MockGroup(deviceId2, 4, "0x444", 4);
Jian Liecb3c0f2015-12-15 10:07:49 -0800103
Varun Sharma1853b3f2016-07-18 14:37:13 +0530104 final MockGroup group5 = new MockGroup(deviceId3, 5, "0x555", 5);
105 final MockGroup group6 = new MockGroup(deviceId3, 6, "0x666", 6);
Jian Liecb3c0f2015-12-15 10:07:49 -0800106
107 /**
108 * Mock class for a group.
109 */
110 private static class MockGroup implements Group {
111
112 final DeviceId deviceId;
113 final ApplicationId appId;
114 final GroupKey appCookie;
115 final long baseValue;
116 final List<GroupBucket> bucketList;
117 GroupBuckets buckets;
118
119 public MockGroup(DeviceId deviceId, int appId, String appCookie, int id) {
120 this.deviceId = deviceId;
121 this.appId = new DefaultApplicationId(appId, String.valueOf(appId));
122 this.appCookie = new DefaultGroupKey(appCookie.getBytes());
123 this.baseValue = id * 100;
124 this.bucketList = new ArrayList<>();
125 this.buckets = new GroupBuckets(bucketList);
126 }
127
128 @Override
129 public GroupId id() {
130 return new DefaultGroupId((int) baseValue + 55);
131 }
132
133 @Override
134 public GroupState state() {
135 return GroupState.ADDED;
136 }
137
138 @Override
139 public long life() {
140 return baseValue + 11;
141 }
142
143 @Override
144 public long packets() {
145 return baseValue + 22;
146 }
147
148 @Override
149 public long bytes() {
150 return baseValue + 33;
151 }
152
153 @Override
154 public long referenceCount() {
155 return baseValue + 44;
156 }
157
158 @Override
alshabibb0285992016-03-28 23:30:37 -0700159 public int age() {
160 return 0;
161 }
162
163 @Override
Jian Liecb3c0f2015-12-15 10:07:49 -0800164 public Type type() {
165 return GroupDescription.Type.ALL;
166 }
167
168 @Override
169 public DeviceId deviceId() {
170 return this.deviceId;
171 }
172
173 @Override
174 public ApplicationId appId() {
175 return this.appId;
176 }
177
178 @Override
179 public GroupKey appCookie() {
180 return this.appCookie;
181 }
182
183 @Override
184 public Integer givenGroupId() {
185 return (int) baseValue + 55;
186 }
187
188 @Override
189 public GroupBuckets buckets() {
190 return this.buckets;
191 }
192 }
193
194 /**
195 * Populates some groups used as testing data.
196 */
197 private void setupMockGroups() {
198 final Set<Group> groups1 = new HashSet<>();
199 groups1.add(group1);
200 groups1.add(group2);
201
202 final Set<Group> groups2 = new HashSet<>();
203 groups2.add(group3);
204 groups2.add(group4);
205
206 groups.put(deviceId1, groups1);
207 groups.put(deviceId2, groups2);
208
209 expect(mockGroupService.getGroups(deviceId1))
210 .andReturn(groups.get(deviceId1)).anyTimes();
211 expect(mockGroupService.getGroups(deviceId2))
212 .andReturn(groups.get(deviceId2)).anyTimes();
213 }
214
215 /**
216 * Sets up the global values for all the tests.
217 */
218 @Before
219 public void setUpTest() {
220 // Mock device service
221 expect(mockDeviceService.getDevice(deviceId1))
222 .andReturn(device1);
223 expect(mockDeviceService.getDevice(deviceId2))
224 .andReturn(device2);
225 expect(mockDeviceService.getDevices())
226 .andReturn(ImmutableSet.of(device1, device2));
227
228 // Mock Core Service
229 expect(mockCoreService.getAppId(anyShort()))
230 .andReturn(NetTestTools.APP_ID).anyTimes();
231 expect(mockCoreService.registerApplication(GroupCodec.REST_APP_ID))
232 .andReturn(APP_ID).anyTimes();
233 replay(mockCoreService);
234
235 // Register the services needed for the test
236 final CodecManager codecService = new CodecManager();
237 codecService.activate();
238 ServiceDirectory testDirectory =
239 new TestServiceDirectory()
240 .add(GroupService.class, mockGroupService)
241 .add(DeviceService.class, mockDeviceService)
242 .add(CodecService.class, codecService)
243 .add(CoreService.class, mockCoreService);
244
245 BaseResource.setServiceDirectory(testDirectory);
246 }
247
248 /**
249 * Cleans up and verifies the mocks.
250 */
251 @After
252 public void tearDownTest() {
253 verify(mockGroupService);
254 verify(mockCoreService);
255 }
256
257 /**
258 * Hamcrest matcher to check that a group representation in JSON matches
259 * the actual group.
260 */
261 public static class GroupJsonMatcher extends TypeSafeMatcher<JsonObject> {
262 private final Group group;
263 private final String expectedAppId;
264 private String reason = "";
265
266 public GroupJsonMatcher(Group groupValue, String expectedAppIdValue) {
267 group = groupValue;
268 expectedAppId = expectedAppIdValue;
269 }
270
271 @Override
272 public boolean matchesSafely(JsonObject jsonGroup) {
273 // check id
274 final String jsonId = jsonGroup.get("id").asString();
Prince Pereira3ff504c2016-08-30 14:23:43 +0530275 final String groupId = group.id().id().toString();
Jian Liecb3c0f2015-12-15 10:07:49 -0800276 if (!jsonId.equals(groupId)) {
Prince Pereira3ff504c2016-08-30 14:23:43 +0530277 reason = "id " + group.id().id().toString();
Jian Liecb3c0f2015-12-15 10:07:49 -0800278 return false;
279 }
280
281 // check application id
282 final String jsonAppId = jsonGroup.get("appId").asString();
varunsha34b30602016-08-18 10:31:18 -0700283 final String appId = group.appId().name();
Jian Liecb3c0f2015-12-15 10:07:49 -0800284 if (!jsonAppId.equals(appId)) {
varunsha34b30602016-08-18 10:31:18 -0700285 reason = "appId " + group.appId().name();
Jian Liecb3c0f2015-12-15 10:07:49 -0800286 return false;
287 }
288
289 // check device id
290 final String jsonDeviceId = jsonGroup.get("deviceId").asString();
291 if (!jsonDeviceId.equals(group.deviceId().toString())) {
292 reason = "deviceId " + group.deviceId();
293 return false;
294 }
295
296 // check bucket array
297 if (group.buckets().buckets() != null) {
298 final JsonArray jsonBuckets = jsonGroup.get("buckets").asArray();
299 if (group.buckets().buckets().size() != jsonBuckets.size()) {
300 reason = "buckets array size of " +
301 Integer.toString(group.buckets().buckets().size());
302 return false;
303 }
304 for (final GroupBucket groupBucket : group.buckets().buckets()) {
305 boolean groupBucketFound = false;
306 for (int groupBucketIndex = 0; groupBucketIndex < jsonBuckets.size(); groupBucketIndex++) {
307 final String jsonType = jsonBuckets.get(groupBucketIndex).asObject().get("type").asString();
308 final String bucketType = groupBucket.type().name();
309 if (jsonType.equals(bucketType)) {
310 groupBucketFound = true;
311 }
312 }
313 if (!groupBucketFound) {
314 reason = "group bucket " + groupBucket.toString();
315 return false;
316 }
317 }
318 }
319
320 return true;
321 }
322
323 @Override
324 public void describeTo(Description description) {
325 description.appendText(reason);
326 }
327 }
328
329 /**
330 * Factory to allocate a group matcher.
331 *
332 * @param group group object we are looking for
333 * @return matcher
334 */
335 private static GroupJsonMatcher matchesGroup(Group group, String expectedAppName) {
336 return new GroupJsonMatcher(group, expectedAppName);
337 }
338
339 /**
340 * Hamcrest matcher to check that a group is represented properly in a JSON
341 * array of flows.
342 */
343 public static class GroupJsonArrayMatcher extends TypeSafeMatcher<JsonArray> {
344 private final Group group;
345 private String reason = "";
346
347 public GroupJsonArrayMatcher(Group groupValue) {
348 group = groupValue;
349 }
350
351 @Override
352 public boolean matchesSafely(JsonArray json) {
353 boolean groupFound = false;
354 for (int jsonGroupIndex = 0; jsonGroupIndex < json.size();
355 jsonGroupIndex++) {
356
357 final JsonObject jsonGroup = json.get(jsonGroupIndex).asObject();
358
Prince Pereira3ff504c2016-08-30 14:23:43 +0530359 final String groupId = group.id().id().toString();
Jian Liecb3c0f2015-12-15 10:07:49 -0800360 final String jsonGroupId = jsonGroup.get("id").asString();
361 if (jsonGroupId.equals(groupId)) {
362 groupFound = true;
363
364 // We found the correct group, check attribute values
365 assertThat(jsonGroup, matchesGroup(group, APP_ID.name()));
366 }
367 }
368 if (!groupFound) {
Prince Pereira3ff504c2016-08-30 14:23:43 +0530369 reason = "Group with id " + group.id().id().toString() + " not found";
Jian Liecb3c0f2015-12-15 10:07:49 -0800370 return false;
371 } else {
372 return true;
373 }
374 }
375
376 @Override
377 public void describeTo(Description description) {
378 description.appendText(reason);
379 }
380 }
381
382 /**
383 * Factory to allocate a group array matcher.
384 *
385 * @param group group object we are looking for
386 * @return matcher
387 */
388 private static GroupJsonArrayMatcher hasGroup(Group group) {
389 return new GroupJsonArrayMatcher(group);
390 }
391
392 /**
393 * Tests the result of the rest api GET when there are no groups.
394 */
395 @Test
396 public void testGroupsEmptyArray() {
397 expect(mockGroupService.getGroups(deviceId1)).andReturn(null).anyTimes();
398 expect(mockGroupService.getGroups(deviceId2)).andReturn(null).anyTimes();
399 replay(mockGroupService);
400 replay(mockDeviceService);
Jian Li9d616492016-03-09 10:52:49 -0800401 final WebTarget wt = target();
402 final String response = wt.path("groups").request().get(String.class);
Jian Liecb3c0f2015-12-15 10:07:49 -0800403 assertThat(response, is("{\"groups\":[]}"));
404 }
405
406 /**
407 * Tests the result of the rest api GET when there are active groups.
408 */
409 @Test
410 public void testGroupsPopulatedArray() {
411 setupMockGroups();
412 replay(mockGroupService);
413 replay(mockDeviceService);
Jian Li9d616492016-03-09 10:52:49 -0800414 final WebTarget wt = target();
415 final String response = wt.path("groups").request().get(String.class);
Jian Li80cfe452016-01-14 16:04:58 -0800416 final JsonObject result = Json.parse(response).asObject();
Jian Liecb3c0f2015-12-15 10:07:49 -0800417 assertThat(result, notNullValue());
418
419 assertThat(result.names(), hasSize(1));
420 assertThat(result.names().get(0), is("groups"));
421 final JsonArray jsonGroups = result.get("groups").asArray();
422 assertThat(jsonGroups, notNullValue());
423 assertThat(jsonGroups, hasGroup(group1));
424 assertThat(jsonGroups, hasGroup(group2));
425 assertThat(jsonGroups, hasGroup(group3));
426 assertThat(jsonGroups, hasGroup(group4));
427 }
428
429 /**
430 * Tests the result of a rest api GET for a device.
431 */
432 @Test
433 public void testGroupsSingleDevice() {
434 setupMockGroups();
435 final Set<Group> groups = new HashSet<>();
436 groups.add(group5);
437 groups.add(group6);
438 expect(mockGroupService.getGroups(anyObject()))
439 .andReturn(groups).anyTimes();
440 replay(mockGroupService);
441 replay(mockDeviceService);
Jian Li9d616492016-03-09 10:52:49 -0800442 final WebTarget wt = target();
443 final String response = wt.path("groups/" + deviceId3).request().get(String.class);
Jian Li80cfe452016-01-14 16:04:58 -0800444 final JsonObject result = Json.parse(response).asObject();
Jian Liecb3c0f2015-12-15 10:07:49 -0800445 assertThat(result, notNullValue());
446
447 assertThat(result.names(), hasSize(1));
448 assertThat(result.names().get(0), is("groups"));
Jian Li2e02fab2016-02-25 15:45:59 +0900449 final JsonArray jsonGroups = result.get("groups").asArray();
450 assertThat(jsonGroups, notNullValue());
451 assertThat(jsonGroups, hasGroup(group5));
452 assertThat(jsonGroups, hasGroup(group6));
Jian Liecb3c0f2015-12-15 10:07:49 -0800453 }
454
455 /**
Jian Lia7f86ce2015-12-20 13:42:10 -0800456 * Test the result of a rest api GET with specifying device id and appcookie.
457 */
458 @Test
459 public void testGroupByDeviceIdAndAppCookie() {
460 setupMockGroups();
461 expect(mockGroupService.getGroup(anyObject(), anyObject()))
462 .andReturn(group5).anyTimes();
463 replay(mockGroupService);
Jian Li9d616492016-03-09 10:52:49 -0800464 final WebTarget wt = target();
Varun Sharma1853b3f2016-07-18 14:37:13 +0530465 final String response = wt.path("groups/" + deviceId3 + "/" + "0x111")
Jian Li9d616492016-03-09 10:52:49 -0800466 .request().get(String.class);
Jian Li80cfe452016-01-14 16:04:58 -0800467 final JsonObject result = Json.parse(response).asObject();
Jian Lia7f86ce2015-12-20 13:42:10 -0800468 assertThat(result, notNullValue());
469
470 assertThat(result.names(), hasSize(1));
471 assertThat(result.names().get(0), is("groups"));
472 final JsonArray jsonFlows = result.get("groups").asArray();
473 assertThat(jsonFlows, notNullValue());
474 assertThat(jsonFlows, hasGroup(group5));
475 }
476
477 /**
478 * Test whether the REST API returns 404 if no entry has been found.
479 */
480 @Test
481 public void testGroupByDeviceIdAndAppCookieNull() {
482 setupMockGroups();
483 expect(mockGroupService.getGroup(anyObject(), anyObject()))
484 .andReturn(null).anyTimes();
485 replay(mockGroupService);
Jian Li9d616492016-03-09 10:52:49 -0800486 final WebTarget wt = target();
Varun Sharma1853b3f2016-07-18 14:37:13 +0530487 final Response response = wt.path("groups/" + deviceId3 + "/" + "0x222").request().get();
Jian Lia7f86ce2015-12-20 13:42:10 -0800488
489 assertEquals(404, response.getStatus());
490 }
491
492 /**
Jian Liecb3c0f2015-12-15 10:07:49 -0800493 * Tests creating a group with POST.
494 */
495 @Test
496 public void testPost() {
497 mockGroupService.addGroup(anyObject());
498 expectLastCall();
499 replay(mockGroupService);
500
Jian Li9d616492016-03-09 10:52:49 -0800501 WebTarget wt = target();
Jian Liecb3c0f2015-12-15 10:07:49 -0800502 InputStream jsonStream = GroupsResourceTest.class
503 .getResourceAsStream("post-group.json");
504
Jian Li9d616492016-03-09 10:52:49 -0800505 Response response = wt.path("groups/of:0000000000000001")
506 .request(MediaType.APPLICATION_JSON_TYPE)
507 .post(Entity.json(jsonStream));
Jian Liecb3c0f2015-12-15 10:07:49 -0800508 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
Jian Li9d616492016-03-09 10:52:49 -0800509 String location = response.getLocation().getPath();
510 assertThat(location, Matchers.startsWith("/groups/of:0000000000000001/"));
Jian Liecb3c0f2015-12-15 10:07:49 -0800511 }
512
513 /**
514 * Tests deleting a group.
515 */
516 @Test
517 public void testDelete() {
518 setupMockGroups();
519 mockGroupService.removeGroup(anyObject(), anyObject(), anyObject());
520 expectLastCall();
521 replay(mockGroupService);
522
Jian Li9d616492016-03-09 10:52:49 -0800523 WebTarget wt = target();
Jian Liecb3c0f2015-12-15 10:07:49 -0800524
Varun Sharma1853b3f2016-07-18 14:37:13 +0530525 String location = "/groups/1/0x111";
Jian Liecb3c0f2015-12-15 10:07:49 -0800526
Jian Li9d616492016-03-09 10:52:49 -0800527 Response deleteResponse = wt.path(location)
528 .request(MediaType.APPLICATION_JSON_TYPE)
529 .delete();
Jian Liecb3c0f2015-12-15 10:07:49 -0800530 assertThat(deleteResponse.getStatus(),
531 is(HttpURLConnection.HTTP_NO_CONTENT));
532 }
533}