blob: 61b1b4a376a4101275c1cb97540b3a17d688d059 [file] [log] [blame]
Ray Milkey2b217142014-12-15 09:24:24 -08001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2016-present Open Networking Laboratory
Ray Milkey2b217142014-12-15 09:24:24 -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 */
Jian Li8ae91202016-03-24 14:36:16 -070016package org.onosproject.rest.resources;
Ray Milkey2b217142014-12-15 09:24:24 -080017
Author Namee252a002016-09-26 22:42:24 +053018import com.eclipsesource.json.Json;
19import com.eclipsesource.json.JsonArray;
20import com.eclipsesource.json.JsonObject;
21import com.eclipsesource.json.JsonValue;
Rafal Szaleckif97e0ed2017-06-01 13:07:18 +020022import com.fasterxml.jackson.databind.node.ObjectNode;
Ray Milkey2b217142014-12-15 09:24:24 -080023import org.hamcrest.Description;
Ray Milkeyb82c42b2015-06-30 09:42:20 -070024import org.hamcrest.Matchers;
Ray Milkey2b217142014-12-15 09:24:24 -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;
Author Namee252a002016-09-26 22:42:24 +053031import org.onlab.packet.MacAddress;
Ray Milkey2b217142014-12-15 09:24:24 -080032import org.onlab.rest.BaseResource;
33import org.onosproject.codec.CodecService;
34import org.onosproject.codec.impl.CodecManager;
Rafal Szaleckif97e0ed2017-06-01 13:07:18 +020035import org.onosproject.codec.impl.MockCodecContext;
Ray Milkey2b217142014-12-15 09:24:24 -080036import org.onosproject.core.ApplicationId;
Ayaka Koshibec06c89b2015-02-10 19:25:41 -080037import org.onosproject.core.CoreService;
Ray Milkey2b217142014-12-15 09:24:24 -080038import org.onosproject.core.DefaultApplicationId;
Author Namee252a002016-09-26 22:42:24 +053039import org.onosproject.core.GroupId;
Rafal Szaleckif97e0ed2017-06-01 13:07:18 +020040import org.onosproject.net.ConnectPoint;
41import org.onosproject.net.DefaultLink;
Author Namee252a002016-09-26 22:42:24 +053042import org.onosproject.net.DeviceId;
Rafal Szaleckif97e0ed2017-06-01 13:07:18 +020043import org.onosproject.net.Link;
Ray Milkey2b217142014-12-15 09:24:24 -080044import org.onosproject.net.NetworkResource;
Rafal Szaleckif97e0ed2017-06-01 13:07:18 +020045import org.onosproject.net.PortNumber;
Author Namee252a002016-09-26 22:42:24 +053046import org.onosproject.net.flow.DefaultTrafficSelector;
47import org.onosproject.net.flow.DefaultTrafficTreatment;
48import org.onosproject.net.flow.FlowEntry;
Ray Milkey634eb172017-04-05 14:48:50 -070049import org.onosproject.net.flow.FlowEntryAdapter;
Author Namee252a002016-09-26 22:42:24 +053050import org.onosproject.net.flow.FlowId;
51import org.onosproject.net.flow.FlowRule;
52import org.onosproject.net.flow.FlowRuleExtPayLoad;
53import org.onosproject.net.flow.FlowRuleService;
Carmelo Cascone41605742017-06-19 15:46:44 +090054import org.onosproject.net.flow.TableId;
Author Namee252a002016-09-26 22:42:24 +053055import org.onosproject.net.flow.TrafficSelector;
56import org.onosproject.net.flow.TrafficTreatment;
57import org.onosproject.net.flow.criteria.Criterion;
58import org.onosproject.net.flow.instructions.Instruction;
Ray Milkey7b158512015-07-21 16:32:43 -070059import org.onosproject.net.intent.FakeIntentManager;
Author Namee252a002016-09-26 22:42:24 +053060import org.onosproject.net.intent.FlowRuleIntent;
Ray Milkey2b217142014-12-15 09:24:24 -080061import org.onosproject.net.intent.Intent;
Ray Milkey2b217142014-12-15 09:24:24 -080062import org.onosproject.net.intent.IntentService;
Ray Milkey1534f8d2015-05-13 15:42:50 -070063import org.onosproject.net.intent.IntentState;
Ray Milkeyf9af43c2015-02-09 16:45:48 -080064import org.onosproject.net.intent.Key;
Thomas Vachuska2048c1f2017-05-10 19:32:22 -070065import org.onosproject.net.intent.MockIdGenerator;
Rafal Szaleckif97e0ed2017-06-01 13:07:18 +020066import org.onosproject.net.provider.ProviderId;
Ray Milkey2b217142014-12-15 09:24:24 -080067
Author Namee252a002016-09-26 22:42:24 +053068import javax.ws.rs.NotFoundException;
69import javax.ws.rs.client.Entity;
70import javax.ws.rs.client.WebTarget;
71import javax.ws.rs.core.MediaType;
72import javax.ws.rs.core.Response;
Rafal Szaleckif97e0ed2017-06-01 13:07:18 +020073import java.io.IOException;
Author Namee252a002016-09-26 22:42:24 +053074import java.io.InputStream;
75import java.net.HttpURLConnection;
76import java.util.ArrayList;
77import java.util.Collections;
78import java.util.HashSet;
79import java.util.List;
Rafal Szaleckif97e0ed2017-06-01 13:07:18 +020080import java.util.Set;
Author Namee252a002016-09-26 22:42:24 +053081import java.util.concurrent.TimeUnit;
Ray Milkey2b217142014-12-15 09:24:24 -080082
Author Namee252a002016-09-26 22:42:24 +053083import static java.util.concurrent.TimeUnit.SECONDS;
84import static org.easymock.EasyMock.*;
85import static org.hamcrest.Matchers.*;
Ray Milkey2b217142014-12-15 09:24:24 -080086import static org.junit.Assert.assertThat;
87import static org.junit.Assert.fail;
Ray Milkey43a28222015-02-23 13:57:58 -080088import static org.onosproject.net.intent.IntentTestsMocks.MockIntent;
Ray Milkey2b217142014-12-15 09:24:24 -080089
90/**
91 * Unit tests for Intents REST APIs.
92 */
Ray Milkey9c3d3362015-01-28 10:39:56 -080093public class IntentsResourceTest extends ResourceTest {
Author Namee252a002016-09-26 22:42:24 +053094
95
96 private static final String APPID = "appId";
97 private static final String CRITERIA = "criteria";
98 private static final String DEVICE_ID = "deviceId";
99 private static final String ID = "id";
100 private static final String INSTRUCTIONS = "instructions";
101 private static final String PATHS = "paths";
Rafal Szaleckif97e0ed2017-06-01 13:07:18 +0200102 private static final String INSTALLABLES = "installables";
103 private static final String RESOURCES = "resources";
104 private static final String DEVICE = "device";
105 private static final String PORT = "port";
106 private static final String SRC = "src";
107 private static final String DST = "dst";
Author Namee252a002016-09-26 22:42:24 +0530108 private static final String SELECTOR = "selector";
109 private static final String SPACE = " ";
110 private static final String TREATMENT = "treatment";
111 private static final String TYPE = "type";
112
Ray Milkey2b217142014-12-15 09:24:24 -0800113 final IntentService mockIntentService = createMock(IntentService.class);
Ayaka Koshibec06c89b2015-02-10 19:25:41 -0800114 final CoreService mockCoreService = createMock(CoreService.class);
Author Namee252a002016-09-26 22:42:24 +0530115 final FlowRuleService mockFlowService = createMock(FlowRuleService.class);
Ray Milkey2b217142014-12-15 09:24:24 -0800116 final HashSet<Intent> intents = new HashSet<>();
Author Namee252a002016-09-26 22:42:24 +0530117 final List<org.onosproject.net.intent.Intent> installableIntents = new ArrayList<>();
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800118 private static final ApplicationId APP_ID = new DefaultApplicationId(1, "test");
Ray Milkey2b217142014-12-15 09:24:24 -0800119
Author Namee252a002016-09-26 22:42:24 +0530120 final DeviceId deviceId1 = DeviceId.deviceId("1");
Rafal Szaleckif97e0ed2017-06-01 13:07:18 +0200121 final DeviceId deviceId2 = DeviceId.deviceId("2");
122 final DeviceId deviceId3 = DeviceId.deviceId("3");
123
124 final ConnectPoint connectPoint1 = new ConnectPoint(deviceId1, PortNumber.portNumber(1L));
125 final ConnectPoint connectPoint2 = new ConnectPoint(deviceId2, PortNumber.portNumber(1L));
126 final ConnectPoint connectPoint3 = new ConnectPoint(deviceId2, PortNumber.portNumber(2L));
127 final ConnectPoint connectPoint4 = new ConnectPoint(deviceId3, PortNumber.portNumber(1L));
Author Namee252a002016-09-26 22:42:24 +0530128
129 final TrafficTreatment treatment1 = DefaultTrafficTreatment.builder()
130 .setEthDst(MacAddress.BROADCAST)
131 .build();
132 final TrafficTreatment treatment2 = DefaultTrafficTreatment.builder()
133 .setEthDst(MacAddress.IPV4_MULTICAST)
134 .build();
135
136 final TrafficSelector selector1 = DefaultTrafficSelector.builder()
137 .matchEthType((short) 3)
138 .matchIPProtocol((byte) 9)
139 .build();
140 final TrafficSelector selector2 = DefaultTrafficSelector.builder()
141 .matchEthType((short) 4)
142 .matchIPProtocol((byte) 10)
143 .build();
144
145 final MockFlowEntry flow1 = new MockFlowEntry(deviceId1, 1, treatment1, selector1);
146 final MockFlowEntry flow2 = new MockFlowEntry(deviceId1, 2, treatment2, selector2);
147
148 final MockFlowRule flowRule1 = new MockFlowRule(deviceId1, 1, treatment1, selector1);
149 final MockFlowRule flowRule2 = new MockFlowRule(deviceId1, 2, treatment2, selector2);
150
Ray Milkey2b217142014-12-15 09:24:24 -0800151 private class MockResource implements NetworkResource {
152 int id;
153
154 MockResource(int id) {
155 this.id = id;
156 }
157
Ayaka Koshibec06c89b2015-02-10 19:25:41 -0800158 @Override
Ray Milkey2b217142014-12-15 09:24:24 -0800159 public String toString() {
160 return "Resource " + Integer.toString(id);
161 }
162 }
163
Ray Milkey2b217142014-12-15 09:24:24 -0800164 /**
Author Namee252a002016-09-26 22:42:24 +0530165 * Mock class for a flow entry.
166 */
Ray Milkey634eb172017-04-05 14:48:50 -0700167 private static class MockFlowEntry extends FlowEntryAdapter {
Author Namee252a002016-09-26 22:42:24 +0530168 final DeviceId deviceId;
169 final long baseValue;
170 TrafficTreatment treatment;
171 TrafficSelector selector;
172
173 public MockFlowEntry(DeviceId deviceId, long id,
174 TrafficTreatment treatment,
175 TrafficSelector selector) {
176 this.deviceId = deviceId;
177 this.baseValue = id * 100;
178 this.treatment = treatment;
179 this.selector = selector;
180 }
181
182 @Override
Author Namee252a002016-09-26 22:42:24 +0530183 public long life() {
184 return life(SECONDS);
185 }
186
187 @Override
kalagesa42019542017-03-14 18:00:47 +0530188 public FlowLiveType liveType() {
189 return FlowLiveType.IMMEDIATE;
190 }
191
192 @Override
Author Namee252a002016-09-26 22:42:24 +0530193 public long life(TimeUnit timeUnit) {
194 return SECONDS.convert(baseValue + 11, timeUnit);
195 }
196
197 @Override
198 public long packets() {
199 return baseValue + 22;
200 }
201
202 @Override
203 public long bytes() {
204 return baseValue + 33;
205 }
206
207 @Override
208 public long lastSeen() {
209 return baseValue + 44;
210 }
211
212 @Override
Author Namee252a002016-09-26 22:42:24 +0530213 public FlowId id() {
214 final long id = baseValue + 55;
215 return FlowId.valueOf(id);
216 }
217
218 @Override
219 public GroupId groupId() {
Yi Tsengfa394de2017-02-01 11:26:40 -0800220 return new GroupId(3);
Author Namee252a002016-09-26 22:42:24 +0530221 }
222
223 @Override
224 public short appId() {
225 return 1;
226 }
227
228 @Override
229 public int priority() {
230 return (int) (baseValue + 66);
231 }
232
233 @Override
234 public DeviceId deviceId() {
235 return deviceId;
236 }
237
238 @Override
239 public TrafficSelector selector() {
240 return selector;
241 }
242
243 @Override
244 public TrafficTreatment treatment() {
245 return treatment;
246 }
247
248 @Override
249 public int timeout() {
250 return (int) (baseValue + 77);
251 }
252
Author Namee252a002016-09-26 22:42:24 +0530253
254 @Override
255 public boolean exactMatch(FlowRule rule) {
256 return this.appId() == rule.appId() &&
257 this.deviceId().equals(rule.deviceId()) &&
258 this.id().equals(rule.id()) &&
259 this.treatment.equals(rule.treatment()) &&
260 this.selector().equals(rule.selector());
261 }
262
263 @Override
Author Namee252a002016-09-26 22:42:24 +0530264 public String toString() {
265 return id().id().toString();
266 }
267 }
268
269 /**
270 * Mock class for a flow rule.
271 */
272 private static class MockFlowRule implements FlowRule {
273
274 final DeviceId deviceId;
275 final long baseValue;
276 TrafficTreatment treatment;
277 TrafficSelector selector;
278
279 public MockFlowRule(DeviceId deviceId,
280 long id,
281 TrafficTreatment treatment,
282 TrafficSelector selector) {
283 this.deviceId = deviceId;
284 this.baseValue = id * 100;
285 this.treatment = treatment;
286 this.selector = selector;
287 }
288
289 @Override
290 public FlowId id() {
291 long id = baseValue + 55;
292 return FlowId.valueOf(id);
293 }
294
295 @Override
296 public short appId() {
297 return 1;
298 }
299
300 @Override
301 public GroupId groupId() {
Yi Tsengfa394de2017-02-01 11:26:40 -0800302 return new GroupId(3);
Author Namee252a002016-09-26 22:42:24 +0530303 }
304
305 @Override
306 public int priority() {
307 return 0;
308 }
309
310 @Override
311 public DeviceId deviceId() {
312 return deviceId;
313 }
314
315 @Override
316 public TrafficSelector selector() {
317 return selector;
318 }
319
320 @Override
321 public TrafficTreatment treatment() {
322 return treatment;
323 }
324
325 @Override
326 public int timeout() {
327 return (int) (baseValue + 77);
328 }
329
330 @Override
331 public int hardTimeout() {
332 return 0;
333 }
334
335 @Override
336 public FlowRemoveReason reason() {
337 return FlowRemoveReason.NO_REASON;
338 }
339
340 @Override
341 public boolean isPermanent() {
342 return false;
343 }
344
345 @Override
346 public int tableId() {
347 return 0;
348 }
349
350 @Override
Carmelo Cascone41605742017-06-19 15:46:44 +0900351 public TableId table() {
352 return DEFAULT_TABLE;
353 }
354
355 @Override
Author Namee252a002016-09-26 22:42:24 +0530356 public boolean exactMatch(FlowRule rule) {
357 return false;
358 }
359
360 @Override
361 public FlowRuleExtPayLoad payLoad() {
362 return null;
363 }
364 }
365
366 /**
Ray Milkey2b217142014-12-15 09:24:24 -0800367 * Hamcrest matcher to check that an intent representation in JSON matches
368 * the actual intent.
369 */
370 public static class IntentJsonMatcher extends TypeSafeMatcher<JsonObject> {
371 private final Intent intent;
372 private String reason = "";
373
374 public IntentJsonMatcher(Intent intentValue) {
375 intent = intentValue;
376 }
377
378 @Override
379 public boolean matchesSafely(JsonObject jsonIntent) {
380 // check id
381 final String jsonId = jsonIntent.get("id").asString();
382 if (!jsonId.equals(intent.id().toString())) {
383 reason = "id " + intent.id().toString();
384 return false;
385 }
386
387 // check application id
Ray Milkeyf7cb4012015-07-20 13:01:07 -0700388
Ray Milkey2b217142014-12-15 09:24:24 -0800389 final String jsonAppId = jsonIntent.get("appId").asString();
Ray Milkeyf7cb4012015-07-20 13:01:07 -0700390 final String appId = intent.appId().name();
391 if (!jsonAppId.equals(appId)) {
392 reason = "appId was " + jsonAppId;
Ray Milkey2b217142014-12-15 09:24:24 -0800393 return false;
394 }
395
396 // check intent type
397 final String jsonType = jsonIntent.get("type").asString();
Rafal Szaleckif97e0ed2017-06-01 13:07:18 +0200398 if (!intent.getClass().getSimpleName().equals(jsonType)) {
Ray Milkey2b217142014-12-15 09:24:24 -0800399 reason = "type MockIntent";
400 return false;
401 }
402
Ray Milkey1534f8d2015-05-13 15:42:50 -0700403 // check state field
404 final String jsonState = jsonIntent.get("state").asString();
Jon Halla3fcf672017-03-28 16:53:22 -0700405 if (!"INSTALLED".equals(jsonState)) {
Ray Milkey1534f8d2015-05-13 15:42:50 -0700406 reason = "state INSTALLED";
407 return false;
408 }
409
Ray Milkey2b217142014-12-15 09:24:24 -0800410 // check resources array
411 final JsonArray jsonResources = jsonIntent.get("resources").asArray();
412 if (intent.resources() != null) {
413 if (intent.resources().size() != jsonResources.size()) {
414 reason = "resources array size of " + Integer.toString(intent.resources().size());
415 return false;
416 }
417 for (final NetworkResource resource : intent.resources()) {
418 boolean resourceFound = false;
419 final String resourceString = resource.toString();
Rafal Szaleckif97e0ed2017-06-01 13:07:18 +0200420
421 if (resource instanceof Link) {
422 final Link resourceLink = (Link) resource;
423 MockCodecContext codecContext = new MockCodecContext();
424
425 for (int resourceIndex = 0; resourceIndex < jsonResources.size(); resourceIndex++) {
426 final ObjectNode value;
427 try {
428 value = (ObjectNode) codecContext.mapper()
429 .readTree(jsonResources.get(resourceIndex).toString());
430 } catch (IOException e) {
431 reason = "bad json";
432 return false;
433 }
434 final Link link = codecContext.codec(Link.class).decode(value, codecContext);
435 if (resourceLink.equals(link)) {
436 resourceFound = true;
437 }
438 }
439 } else {
440
441 for (int resourceIndex = 0; resourceIndex < jsonResources.size(); resourceIndex++) {
442 final JsonValue value = jsonResources.get(resourceIndex);
443 if (value.asString().equals(resourceString)) {
444 resourceFound = true;
445 }
Ray Milkey2b217142014-12-15 09:24:24 -0800446 }
447 }
448 if (!resourceFound) {
449 reason = "resource " + resourceString;
450 return false;
451 }
452 }
453 } else if (jsonResources.size() != 0) {
454 reason = "resources array empty";
455 return false;
456 }
457 return true;
458 }
459
460 @Override
461 public void describeTo(Description description) {
462 description.appendText(reason);
463 }
464 }
465
466 /**
467 * Factory to allocate an intent matcher.
468 *
469 * @param intent intent object we are looking for
470 * @return matcher
471 */
472 private static IntentJsonMatcher matchesIntent(Intent intent) {
473 return new IntentJsonMatcher(intent);
474 }
475
476 /**
Author Namee252a002016-09-26 22:42:24 +0530477 * Factory to allocate an IntentRelatedFlows matcher.
478 *
479 * @param pathEntries list of path conatining flow entries of a particular intent
480 * @param expectedAppId expected app id we are looking for
481 * @return matcher
482 */
483 private static IntentStatsJsonMatcher matchesRelatedFlowEntries(
484 List<List<FlowEntry>> pathEntries,
485 final String expectedAppId) {
486 return new IntentStatsJsonMatcher(pathEntries, expectedAppId);
487 }
488
489 /**
490 * Hamcrest matcher to check that an list of flowEntries in JSON matches
491 * the actual list of flow entries.
492 */
493 public static class IntentStatsJsonMatcher extends
494 TypeSafeMatcher<JsonObject> {
495
496 private final List<List<FlowEntry>> pathEntries;
497 private final String expectedAppId;
498 private String reason = "";
499
500 public IntentStatsJsonMatcher(
501 final List<List<FlowEntry>> pathEntries,
502 final String expectedAppId) {
503 this.pathEntries = pathEntries;
504 this.expectedAppId = expectedAppId;
505 }
506
507 @Override
508 public boolean matchesSafely(JsonObject jsonIntent) {
509 int jsonPathIndex = 0;
510 JsonArray jsonPaths = jsonIntent.get(PATHS).asArray();
511
512 if (pathEntries != null) {
513
rohitsharanfd747602017-01-23 21:57:28 +0530514 if (pathEntries.size() == 0) {
515 reason = "pathEntries array empty";
516 return false;
517 }
518
Author Namee252a002016-09-26 22:42:24 +0530519 if (pathEntries.size() != jsonPaths.size()) {
520 reason = "path entries array size of " +
521 Integer.toString(pathEntries.size());
522 return false;
523 }
524
525 for (List<FlowEntry> flowEntries : pathEntries) {
526 JsonArray jsonFlowEntries = jsonPaths.get(
527 jsonPathIndex++).asArray();
528
529 if (flowEntries.size() != jsonFlowEntries.size()) {
530 reason = "flow entries array size of " +
531 Integer.toString(pathEntries.size());
532
533 return false;
534 }
535
536 int jsonFlowEntryIndex = 0;
537 for (FlowEntry flow : flowEntries) {
538
539 JsonObject jsonFlow = jsonFlowEntries.get(
540 jsonFlowEntryIndex++).asObject();
541
542 String jsonId = jsonFlow.get(ID).asString();
543 String flowId = Long.toString(flow.id().value());
544 if (!jsonId.equals(flowId)) {
545 reason = ID + SPACE + flow.id();
546 return false;
547 }
548
549 // check application id
550 String jsonAppId = jsonFlow.get(APPID).asString();
551 if (!jsonAppId.equals(expectedAppId)) {
552 reason = APPID + SPACE + Short.toString(flow.appId());
553 return false;
554 }
555
556 // check device id
557 String jsonDeviceId =
558 jsonFlow.get(DEVICE_ID).asString();
559
560 if (!jsonDeviceId.equals(flow.deviceId().toString())) {
561 reason = DEVICE_ID + SPACE + flow.deviceId();
562 return false;
563 }
564
565 if (!checkFlowTreatment(flow, jsonFlow)) {
566 return false;
567 }
568
569 if (!checkFlowSelector(flow, jsonFlow)) {
570 return false;
571 }
572
573 }
574
575 }
rohitsharanfd747602017-01-23 21:57:28 +0530576 } else {
Author Namee252a002016-09-26 22:42:24 +0530577 reason = "pathEntries array empty";
578 return false;
579 }
580
581 return true;
582 }
583
584 // check treatment and instructions array.
585 private boolean checkFlowTreatment(FlowEntry flow, JsonObject jsonFlow) {
586
587 if (flow.treatment() != null) {
588 JsonObject jsonTreatment =
589 jsonFlow.get(TREATMENT).asObject();
590 JsonArray jsonInstructions =
591 jsonTreatment.get(INSTRUCTIONS).asArray();
592
593 if (flow.treatment().immediate().size() !=
594 jsonInstructions.size()) {
595 reason = "instructions array size of " +
596 flow.treatment().immediate().size();
597
598 return false;
599 }
600 for (Instruction instruction :
601 flow.treatment().immediate()) {
602 boolean instructionFound = false;
603 for (int instructionIndex = 0;
604 instructionIndex < jsonInstructions.size();
605 instructionIndex++) {
606 String jsonType =
607 jsonInstructions.get(instructionIndex)
608 .asObject().get(TYPE).asString();
609
610 String instructionType =
611 instruction.type().name();
612
613 if (jsonType.equals(instructionType)) {
614 instructionFound = true;
615 }
616 }
617 if (!instructionFound) {
618 reason = INSTRUCTIONS + SPACE + instruction;
619 return false;
620 }
621 }
622 }
623 return true;
624 }
625
626 // check selector and criteria array.
627 private boolean checkFlowSelector(FlowEntry flow, JsonObject jsonFlow) {
628
629 if (flow.selector() != null) {
630 JsonObject jsonTreatment =
631 jsonFlow.get(SELECTOR).asObject();
632
633 JsonArray jsonCriteria =
634 jsonTreatment.get(CRITERIA).asArray();
635
636 if (flow.selector().criteria().size() != jsonCriteria.size()) {
637 reason = CRITERIA + " array size of " +
638 Integer.toString(flow.selector().criteria().size());
639 return false;
640 }
641 for (Criterion criterion : flow.selector().criteria()) {
642 boolean criterionFound = false;
643
644 for (int criterionIndex = 0;
645 criterionIndex < jsonCriteria.size();
646 criterionIndex++) {
647 String jsonType =
648 jsonCriteria.get(criterionIndex)
649 .asObject().get(TYPE).asString();
650 String criterionType = criterion.type().name();
651 if (jsonType.equals(criterionType)) {
652 criterionFound = true;
653 }
654 }
655 if (!criterionFound) {
656 reason = "criterion " + criterion;
657 return false;
658 }
659 }
660 }
661 return true;
662 }
663
664 @Override
665 public void describeTo(Description description) {
666 description.appendText(reason);
667 }
668 }
669
670 /**
Ray Milkey2b217142014-12-15 09:24:24 -0800671 * Hamcrest matcher to check that an intent is represented properly in a JSON
672 * array of intents.
673 */
674 public static class IntentJsonArrayMatcher extends TypeSafeMatcher<JsonArray> {
675 private final Intent intent;
676 private String reason = "";
677
678 public IntentJsonArrayMatcher(Intent intentValue) {
679 intent = intentValue;
680 }
681
682 @Override
683 public boolean matchesSafely(JsonArray json) {
684 boolean intentFound = false;
Ray Milkeyf7cb4012015-07-20 13:01:07 -0700685 final int expectedAttributes = 5;
Ray Milkey2b217142014-12-15 09:24:24 -0800686 for (int jsonIntentIndex = 0; jsonIntentIndex < json.size();
687 jsonIntentIndex++) {
688
689 final JsonObject jsonIntent = json.get(jsonIntentIndex).asObject();
690
691 if (jsonIntent.names().size() != expectedAttributes) {
692 reason = "Found an intent with the wrong number of attributes";
693 return false;
694 }
695
696 final String jsonIntentId = jsonIntent.get("id").asString();
697 if (jsonIntentId.equals(intent.id().toString())) {
698 intentFound = true;
699
700 // We found the correct intent, check attribute values
701 assertThat(jsonIntent, matchesIntent(intent));
702 }
703 }
704 if (!intentFound) {
705 reason = "Intent with id " + intent.id().toString() + " not found";
706 return false;
707 } else {
708 return true;
709 }
710 }
711
712 @Override
713 public void describeTo(Description description) {
714 description.appendText(reason);
715 }
716 }
717
718 /**
719 * Factory to allocate an intent array matcher.
720 *
721 * @param intent intent object we are looking for
722 * @return matcher
723 */
724 private static IntentJsonArrayMatcher hasIntent(Intent intent) {
725 return new IntentJsonArrayMatcher(intent);
726 }
Ray Milkey4f5de002014-12-17 19:26:11 -0800727
Ray Milkeyed0b1662015-02-05 09:34:29 -0800728 /**
729 * Initializes test mocks and environment.
730 */
Ray Milkey2b217142014-12-15 09:24:24 -0800731 @Before
Ray Milkeyed0b1662015-02-05 09:34:29 -0800732 public void setUpTest() {
Ray Milkey2b217142014-12-15 09:24:24 -0800733 expect(mockIntentService.getIntents()).andReturn(intents).anyTimes();
Ray Milkey1534f8d2015-05-13 15:42:50 -0700734 expect(mockIntentService.getIntentState(anyObject()))
735 .andReturn(IntentState.INSTALLED)
736 .anyTimes();
Ray Milkey2b217142014-12-15 09:24:24 -0800737 // Register the services needed for the test
738 final CodecManager codecService = new CodecManager();
739 codecService.activate();
740 ServiceDirectory testDirectory =
741 new TestServiceDirectory()
742 .add(IntentService.class, mockIntentService)
Author Namee252a002016-09-26 22:42:24 +0530743 .add(FlowRuleService.class, mockFlowService)
Ayaka Koshibec06c89b2015-02-10 19:25:41 -0800744 .add(CodecService.class, codecService)
745 .add(CoreService.class, mockCoreService);
Ray Milkey2b217142014-12-15 09:24:24 -0800746
747 BaseResource.setServiceDirectory(testDirectory);
748
Thomas Vachuska2048c1f2017-05-10 19:32:22 -0700749 MockIdGenerator.cleanBind();
Ray Milkey2b217142014-12-15 09:24:24 -0800750 }
751
Ray Milkeyed0b1662015-02-05 09:34:29 -0800752 /**
753 * Tears down and verifies test mocks and environment.
754 */
Ray Milkey2b217142014-12-15 09:24:24 -0800755 @After
Ray Milkeyed0b1662015-02-05 09:34:29 -0800756 public void tearDownTest() {
Thomas Vachuska2048c1f2017-05-10 19:32:22 -0700757 MockIdGenerator.unbind();
Ray Milkey2b217142014-12-15 09:24:24 -0800758 verify(mockIntentService);
Ray Milkey2b217142014-12-15 09:24:24 -0800759 }
760
761 /**
762 * Tests the result of the rest api GET when there are no intents.
763 */
764 @Test
765 public void testIntentsEmptyArray() {
766 replay(mockIntentService);
Jian Li9d616492016-03-09 10:52:49 -0800767 final WebTarget wt = target();
768 final String response = wt.path("intents").request().get(String.class);
Ray Milkey2b217142014-12-15 09:24:24 -0800769 assertThat(response, is("{\"intents\":[]}"));
770 }
771
772 /**
773 * Tests the result of the rest api GET when intents are defined.
774 */
775 @Test
776 public void testIntentsArray() {
777 replay(mockIntentService);
778
Ray Milkey43a28222015-02-23 13:57:58 -0800779 final Intent intent1 = new MockIntent(1L, Collections.emptyList());
Ray Milkey2b217142014-12-15 09:24:24 -0800780 final HashSet<NetworkResource> resources = new HashSet<>();
781 resources.add(new MockResource(1));
782 resources.add(new MockResource(2));
783 resources.add(new MockResource(3));
Ray Milkey43a28222015-02-23 13:57:58 -0800784 final Intent intent2 = new MockIntent(2L, resources);
Ray Milkey2b217142014-12-15 09:24:24 -0800785
786 intents.add(intent1);
787 intents.add(intent2);
Jian Li9d616492016-03-09 10:52:49 -0800788 final WebTarget wt = target();
789 final String response = wt.path("intents").request().get(String.class);
Ray Milkey2b217142014-12-15 09:24:24 -0800790 assertThat(response, containsString("{\"intents\":["));
791
Jian Li80cfe452016-01-14 16:04:58 -0800792 final JsonObject result = Json.parse(response).asObject();
Ray Milkey2b217142014-12-15 09:24:24 -0800793 assertThat(result, notNullValue());
794
795 assertThat(result.names(), hasSize(1));
796 assertThat(result.names().get(0), is("intents"));
797
798 final JsonArray jsonIntents = result.get("intents").asArray();
799 assertThat(jsonIntents, notNullValue());
800
801 assertThat(jsonIntents, hasIntent(intent1));
802 assertThat(jsonIntents, hasIntent(intent2));
803 }
804
805 /**
806 * Tests the result of a rest api GET for a single intent.
807 */
808 @Test
809 public void testIntentsSingle() {
810 final HashSet<NetworkResource> resources = new HashSet<>();
811 resources.add(new MockResource(1));
812 resources.add(new MockResource(2));
813 resources.add(new MockResource(3));
Ray Milkey43a28222015-02-23 13:57:58 -0800814 final Intent intent = new MockIntent(3L, resources);
Ray Milkey2b217142014-12-15 09:24:24 -0800815
816 intents.add(intent);
817
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800818 expect(mockIntentService.getIntent(Key.of(0, APP_ID)))
Ray Milkey2b217142014-12-15 09:24:24 -0800819 .andReturn(intent)
820 .anyTimes();
Ayaka Koshibec06c89b2015-02-10 19:25:41 -0800821 expect(mockIntentService.getIntent(Key.of("0", APP_ID)))
822 .andReturn(intent)
823 .anyTimes();
Ray Milkey05b169d2015-08-13 14:33:33 -0700824 expect(mockIntentService.getIntent(Key.of(0, APP_ID)))
825 .andReturn(intent)
826 .anyTimes();
827 expect(mockIntentService.getIntent(Key.of("0x0", APP_ID)))
828 .andReturn(null)
829 .anyTimes();
Ray Milkey2b217142014-12-15 09:24:24 -0800830 replay(mockIntentService);
Ray Milkeyf7cb4012015-07-20 13:01:07 -0700831 expect(mockCoreService.getAppId(APP_ID.name()))
Ayaka Koshibec06c89b2015-02-10 19:25:41 -0800832 .andReturn(APP_ID).anyTimes();
833 replay(mockCoreService);
Jian Li9d616492016-03-09 10:52:49 -0800834 final WebTarget wt = target();
Ray Milkey05b169d2015-08-13 14:33:33 -0700835
836 // Test get using key string
Jian Li9d616492016-03-09 10:52:49 -0800837 final String response = wt.path("intents/" + APP_ID.name()
838 + "/0").request().get(String.class);
Jian Li80cfe452016-01-14 16:04:58 -0800839 final JsonObject result = Json.parse(response).asObject();
Ray Milkey2b217142014-12-15 09:24:24 -0800840 assertThat(result, matchesIntent(intent));
Ray Milkey05b169d2015-08-13 14:33:33 -0700841
842 // Test get using numeric value
Jian Li9d616492016-03-09 10:52:49 -0800843 final String responseNumeric = wt.path("intents/" + APP_ID.name()
844 + "/0x0").request().get(String.class);
Jian Li80cfe452016-01-14 16:04:58 -0800845 final JsonObject resultNumeric = Json.parse(responseNumeric).asObject();
Ray Milkey05b169d2015-08-13 14:33:33 -0700846 assertThat(resultNumeric, matchesIntent(intent));
Ray Milkey2b217142014-12-15 09:24:24 -0800847 }
848
849 /**
Author Namee252a002016-09-26 22:42:24 +0530850 * Tests the result of a rest api GET for related flows for single intent.
851 */
852 @Test
853 public void testRelatedFlowsForIntents() {
854 List<FlowEntry> flowEntries = new ArrayList<>();
855 flowEntries.add(flow1);
856 flowEntries.add(flow2);
857 List<List<FlowEntry>> paths = new ArrayList<>();
858 paths.add(flowEntries);
859 List<FlowRule> flowRules = new ArrayList<>();
860 flowRules.add(flowRule1);
861 flowRules.add(flowRule2);
862 FlowRuleIntent flowRuleIntent = new FlowRuleIntent(
863 APP_ID,
864 flowRules,
865 new HashSet<NetworkResource>());
866 Intent intent = new MockIntent(3L);
867 installableIntents.add(flowRuleIntent);
868 intents.add(intent);
869
870 expect(mockIntentService.getIntent(Key.of(0, APP_ID)))
871 .andReturn(intent)
872 .anyTimes();
873 expect(mockIntentService.getIntent(Key.of("0", APP_ID)))
874 .andReturn(intent)
875 .anyTimes();
876 expect(mockIntentService.getIntent(Key.of(0, APP_ID)))
877 .andReturn(intent)
878 .anyTimes();
879 expect(mockIntentService.getIntent(Key.of("0x0", APP_ID)))
880 .andReturn(null)
881 .anyTimes();
882 expect(mockIntentService.getInstallableIntents(intent.key()))
883 .andReturn(installableIntents)
884 .anyTimes();
885 replay(mockIntentService);
886
887 expect(mockFlowService.getFlowEntries(deviceId1))
888 .andReturn(flowEntries).anyTimes();
889 replay(mockFlowService);
890
891 expect(mockCoreService.getAppId(APP_ID.name()))
892 .andReturn(APP_ID).anyTimes();
893 expect(mockCoreService.getAppId(APP_ID.id()))
894 .andReturn(APP_ID).anyTimes();
895 replay(mockCoreService);
896
897 final WebTarget wt = target();
898
899 // Test get using key string
900 final String response = wt.path("intents/relatedflows/" + APP_ID.name()
901 + "/0").request().get(String.class);
902 final JsonObject result = Json.parse(response).asObject();
903 assertThat(result, matchesRelatedFlowEntries(paths, APP_ID.name()));
904
905 // Test get using numeric value
906 final String responseNumeric = wt.path("intents/relatedflows/" + APP_ID.name()
907 + "/0x0").request().get(String.class);
908 final JsonObject resultNumeric = Json.parse(responseNumeric).asObject();
909 assertThat(resultNumeric, matchesRelatedFlowEntries(paths, APP_ID.name()));
910 }
911
912 /**
Rafal Szaleckif97e0ed2017-06-01 13:07:18 +0200913 * Tests the result of a rest api GET for intent installables.
914 */
915 @Test
916 public void testIntentInstallables() {
917
918 Link link1 = DefaultLink.builder()
919 .type(Link.Type.DIRECT)
920 .providerId(ProviderId.NONE)
921 .src(connectPoint1)
922 .dst(connectPoint2)
923 .build();
924
925 Link link2 = DefaultLink.builder()
926 .type(Link.Type.DIRECT)
927 .providerId(ProviderId.NONE)
928 .src(connectPoint3)
929 .dst(connectPoint4)
930 .build();
931
932 Set<NetworkResource> resources = new HashSet<>();
933 resources.add(link1);
934 resources.add(link2);
935
936 FlowRuleIntent flowRuleIntent = new FlowRuleIntent(
937 APP_ID,
938 new ArrayList<>(),
939 resources);
940
941 Intent intent = new MockIntent(MockIntent.nextId());
942 Long intentId = intent.id().id();
943 installableIntents.add(flowRuleIntent);
944 intents.add(intent);
945
946 expect(mockIntentService.getIntent(Key.of(intentId, APP_ID)))
947 .andReturn(intent)
948 .anyTimes();
949 expect(mockIntentService.getIntent(Key.of(intentId.toString(), APP_ID)))
950 .andReturn(intent)
951 .anyTimes();
952 expect(mockIntentService.getIntent(Key.of(intentId, APP_ID)))
953 .andReturn(intent)
954 .anyTimes();
955 expect(mockIntentService.getIntent(Key.of(Long.toHexString(intentId), APP_ID)))
956 .andReturn(null)
957 .anyTimes();
958 expect(mockIntentService.getInstallableIntents(intent.key()))
959 .andReturn(installableIntents)
960 .anyTimes();
961 replay(mockIntentService);
962
963 replay(mockFlowService);
964
965 expect(mockCoreService.getAppId(APP_ID.name()))
966 .andReturn(APP_ID).anyTimes();
967 expect(mockCoreService.getAppId(APP_ID.id()))
968 .andReturn(APP_ID).anyTimes();
969 replay(mockCoreService);
970
971 final WebTarget wt = target();
972
973 // Test get using key string
974 final String response = wt.path("intents/installables/" + APP_ID.name()
975 + "/" + intentId).request().get(String.class);
976 final JsonObject result = Json.parse(response).asObject();
977 assertThat(result.get(INSTALLABLES).asArray(), hasIntent(flowRuleIntent));
978
979 // Test get using numeric value
980 final String responseNumeric = wt.path("intents/installables/" + APP_ID.name()
981 + "/" + Long.toHexString(intentId)).request().get(String.class);
982 final JsonObject resultNumeric = Json.parse(responseNumeric).asObject();
983 assertThat(resultNumeric.get(INSTALLABLES).asArray(), hasIntent(flowRuleIntent));
984 }
985
986 /**
Ray Milkey2b217142014-12-15 09:24:24 -0800987 * Tests that a fetch of a non-existent intent object throws an exception.
988 */
989 @Test
990 public void testBadGet() {
991
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800992 expect(mockIntentService.getIntent(Key.of(0, APP_ID)))
Ray Milkey2b217142014-12-15 09:24:24 -0800993 .andReturn(null)
994 .anyTimes();
995 replay(mockIntentService);
996
Jian Li9d616492016-03-09 10:52:49 -0800997 WebTarget wt = target();
Ray Milkey2b217142014-12-15 09:24:24 -0800998 try {
Jian Li9d616492016-03-09 10:52:49 -0800999 wt.path("intents/0").request().get(String.class);
Ray Milkey2b217142014-12-15 09:24:24 -08001000 fail("Fetch of non-existent intent did not throw an exception");
Jian Li9d616492016-03-09 10:52:49 -08001001 } catch (NotFoundException ex) {
Ray Milkey2b217142014-12-15 09:24:24 -08001002 assertThat(ex.getMessage(),
Jian Li9d616492016-03-09 10:52:49 -08001003 containsString("HTTP 404 Not Found"));
Ray Milkey2b217142014-12-15 09:24:24 -08001004 }
1005 }
Ray Milkeyb82c42b2015-06-30 09:42:20 -07001006
1007 /**
1008 * Tests creating an intent with POST.
1009 */
1010 @Test
1011 public void testPost() {
Ray Milkeyf7cb4012015-07-20 13:01:07 -07001012 ApplicationId testId = new DefaultApplicationId(2, "myApp");
1013 expect(mockCoreService.getAppId("myApp"))
1014 .andReturn(testId);
Ray Milkeyb82c42b2015-06-30 09:42:20 -07001015 replay(mockCoreService);
1016
1017 mockIntentService.submit(anyObject());
1018 expectLastCall();
1019 replay(mockIntentService);
1020
1021 InputStream jsonStream = IntentsResourceTest.class
1022 .getResourceAsStream("post-intent.json");
Jian Li9d616492016-03-09 10:52:49 -08001023 WebTarget wt = target();
Ray Milkeyb82c42b2015-06-30 09:42:20 -07001024
Jian Li9d616492016-03-09 10:52:49 -08001025 Response response = wt.path("intents")
1026 .request(MediaType.APPLICATION_JSON_TYPE)
1027 .post(Entity.json(jsonStream));
Ray Milkeyb82c42b2015-06-30 09:42:20 -07001028 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
1029 String location = response.getLocation().getPath();
Ray Milkey8d076402015-08-31 15:43:18 -07001030 assertThat(location, Matchers.startsWith("/intents/myApp/"));
Ray Milkeyb82c42b2015-06-30 09:42:20 -07001031 }
Ray Milkey7b158512015-07-21 16:32:43 -07001032
1033 /**
Ray Milkey05b169d2015-08-13 14:33:33 -07001034 * Tests creating an intent with POST and illegal JSON.
1035 */
1036 @Test
1037 public void testBadPost() {
1038 replay(mockCoreService);
1039 replay(mockIntentService);
1040
1041 String json = "this is invalid!";
Jian Li9d616492016-03-09 10:52:49 -08001042 WebTarget wt = target();
Ray Milkey05b169d2015-08-13 14:33:33 -07001043
Jian Li9d616492016-03-09 10:52:49 -08001044 Response response = wt.path("intents")
1045 .request(MediaType.APPLICATION_JSON_TYPE)
1046 .post(Entity.json(json));
Ray Milkey05b169d2015-08-13 14:33:33 -07001047 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_BAD_REQUEST));
1048 }
1049
1050 /**
Ray Milkey7b158512015-07-21 16:32:43 -07001051 * Tests removing an intent with DELETE.
1052 */
1053 @Test
1054 public void testRemove() {
1055 final HashSet<NetworkResource> resources = new HashSet<>();
1056 resources.add(new MockResource(1));
1057 resources.add(new MockResource(2));
1058 resources.add(new MockResource(3));
1059 final Intent intent = new MockIntent(3L, resources);
1060 final ApplicationId appId = new DefaultApplicationId(2, "app");
1061 IntentService fakeManager = new FakeIntentManager();
1062
1063 expect(mockCoreService.getAppId("app"))
1064 .andReturn(appId).once();
1065 replay(mockCoreService);
1066
1067 mockIntentService.withdraw(anyObject());
1068 expectLastCall().andDelegateTo(fakeManager).once();
1069 expect(mockIntentService.getIntent(Key.of(2, appId)))
1070 .andReturn(intent)
1071 .once();
1072 expect(mockIntentService.getIntent(Key.of("0x2", appId)))
1073 .andReturn(null)
1074 .once();
1075
1076 mockIntentService.addListener(anyObject());
1077 expectLastCall().andDelegateTo(fakeManager).once();
1078 mockIntentService.removeListener(anyObject());
1079 expectLastCall().andDelegateTo(fakeManager).once();
1080
1081 replay(mockIntentService);
1082
Jian Li9d616492016-03-09 10:52:49 -08001083 WebTarget wt = target();
Ray Milkey7b158512015-07-21 16:32:43 -07001084
Jian Li9d616492016-03-09 10:52:49 -08001085 Response response = wt.path("intents/app/0x2")
Ray Milkey7c251822016-04-06 17:38:25 -07001086 .request(MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN)
Jian Li9d616492016-03-09 10:52:49 -08001087 .delete();
Ray Milkey7b158512015-07-21 16:32:43 -07001088 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_NO_CONTENT));
1089 }
Ray Milkey05b169d2015-08-13 14:33:33 -07001090
1091 /**
1092 * Tests removal of a non existent intent with DELETE.
1093 */
1094 @Test
1095 public void testBadRemove() {
1096 final ApplicationId appId = new DefaultApplicationId(2, "app");
1097
1098 expect(mockCoreService.getAppId("app"))
1099 .andReturn(appId).once();
1100 replay(mockCoreService);
1101
1102 expect(mockIntentService.getIntent(Key.of(2, appId)))
1103 .andReturn(null)
1104 .once();
1105 expect(mockIntentService.getIntent(Key.of("0x2", appId)))
1106 .andReturn(null)
1107 .once();
1108
1109 replay(mockIntentService);
1110
Jian Li9d616492016-03-09 10:52:49 -08001111 WebTarget wt = target();
Ray Milkey05b169d2015-08-13 14:33:33 -07001112
Jian Li9d616492016-03-09 10:52:49 -08001113 Response response = wt.path("intents/app/0x2")
Ray Milkey7c251822016-04-06 17:38:25 -07001114 .request(MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN)
Jian Li9d616492016-03-09 10:52:49 -08001115 .delete();
Ray Milkey05b169d2015-08-13 14:33:33 -07001116 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_NO_CONTENT));
1117 }
1118
Ray Milkey2b217142014-12-15 09:24:24 -08001119}