blob: db322e36a36f520de22395ddef806bef5716b8bf [file] [log] [blame]
Jian Liecb3c0f2015-12-15 10:07:49 -08001/*
2 * Copyright 2014-2015 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 */
16
17package org.onosproject.rest;
18
19import com.eclipsesource.json.JsonArray;
20import com.eclipsesource.json.JsonObject;
21import com.google.common.collect.ImmutableSet;
22import com.sun.jersey.api.client.ClientResponse;
23import com.sun.jersey.api.client.WebResource;
24import org.hamcrest.Description;
25import 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;
Jian Lia7f86ce2015-12-20 13:42:10 -080052import org.onosproject.rest.resources.CoreWebApplication;
Jian Liecb3c0f2015-12-15 10:07:49 -080053
54import javax.ws.rs.core.MediaType;
55import java.io.InputStream;
56import java.net.HttpURLConnection;
57import java.util.ArrayList;
58import java.util.HashMap;
59import java.util.HashSet;
60import java.util.List;
61import java.util.Set;
62
63import static org.easymock.EasyMock.anyObject;
64import static org.easymock.EasyMock.anyShort;
65import static org.easymock.EasyMock.createMock;
66import static org.easymock.EasyMock.expect;
67import static org.easymock.EasyMock.expectLastCall;
68import static org.easymock.EasyMock.replay;
69import static org.easymock.EasyMock.verify;
70import static org.hamcrest.Matchers.hasSize;
71import static org.hamcrest.Matchers.is;
72import static org.hamcrest.Matchers.notNullValue;
73import static org.junit.Assert.assertThat;
Jian Lia7f86ce2015-12-20 13:42:10 -080074import static org.junit.Assert.assertEquals;
Jian Liecb3c0f2015-12-15 10:07:49 -080075import static org.onosproject.net.NetTestTools.APP_ID;
76
77/**
78 * Unit tests for Groups REST APIs.
79 */
80public class GroupsResourceTest extends ResourceTest {
81 final GroupService mockGroupService = createMock(GroupService.class);
82 CoreService mockCoreService = createMock(CoreService.class);
83 final DeviceService mockDeviceService = createMock(DeviceService.class);
84
85 final HashMap<DeviceId, Set<Group>> groups = new HashMap<>();
86
87
88 final DeviceId deviceId1 = DeviceId.deviceId("1");
89 final DeviceId deviceId2 = DeviceId.deviceId("2");
90 final DeviceId deviceId3 = DeviceId.deviceId("3");
91 final Device device1 = new DefaultDevice(null, deviceId1, Device.Type.OTHER,
92 "", "", "", "", null);
93 final Device device2 = new DefaultDevice(null, deviceId2, Device.Type.OTHER,
94 "", "", "", "", null);
95
96 final MockGroup group1 = new MockGroup(deviceId1, 1, "111", 1);
97 final MockGroup group2 = new MockGroup(deviceId1, 2, "222", 2);
98
99 final MockGroup group3 = new MockGroup(deviceId2, 3, "333", 3);
100 final MockGroup group4 = new MockGroup(deviceId2, 4, "444", 4);
101
102 final MockGroup group5 = new MockGroup(deviceId3, 5, "555", 5);
103 final MockGroup group6 = new MockGroup(deviceId3, 6, "666", 6);
104
Jian Lia7f86ce2015-12-20 13:42:10 -0800105 public GroupsResourceTest() {
106 super(CoreWebApplication.class);
107 }
108
Jian Liecb3c0f2015-12-15 10:07:49 -0800109 /**
110 * Mock class for a group.
111 */
112 private static class MockGroup implements Group {
113
114 final DeviceId deviceId;
115 final ApplicationId appId;
116 final GroupKey appCookie;
117 final long baseValue;
118 final List<GroupBucket> bucketList;
119 GroupBuckets buckets;
120
121 public MockGroup(DeviceId deviceId, int appId, String appCookie, int id) {
122 this.deviceId = deviceId;
123 this.appId = new DefaultApplicationId(appId, String.valueOf(appId));
124 this.appCookie = new DefaultGroupKey(appCookie.getBytes());
125 this.baseValue = id * 100;
126 this.bucketList = new ArrayList<>();
127 this.buckets = new GroupBuckets(bucketList);
128 }
129
130 @Override
131 public GroupId id() {
132 return new DefaultGroupId((int) baseValue + 55);
133 }
134
135 @Override
136 public GroupState state() {
137 return GroupState.ADDED;
138 }
139
140 @Override
141 public long life() {
142 return baseValue + 11;
143 }
144
145 @Override
146 public long packets() {
147 return baseValue + 22;
148 }
149
150 @Override
151 public long bytes() {
152 return baseValue + 33;
153 }
154
155 @Override
156 public long referenceCount() {
157 return baseValue + 44;
158 }
159
160 @Override
161 public Type type() {
162 return GroupDescription.Type.ALL;
163 }
164
165 @Override
166 public DeviceId deviceId() {
167 return this.deviceId;
168 }
169
170 @Override
171 public ApplicationId appId() {
172 return this.appId;
173 }
174
175 @Override
176 public GroupKey appCookie() {
177 return this.appCookie;
178 }
179
180 @Override
181 public Integer givenGroupId() {
182 return (int) baseValue + 55;
183 }
184
185 @Override
186 public GroupBuckets buckets() {
187 return this.buckets;
188 }
189 }
190
191 /**
192 * Populates some groups used as testing data.
193 */
194 private void setupMockGroups() {
195 final Set<Group> groups1 = new HashSet<>();
196 groups1.add(group1);
197 groups1.add(group2);
198
199 final Set<Group> groups2 = new HashSet<>();
200 groups2.add(group3);
201 groups2.add(group4);
202
203 groups.put(deviceId1, groups1);
204 groups.put(deviceId2, groups2);
205
206 expect(mockGroupService.getGroups(deviceId1))
207 .andReturn(groups.get(deviceId1)).anyTimes();
208 expect(mockGroupService.getGroups(deviceId2))
209 .andReturn(groups.get(deviceId2)).anyTimes();
210 }
211
212 /**
213 * Sets up the global values for all the tests.
214 */
215 @Before
216 public void setUpTest() {
217 // Mock device service
218 expect(mockDeviceService.getDevice(deviceId1))
219 .andReturn(device1);
220 expect(mockDeviceService.getDevice(deviceId2))
221 .andReturn(device2);
222 expect(mockDeviceService.getDevices())
223 .andReturn(ImmutableSet.of(device1, device2));
224
225 // Mock Core Service
226 expect(mockCoreService.getAppId(anyShort()))
227 .andReturn(NetTestTools.APP_ID).anyTimes();
228 expect(mockCoreService.registerApplication(GroupCodec.REST_APP_ID))
229 .andReturn(APP_ID).anyTimes();
230 replay(mockCoreService);
231
232 // Register the services needed for the test
233 final CodecManager codecService = new CodecManager();
234 codecService.activate();
235 ServiceDirectory testDirectory =
236 new TestServiceDirectory()
237 .add(GroupService.class, mockGroupService)
238 .add(DeviceService.class, mockDeviceService)
239 .add(CodecService.class, codecService)
240 .add(CoreService.class, mockCoreService);
241
242 BaseResource.setServiceDirectory(testDirectory);
243 }
244
245 /**
246 * Cleans up and verifies the mocks.
247 */
248 @After
249 public void tearDownTest() {
250 verify(mockGroupService);
251 verify(mockCoreService);
252 }
253
254 /**
255 * Hamcrest matcher to check that a group representation in JSON matches
256 * the actual group.
257 */
258 public static class GroupJsonMatcher extends TypeSafeMatcher<JsonObject> {
259 private final Group group;
260 private final String expectedAppId;
261 private String reason = "";
262
263 public GroupJsonMatcher(Group groupValue, String expectedAppIdValue) {
264 group = groupValue;
265 expectedAppId = expectedAppIdValue;
266 }
267
268 @Override
269 public boolean matchesSafely(JsonObject jsonGroup) {
270 // check id
271 final String jsonId = jsonGroup.get("id").asString();
272 final String groupId = group.id().toString();
273 if (!jsonId.equals(groupId)) {
274 reason = "id " + group.id().toString();
275 return false;
276 }
277
278 // check application id
279 final String jsonAppId = jsonGroup.get("appId").asString();
280 final String appId = group.appId().toString();
281 if (!jsonAppId.equals(appId)) {
282 reason = "appId " + group.appId().toString();
283 return false;
284 }
285
286 // check device id
287 final String jsonDeviceId = jsonGroup.get("deviceId").asString();
288 if (!jsonDeviceId.equals(group.deviceId().toString())) {
289 reason = "deviceId " + group.deviceId();
290 return false;
291 }
292
293 // check bucket array
294 if (group.buckets().buckets() != null) {
295 final JsonArray jsonBuckets = jsonGroup.get("buckets").asArray();
296 if (group.buckets().buckets().size() != jsonBuckets.size()) {
297 reason = "buckets array size of " +
298 Integer.toString(group.buckets().buckets().size());
299 return false;
300 }
301 for (final GroupBucket groupBucket : group.buckets().buckets()) {
302 boolean groupBucketFound = false;
303 for (int groupBucketIndex = 0; groupBucketIndex < jsonBuckets.size(); groupBucketIndex++) {
304 final String jsonType = jsonBuckets.get(groupBucketIndex).asObject().get("type").asString();
305 final String bucketType = groupBucket.type().name();
306 if (jsonType.equals(bucketType)) {
307 groupBucketFound = true;
308 }
309 }
310 if (!groupBucketFound) {
311 reason = "group bucket " + groupBucket.toString();
312 return false;
313 }
314 }
315 }
316
317 return true;
318 }
319
320 @Override
321 public void describeTo(Description description) {
322 description.appendText(reason);
323 }
324 }
325
326 /**
327 * Factory to allocate a group matcher.
328 *
329 * @param group group object we are looking for
330 * @return matcher
331 */
332 private static GroupJsonMatcher matchesGroup(Group group, String expectedAppName) {
333 return new GroupJsonMatcher(group, expectedAppName);
334 }
335
336 /**
337 * Hamcrest matcher to check that a group is represented properly in a JSON
338 * array of flows.
339 */
340 public static class GroupJsonArrayMatcher extends TypeSafeMatcher<JsonArray> {
341 private final Group group;
342 private String reason = "";
343
344 public GroupJsonArrayMatcher(Group groupValue) {
345 group = groupValue;
346 }
347
348 @Override
349 public boolean matchesSafely(JsonArray json) {
350 boolean groupFound = false;
351 for (int jsonGroupIndex = 0; jsonGroupIndex < json.size();
352 jsonGroupIndex++) {
353
354 final JsonObject jsonGroup = json.get(jsonGroupIndex).asObject();
355
356 final String groupId = group.id().toString();
357 final String jsonGroupId = jsonGroup.get("id").asString();
358 if (jsonGroupId.equals(groupId)) {
359 groupFound = true;
360
361 // We found the correct group, check attribute values
362 assertThat(jsonGroup, matchesGroup(group, APP_ID.name()));
363 }
364 }
365 if (!groupFound) {
366 reason = "Group with id " + group.id().toString() + " not found";
367 return false;
368 } else {
369 return true;
370 }
371 }
372
373 @Override
374 public void describeTo(Description description) {
375 description.appendText(reason);
376 }
377 }
378
379 /**
380 * Factory to allocate a group array matcher.
381 *
382 * @param group group object we are looking for
383 * @return matcher
384 */
385 private static GroupJsonArrayMatcher hasGroup(Group group) {
386 return new GroupJsonArrayMatcher(group);
387 }
388
389 /**
390 * Tests the result of the rest api GET when there are no groups.
391 */
392 @Test
393 public void testGroupsEmptyArray() {
394 expect(mockGroupService.getGroups(deviceId1)).andReturn(null).anyTimes();
395 expect(mockGroupService.getGroups(deviceId2)).andReturn(null).anyTimes();
396 replay(mockGroupService);
397 replay(mockDeviceService);
398 final WebResource rs = resource();
399 final String response = rs.path("groups").get(String.class);
400 assertThat(response, is("{\"groups\":[]}"));
401 }
402
403 /**
404 * Tests the result of the rest api GET when there are active groups.
405 */
406 @Test
407 public void testGroupsPopulatedArray() {
408 setupMockGroups();
409 replay(mockGroupService);
410 replay(mockDeviceService);
411 final WebResource rs = resource();
412 final String response = rs.path("groups").get(String.class);
413 final JsonObject result = JsonObject.readFrom(response);
414 assertThat(result, notNullValue());
415
416 assertThat(result.names(), hasSize(1));
417 assertThat(result.names().get(0), is("groups"));
418 final JsonArray jsonGroups = result.get("groups").asArray();
419 assertThat(jsonGroups, notNullValue());
420 assertThat(jsonGroups, hasGroup(group1));
421 assertThat(jsonGroups, hasGroup(group2));
422 assertThat(jsonGroups, hasGroup(group3));
423 assertThat(jsonGroups, hasGroup(group4));
424 }
425
426 /**
427 * Tests the result of a rest api GET for a device.
428 */
429 @Test
430 public void testGroupsSingleDevice() {
431 setupMockGroups();
432 final Set<Group> groups = new HashSet<>();
433 groups.add(group5);
434 groups.add(group6);
435 expect(mockGroupService.getGroups(anyObject()))
436 .andReturn(groups).anyTimes();
437 replay(mockGroupService);
438 replay(mockDeviceService);
439 final WebResource rs = resource();
440 final String response = rs.path("groups/" + deviceId3).get(String.class);
441 final JsonObject result = JsonObject.readFrom(response);
442 assertThat(result, notNullValue());
443
444 assertThat(result.names(), hasSize(1));
445 assertThat(result.names().get(0), is("groups"));
446 final JsonArray jsonFlows = result.get("groups").asArray();
447 assertThat(jsonFlows, notNullValue());
448 assertThat(jsonFlows, hasGroup(group5));
449 assertThat(jsonFlows, hasGroup(group6));
450 }
451
452 /**
Jian Lia7f86ce2015-12-20 13:42:10 -0800453 * Test the result of a rest api GET with specifying device id and appcookie.
454 */
455 @Test
456 public void testGroupByDeviceIdAndAppCookie() {
457 setupMockGroups();
458 expect(mockGroupService.getGroup(anyObject(), anyObject()))
459 .andReturn(group5).anyTimes();
460 replay(mockGroupService);
461 final WebResource rs = resource();
462 final String response = rs.path("groups/" + deviceId3 + "/" + "111").get(String.class);
463 final JsonObject result = JsonObject.readFrom(response);
464 assertThat(result, notNullValue());
465
466 assertThat(result.names(), hasSize(1));
467 assertThat(result.names().get(0), is("groups"));
468 final JsonArray jsonFlows = result.get("groups").asArray();
469 assertThat(jsonFlows, notNullValue());
470 assertThat(jsonFlows, hasGroup(group5));
471 }
472
473 /**
474 * Test whether the REST API returns 404 if no entry has been found.
475 */
476 @Test
477 public void testGroupByDeviceIdAndAppCookieNull() {
478 setupMockGroups();
479 expect(mockGroupService.getGroup(anyObject(), anyObject()))
480 .andReturn(null).anyTimes();
481 replay(mockGroupService);
482 final WebResource rs = resource();
483 final ClientResponse response = rs.path("groups/" + deviceId3 + "/" + "222").get(ClientResponse.class);
484
485 assertEquals(404, response.getStatus());
486 }
487
488 /**
Jian Liecb3c0f2015-12-15 10:07:49 -0800489 * Tests creating a group with POST.
490 */
491 @Test
492 public void testPost() {
493 mockGroupService.addGroup(anyObject());
494 expectLastCall();
495 replay(mockGroupService);
496
497 WebResource rs = resource();
498 InputStream jsonStream = GroupsResourceTest.class
499 .getResourceAsStream("post-group.json");
500
501 ClientResponse response = rs.path("groups/of:0000000000000001")
502 .type(MediaType.APPLICATION_JSON_TYPE)
503 .post(ClientResponse.class, jsonStream);
504 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
505 }
506
507 /**
508 * Tests deleting a group.
509 */
510 @Test
511 public void testDelete() {
512 setupMockGroups();
513 mockGroupService.removeGroup(anyObject(), anyObject(), anyObject());
514 expectLastCall();
515 replay(mockGroupService);
516
517 WebResource rs = resource();
518
519 String location = "/groups/1/111";
520
521 ClientResponse deleteResponse = rs.path(location)
522 .type(MediaType.APPLICATION_JSON_TYPE)
523 .delete(ClientResponse.class);
524 assertThat(deleteResponse.getStatus(),
525 is(HttpURLConnection.HTTP_NO_CONTENT));
526 }
527}