blob: bcf5d17aebdc4b597ba5f78f1b729cd110d97cfc [file] [log] [blame]
Jian Li5c411232015-12-16 15:29:16 -08001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2016-present Open Networking Laboratory
Jian Li5c411232015-12-16 15:29:16 -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 Li5c411232015-12-16 15:29:16 -080018
Jian Li80cfe452016-01-14 16:04:58 -080019import com.eclipsesource.json.Json;
Jian Li5c411232015-12-16 15:29:16 -080020import com.eclipsesource.json.JsonArray;
21import com.eclipsesource.json.JsonObject;
22import com.google.common.collect.ImmutableSet;
Jian Li5c411232015-12-16 15:29:16 -080023import org.hamcrest.Description;
Jian Li9d616492016-03-09 10:52:49 -080024import org.hamcrest.Matchers;
Jian Li5c411232015-12-16 15:29:16 -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.MeterCodec;
35import org.onosproject.core.ApplicationId;
36import org.onosproject.core.CoreService;
37import org.onosproject.core.DefaultApplicationId;
38import org.onosproject.net.DefaultDevice;
39import org.onosproject.net.Device;
40import org.onosproject.net.DeviceId;
41import org.onosproject.net.NetTestTools;
42import org.onosproject.net.device.DeviceService;
43import org.onosproject.net.meter.Band;
44import org.onosproject.net.meter.DefaultBand;
45import org.onosproject.net.meter.Meter;
46import org.onosproject.net.meter.MeterId;
47import org.onosproject.net.meter.MeterService;
48import org.onosproject.net.meter.MeterState;
49
Jian Li9d616492016-03-09 10:52:49 -080050import javax.ws.rs.client.Entity;
51import javax.ws.rs.client.WebTarget;
Jian Li5c411232015-12-16 15:29:16 -080052import javax.ws.rs.core.MediaType;
Jian Li9d616492016-03-09 10:52:49 -080053import javax.ws.rs.core.Response;
Jian Li5c411232015-12-16 15:29:16 -080054import java.io.InputStream;
55import java.net.HttpURLConnection;
56import java.util.ArrayList;
57import java.util.Collection;
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;
Jian Li9d616492016-03-09 10:52:49 -080072import static org.hamcrest.Matchers.notNullValue;
Jian Li5e5734f2016-01-06 00:33:43 -080073import static org.junit.Assert.assertEquals;
Jian Li5c411232015-12-16 15:29:16 -080074import static org.junit.Assert.assertThat;
Jian Li5c411232015-12-16 15:29:16 -080075import static org.onosproject.net.NetTestTools.APP_ID;
76
77/**
78 * Unit tests for meters REST APIs.
79 */
80public class MetersResourceTest extends ResourceTest {
81 final MeterService mockMeterService = createMock(MeterService.class);
82 CoreService mockCoreService = createMock(CoreService.class);
83 final DeviceService mockDeviceService = createMock(DeviceService.class);
84
85 final HashMap<DeviceId, Set<Meter>> meters = new HashMap<>();
86
87 final DeviceId deviceId1 = DeviceId.deviceId("1");
88 final DeviceId deviceId2 = DeviceId.deviceId("2");
89 final DeviceId deviceId3 = DeviceId.deviceId("3");
90 final Device device1 = new DefaultDevice(null, deviceId1, Device.Type.OTHER,
91 "", "", "", "", null);
92 final Device device2 = new DefaultDevice(null, deviceId2, Device.Type.OTHER,
93 "", "", "", "", null);
94
95 final MockMeter meter1 = new MockMeter(deviceId1, 1, 111, 1);
96 final MockMeter meter2 = new MockMeter(deviceId1, 2, 222, 2);
97 final MockMeter meter3 = new MockMeter(deviceId2, 3, 333, 3);
98 final MockMeter meter4 = new MockMeter(deviceId2, 4, 444, 4);
99 final MockMeter meter5 = new MockMeter(deviceId3, 5, 555, 5);
100
101 /**
102 * Mock class for a meter.
103 */
104 private static class MockMeter implements Meter {
105
106 final DeviceId deviceId;
107 final ApplicationId appId;
108 final MeterId meterId;
109 final long baseValue;
110 final List<Band> bandList;
111
112 public MockMeter(DeviceId deviceId, int appId, long meterId, int id) {
113 this.deviceId = deviceId;
114 this.appId = new DefaultApplicationId(appId, String.valueOf(appId));
115 this.baseValue = id * 200;
116 this.meterId = MeterId.meterId(meterId);
117
118 Band band = DefaultBand.builder()
119 .ofType(Band.Type.REMARK)
120 .withRate(10)
121 .dropPrecedence((short) 20)
122 .burstSize(30).build();
123
124 this.bandList = new ArrayList<>();
125 this.bandList.add(band);
126 }
127
128 @Override
129 public DeviceId deviceId() {
130 return this.deviceId;
131 }
132
133 @Override
134 public MeterId id() {
135 return this.meterId;
136 }
137
138 @Override
139 public ApplicationId appId() {
140 return this.appId;
141 }
142
143 @Override
144 public Unit unit() {
145 return Unit.KB_PER_SEC;
146 }
147
148 @Override
149 public boolean isBurst() {
150 return false;
151 }
152
153 @Override
154 public Collection<Band> bands() {
155 return this.bandList;
156 }
157
158 @Override
159 public MeterState state() {
160 return MeterState.ADDED;
161 }
162
163 @Override
164 public long life() {
165 return baseValue + 11;
166 }
167
168 @Override
169 public long referenceCount() {
170 return baseValue + 22;
171 }
172
173 @Override
174 public long packetsSeen() {
175 return baseValue + 33;
176 }
177
178 @Override
179 public long bytesSeen() {
180 return baseValue + 44;
181 }
182 }
183
184 /**
185 * Populates some meters used as testing data.
186 */
187 private void setupMockMeters() {
188 final Set<Meter> meters1 = new HashSet<>();
189 meters1.add(meter1);
190 meters1.add(meter2);
191
192 final Set<Meter> meters2 = new HashSet<>();
193 meters2.add(meter3);
194 meters2.add(meter4);
195
196 meters.put(deviceId1, meters1);
197 meters.put(deviceId2, meters2);
198
199 Set<Meter> allMeters = new HashSet<>();
200 for (DeviceId deviceId : meters.keySet()) {
201 allMeters.addAll(meters.get(deviceId));
202 }
203
204 expect(mockMeterService.getAllMeters()).andReturn(allMeters).anyTimes();
205 }
206
207 /**
208 * Sets up the global values for all the tests.
209 */
210 @Before
211 public void setUpTest() {
212 // Mock device service
213 expect(mockDeviceService.getDevice(deviceId1))
214 .andReturn(device1);
215 expect(mockDeviceService.getDevice(deviceId2))
216 .andReturn(device2);
217 expect(mockDeviceService.getDevices())
218 .andReturn(ImmutableSet.of(device1, device2));
219
220 // Mock Core Service
221 expect(mockCoreService.getAppId(anyShort()))
222 .andReturn(NetTestTools.APP_ID).anyTimes();
223 expect(mockCoreService.registerApplication(MeterCodec.REST_APP_ID))
224 .andReturn(APP_ID).anyTimes();
225 replay(mockCoreService);
226
227 // Register the services needed for the test
228 final CodecManager codecService = new CodecManager();
229 codecService.activate();
230 ServiceDirectory testDirectory =
231 new TestServiceDirectory()
232 .add(MeterService.class, mockMeterService)
233 .add(DeviceService.class, mockDeviceService)
234 .add(CodecService.class, codecService)
235 .add(CoreService.class, mockCoreService);
236
237 BaseResource.setServiceDirectory(testDirectory);
238 }
239
240 /**
241 * Cleans up and verifies the mocks.
242 */
243 @After
244 public void tearDownTest() {
245 verify(mockMeterService);
246 verify(mockCoreService);
247 }
248
249 /**
250 * Hamcrest matcher to check that a meter representation in JSON matches
251 * the actual meter.
252 */
253 public static class MeterJsonMatcher extends TypeSafeMatcher<JsonObject> {
254 private final Meter meter;
255 private String reason = "";
256
257 public MeterJsonMatcher(Meter meterValue) {
258 this.meter = meterValue;
259 }
260
261 @Override
262 protected boolean matchesSafely(JsonObject jsonMeter) {
263
264 // check application id
265 final String jsonAppId = jsonMeter.get("appId").asString();
266 final String appId = meter.appId().toString();
267 if (!jsonAppId.equals(appId)) {
268 reason = "appId " + meter.appId().toString();
269 return false;
270 }
271
272 // check device id
273 final String jsonDeviceId = jsonMeter.get("deviceId").asString();
274 if (!jsonDeviceId.equals(meter.deviceId().toString())) {
275 reason = "deviceId " + meter.deviceId();
276 return false;
277 }
278
279 // check band array
280 if (meter.bands() != null) {
281 final JsonArray jsonBands = jsonMeter.get("bands").asArray();
282 if (meter.bands().size() != jsonBands.size()) {
283 reason = "bands array size of " +
284 Integer.toString(meter.bands().size());
285 return false;
286 }
287 for (final Band band : meter.bands()) {
288 boolean bandFound = false;
289 for (int bandIndex = 0; bandIndex < jsonBands.size(); bandIndex++) {
290 final String jsonType = jsonBands.get(bandIndex).asObject().get("type").asString();
291 final String bandType = band.type().name();
292 if (jsonType.equals(bandType)) {
293 bandFound = true;
294 }
295 }
296 if (!bandFound) {
297 reason = "meter band " + band.toString();
298 return false;
299 }
300 }
301 }
302
303 return true;
304 }
305
306 @Override
307 public void describeTo(Description description) {
308 description.appendText(reason);
309 }
310 }
311
312 private static MeterJsonMatcher matchesMeter(Meter meter) {
313 return new MeterJsonMatcher(meter);
314 }
315
316 /**
317 * Hamcrest matcher to check that a meter is represented properly in a JSON
318 * array of meters.
319 */
320 public static class MeterJsonArrayMatcher extends TypeSafeMatcher<JsonArray> {
321 private final Meter meter;
322 private String reason = "";
323
324 public MeterJsonArrayMatcher(Meter meterValue) {
325 meter = meterValue;
326 }
327
328 @Override
329 protected boolean matchesSafely(JsonArray json) {
330 boolean meterFound = false;
331 for (int jsonMeterIndex = 0; jsonMeterIndex < json.size(); jsonMeterIndex++) {
332 final JsonObject jsonMeter = json.get(jsonMeterIndex).asObject();
333
334 final String meterId = meter.id().toString();
335 final String jsonMeterId = jsonMeter.get("id").asString();
336 if (jsonMeterId.equals(meterId)) {
337 meterFound = true;
338
339 assertThat(jsonMeter, matchesMeter(meter));
340 }
341 }
342 if (!meterFound) {
343 reason = "Meter with id " + meter.id().toString() + " not found";
344 return false;
345 } else {
346 return true;
347 }
348 }
349
350 @Override
351 public void describeTo(Description description) {
352 description.appendText(reason);
353 }
354 }
355
356 /**
357 * Factory to allocate a meter array matcher.
358 *
359 * @param meter meter object we are looking for
360 * @return matcher
361 */
362 private static MeterJsonArrayMatcher hasMeter(Meter meter) {
363 return new MeterJsonArrayMatcher(meter);
364 }
365
366 @Test
367 public void testMeterEmptyArray() {
368 expect(mockMeterService.getAllMeters()).andReturn(null).anyTimes();
369 replay(mockMeterService);
370 replay(mockDeviceService);
Jian Li9d616492016-03-09 10:52:49 -0800371 final WebTarget wt = target();
372 final String response = wt.path("meters").request().get(String.class);
Jian Li5c411232015-12-16 15:29:16 -0800373 assertThat(response, is("{\"meters\":[]}"));
374 }
375
376 /**
377 * Tests the result of the rest api GET when there are active meters.
378 */
379 @Test
380 public void testMetersPopulatedArray() {
381 setupMockMeters();
382 replay(mockMeterService);
383 replay(mockDeviceService);
Jian Li9d616492016-03-09 10:52:49 -0800384 final WebTarget wt = target();
385 final String response = wt.path("meters").request().get(String.class);
Jian Li80cfe452016-01-14 16:04:58 -0800386 final JsonObject result = Json.parse(response).asObject();
Jian Li5c411232015-12-16 15:29:16 -0800387 assertThat(result, notNullValue());
388
389 assertThat(result.names(), hasSize(1));
390 assertThat(result.names().get(0), is("meters"));
391 final JsonArray jsonMeters = result.get("meters").asArray();
392 assertThat(jsonMeters, notNullValue());
393 assertThat(jsonMeters, hasMeter(meter1));
394 assertThat(jsonMeters, hasMeter(meter2));
395 assertThat(jsonMeters, hasMeter(meter3));
396 assertThat(jsonMeters, hasMeter(meter4));
397 }
398
399 /**
Jian Lia4faf4f2016-01-06 00:22:52 -0800400 * Tests the results of a rest api GET for a device.
401 */
402 @Test
403 public void testMeterSingleDevice() {
404 setupMockMeters();
405
406 final Set<Meter> meters1 = new HashSet<>();
407 meters1.add(meter1);
408 meters1.add(meter2);
409
410 expect(mockMeterService.getMeters(anyObject())).andReturn(meters1).anyTimes();
411 replay(mockMeterService);
412 replay(mockDeviceService);
413
Jian Li9d616492016-03-09 10:52:49 -0800414 final WebTarget wt = target();
415 final String response = wt.path("meters/" + deviceId1.toString()).request().get(String.class);
Jian Li80cfe452016-01-14 16:04:58 -0800416 final JsonObject result = Json.parse(response).asObject();
Jian Lia4faf4f2016-01-06 00:22:52 -0800417 assertThat(result, notNullValue());
418
419 assertThat(result.names(), hasSize(1));
420 assertThat(result.names().get(0), is("meters"));
421 final JsonArray jsonMeters = result.get("meters").asArray();
422 assertThat(jsonMeters, notNullValue());
423 assertThat(jsonMeters, hasMeter(meter1));
424 assertThat(jsonMeters, hasMeter(meter2));
425 }
426
427 /**
428 * Tests the result of a rest api GET for a device with meter id.
Jian Li5c411232015-12-16 15:29:16 -0800429 */
430 @Test
431 public void testMeterSingleDeviceWithId() {
432 setupMockMeters();
433
434 expect(mockMeterService.getMeter(anyObject(), anyObject()))
435 .andReturn(meter5).anyTimes();
436 replay(mockMeterService);
437 replay(mockDeviceService);
438
Jian Li9d616492016-03-09 10:52:49 -0800439 final WebTarget wt = target();
440 final String response = wt.path("meters/" + deviceId3.toString()
441 + "/" + meter5.id().id()).request().get(String.class);
Jian Li80cfe452016-01-14 16:04:58 -0800442 final JsonObject result = Json.parse(response).asObject();
Jian Li5c411232015-12-16 15:29:16 -0800443 assertThat(result, notNullValue());
444
445 assertThat(result.names(), hasSize(1));
446 assertThat(result.names().get(0), is("meters"));
Jian Li2e02fab2016-02-25 15:45:59 +0900447 final JsonArray jsonMeters = result.get("meters").asArray();
448 assertThat(jsonMeters, notNullValue());
449 assertThat(jsonMeters, hasMeter(meter5));
Jian Li5c411232015-12-16 15:29:16 -0800450 }
451
452 /**
Jian Li5e5734f2016-01-06 00:33:43 -0800453 * Test whether the REST API returns 404 if no entry has been found.
454 */
455 @Test
456 public void testMeterByDeviceIdAndMeterId() {
457 setupMockMeters();
458
459 expect(mockMeterService.getMeter(anyObject(), anyObject()))
460 .andReturn(null).anyTimes();
461 replay(mockMeterService);
462
Jian Li9d616492016-03-09 10:52:49 -0800463 final WebTarget wt = target();
464 final Response response = wt.path("meters/" + deviceId3.toString()
465 + "/" + "888").request().get();
Jian Li5e5734f2016-01-06 00:33:43 -0800466
467 assertEquals(404, response.getStatus());
468 }
469
470 /**
Jian Li5c411232015-12-16 15:29:16 -0800471 * Tests creating a meter with POST.
472 */
473 @Test
474 public void testPost() {
475 mockMeterService.submit(anyObject());
476 expectLastCall().andReturn(meter5).anyTimes();
477 replay(mockMeterService);
478
Jian Li9d616492016-03-09 10:52:49 -0800479 WebTarget wt = target();
Jian Li5c411232015-12-16 15:29:16 -0800480 InputStream jsonStream = MetersResourceTest.class
481 .getResourceAsStream("post-meter.json");
482
Jian Li9d616492016-03-09 10:52:49 -0800483 Response response = wt.path("meters/of:0000000000000001")
484 .request(MediaType.APPLICATION_JSON_TYPE)
485 .post(Entity.json(jsonStream));
Jian Li5c411232015-12-16 15:29:16 -0800486 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
Jian Li9d616492016-03-09 10:52:49 -0800487 String location = response.getLocation().getPath();
488 assertThat(location, Matchers.startsWith("/meters/of:0000000000000001/"));
Jian Li5c411232015-12-16 15:29:16 -0800489 }
490
491 /**
492 * Tests deleting a meter.
493 */
494 @Test
495 public void testDelete() {
496 setupMockMeters();
497 expect(mockMeterService.getMeter(anyObject(), anyObject()))
498 .andReturn(meter5).anyTimes();
499 mockMeterService.withdraw(anyObject(), anyObject());
500 expectLastCall();
501 replay(mockMeterService);
502
Jian Li9d616492016-03-09 10:52:49 -0800503 WebTarget wt = target();
Jian Li5c411232015-12-16 15:29:16 -0800504
505 String location = "/meters/3/555";
506
Jian Li9d616492016-03-09 10:52:49 -0800507 Response deleteResponse = wt.path(location)
508 .request(MediaType.APPLICATION_JSON_TYPE)
509 .delete();
Jian Li5c411232015-12-16 15:29:16 -0800510 assertThat(deleteResponse.getStatus(),
511 is(HttpURLConnection.HTTP_NO_CONTENT));
512 }
513}