blob: e87b94ae7e0e913d57c03bffd35bb920c40a6e0e [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
Ray Milkey7c251822016-04-06 17:38:25 -070018import java.io.InputStream;
19import java.net.HttpURLConnection;
20import java.util.Collections;
21import java.util.HashSet;
22
23import javax.ws.rs.NotFoundException;
24import javax.ws.rs.client.Entity;
25import javax.ws.rs.client.WebTarget;
26import javax.ws.rs.core.MediaType;
27import javax.ws.rs.core.Response;
28
Ray Milkey2b217142014-12-15 09:24:24 -080029import org.hamcrest.Description;
Ray Milkeyb82c42b2015-06-30 09:42:20 -070030import org.hamcrest.Matchers;
Ray Milkey2b217142014-12-15 09:24:24 -080031import org.hamcrest.TypeSafeMatcher;
32import org.junit.After;
33import org.junit.Before;
34import org.junit.Test;
35import org.onlab.osgi.ServiceDirectory;
36import org.onlab.osgi.TestServiceDirectory;
37import org.onlab.rest.BaseResource;
38import org.onosproject.codec.CodecService;
39import org.onosproject.codec.impl.CodecManager;
40import org.onosproject.core.ApplicationId;
Ayaka Koshibec06c89b2015-02-10 19:25:41 -080041import org.onosproject.core.CoreService;
Ray Milkey2b217142014-12-15 09:24:24 -080042import org.onosproject.core.DefaultApplicationId;
43import org.onosproject.core.IdGenerator;
44import org.onosproject.net.NetworkResource;
Ray Milkey7b158512015-07-21 16:32:43 -070045import org.onosproject.net.intent.FakeIntentManager;
Ray Milkey2b217142014-12-15 09:24:24 -080046import org.onosproject.net.intent.Intent;
Ray Milkey2b217142014-12-15 09:24:24 -080047import org.onosproject.net.intent.IntentService;
Ray Milkey1534f8d2015-05-13 15:42:50 -070048import org.onosproject.net.intent.IntentState;
Ray Milkeyf9af43c2015-02-09 16:45:48 -080049import org.onosproject.net.intent.Key;
Ray Milkey2b217142014-12-15 09:24:24 -080050
Ray Milkey7c251822016-04-06 17:38:25 -070051import com.eclipsesource.json.Json;
52import com.eclipsesource.json.JsonArray;
53import com.eclipsesource.json.JsonObject;
54import com.eclipsesource.json.JsonValue;
Ray Milkey2b217142014-12-15 09:24:24 -080055
Jian Li9d616492016-03-09 10:52:49 -080056import static org.easymock.EasyMock.anyObject;
57import static org.easymock.EasyMock.createMock;
58import static org.easymock.EasyMock.expect;
59import static org.easymock.EasyMock.expectLastCall;
60import static org.easymock.EasyMock.replay;
61import static org.easymock.EasyMock.verify;
62import static org.hamcrest.Matchers.containsString;
63import static org.hamcrest.Matchers.hasSize;
64import static org.hamcrest.Matchers.is;
65import static org.hamcrest.Matchers.notNullValue;
Ray Milkey2b217142014-12-15 09:24:24 -080066import static org.junit.Assert.assertThat;
67import static org.junit.Assert.fail;
Ray Milkey43a28222015-02-23 13:57:58 -080068import static org.onosproject.net.intent.IntentTestsMocks.MockIntent;
Ray Milkey7c251822016-04-06 17:38:25 -070069import static org.onosproject.net.intent.MockIdGenerator.bindNewGenerator;
Ray Milkey2b217142014-12-15 09:24:24 -080070
71/**
72 * Unit tests for Intents REST APIs.
73 */
Ray Milkey9c3d3362015-01-28 10:39:56 -080074public class IntentsResourceTest extends ResourceTest {
Ray Milkey2b217142014-12-15 09:24:24 -080075 final IntentService mockIntentService = createMock(IntentService.class);
Ayaka Koshibec06c89b2015-02-10 19:25:41 -080076 final CoreService mockCoreService = createMock(CoreService.class);
Ray Milkey2b217142014-12-15 09:24:24 -080077 final HashSet<Intent> intents = new HashSet<>();
Thomas Vachuska02aeb032015-01-06 22:36:30 -080078 private static final ApplicationId APP_ID = new DefaultApplicationId(1, "test");
Ray Milkey2b217142014-12-15 09:24:24 -080079 private IdGenerator mockGenerator;
80
Ray Milkey2b217142014-12-15 09:24:24 -080081 private class MockResource implements NetworkResource {
82 int id;
83
84 MockResource(int id) {
85 this.id = id;
86 }
87
Ayaka Koshibec06c89b2015-02-10 19:25:41 -080088 @Override
Ray Milkey2b217142014-12-15 09:24:24 -080089 public String toString() {
90 return "Resource " + Integer.toString(id);
91 }
92 }
93
Ray Milkey2b217142014-12-15 09:24:24 -080094 /**
95 * Hamcrest matcher to check that an intent representation in JSON matches
96 * the actual intent.
97 */
98 public static class IntentJsonMatcher extends TypeSafeMatcher<JsonObject> {
99 private final Intent intent;
100 private String reason = "";
101
102 public IntentJsonMatcher(Intent intentValue) {
103 intent = intentValue;
104 }
105
106 @Override
107 public boolean matchesSafely(JsonObject jsonIntent) {
108 // check id
109 final String jsonId = jsonIntent.get("id").asString();
110 if (!jsonId.equals(intent.id().toString())) {
111 reason = "id " + intent.id().toString();
112 return false;
113 }
114
115 // check application id
Ray Milkeyf7cb4012015-07-20 13:01:07 -0700116
Ray Milkey2b217142014-12-15 09:24:24 -0800117 final String jsonAppId = jsonIntent.get("appId").asString();
Ray Milkeyf7cb4012015-07-20 13:01:07 -0700118 final String appId = intent.appId().name();
119 if (!jsonAppId.equals(appId)) {
120 reason = "appId was " + jsonAppId;
Ray Milkey2b217142014-12-15 09:24:24 -0800121 return false;
122 }
123
124 // check intent type
125 final String jsonType = jsonIntent.get("type").asString();
126 if (!jsonType.equals("MockIntent")) {
127 reason = "type MockIntent";
128 return false;
129 }
130
Ray Milkey1534f8d2015-05-13 15:42:50 -0700131 // check state field
132 final String jsonState = jsonIntent.get("state").asString();
133 if (!jsonState.equals("INSTALLED")) {
134 reason = "state INSTALLED";
135 return false;
136 }
137
Ray Milkey2b217142014-12-15 09:24:24 -0800138 // check resources array
139 final JsonArray jsonResources = jsonIntent.get("resources").asArray();
140 if (intent.resources() != null) {
141 if (intent.resources().size() != jsonResources.size()) {
142 reason = "resources array size of " + Integer.toString(intent.resources().size());
143 return false;
144 }
145 for (final NetworkResource resource : intent.resources()) {
146 boolean resourceFound = false;
147 final String resourceString = resource.toString();
148 for (int resourceIndex = 0; resourceIndex < jsonResources.size(); resourceIndex++) {
149 final JsonValue value = jsonResources.get(resourceIndex);
150 if (value.asString().equals(resourceString)) {
151 resourceFound = true;
152 }
153 }
154 if (!resourceFound) {
155 reason = "resource " + resourceString;
156 return false;
157 }
158 }
159 } else if (jsonResources.size() != 0) {
160 reason = "resources array empty";
161 return false;
162 }
163 return true;
164 }
165
166 @Override
167 public void describeTo(Description description) {
168 description.appendText(reason);
169 }
170 }
171
172 /**
173 * Factory to allocate an intent matcher.
174 *
175 * @param intent intent object we are looking for
176 * @return matcher
177 */
178 private static IntentJsonMatcher matchesIntent(Intent intent) {
179 return new IntentJsonMatcher(intent);
180 }
181
182 /**
183 * Hamcrest matcher to check that an intent is represented properly in a JSON
184 * array of intents.
185 */
186 public static class IntentJsonArrayMatcher extends TypeSafeMatcher<JsonArray> {
187 private final Intent intent;
188 private String reason = "";
189
190 public IntentJsonArrayMatcher(Intent intentValue) {
191 intent = intentValue;
192 }
193
194 @Override
195 public boolean matchesSafely(JsonArray json) {
196 boolean intentFound = false;
Ray Milkeyf7cb4012015-07-20 13:01:07 -0700197 final int expectedAttributes = 5;
Ray Milkey2b217142014-12-15 09:24:24 -0800198 for (int jsonIntentIndex = 0; jsonIntentIndex < json.size();
199 jsonIntentIndex++) {
200
201 final JsonObject jsonIntent = json.get(jsonIntentIndex).asObject();
202
203 if (jsonIntent.names().size() != expectedAttributes) {
204 reason = "Found an intent with the wrong number of attributes";
205 return false;
206 }
207
208 final String jsonIntentId = jsonIntent.get("id").asString();
209 if (jsonIntentId.equals(intent.id().toString())) {
210 intentFound = true;
211
212 // We found the correct intent, check attribute values
213 assertThat(jsonIntent, matchesIntent(intent));
214 }
215 }
216 if (!intentFound) {
217 reason = "Intent with id " + intent.id().toString() + " not found";
218 return false;
219 } else {
220 return true;
221 }
222 }
223
224 @Override
225 public void describeTo(Description description) {
226 description.appendText(reason);
227 }
228 }
229
230 /**
231 * Factory to allocate an intent array matcher.
232 *
233 * @param intent intent object we are looking for
234 * @return matcher
235 */
236 private static IntentJsonArrayMatcher hasIntent(Intent intent) {
237 return new IntentJsonArrayMatcher(intent);
238 }
Ray Milkey4f5de002014-12-17 19:26:11 -0800239
Ray Milkeyed0b1662015-02-05 09:34:29 -0800240 /**
241 * Initializes test mocks and environment.
242 */
Ray Milkey2b217142014-12-15 09:24:24 -0800243 @Before
Ray Milkeyed0b1662015-02-05 09:34:29 -0800244 public void setUpTest() {
Ray Milkey2b217142014-12-15 09:24:24 -0800245 expect(mockIntentService.getIntents()).andReturn(intents).anyTimes();
Ray Milkey1534f8d2015-05-13 15:42:50 -0700246 expect(mockIntentService.getIntentState(anyObject()))
247 .andReturn(IntentState.INSTALLED)
248 .anyTimes();
Ray Milkey2b217142014-12-15 09:24:24 -0800249 // Register the services needed for the test
250 final CodecManager codecService = new CodecManager();
251 codecService.activate();
252 ServiceDirectory testDirectory =
253 new TestServiceDirectory()
254 .add(IntentService.class, mockIntentService)
Ayaka Koshibec06c89b2015-02-10 19:25:41 -0800255 .add(CodecService.class, codecService)
256 .add(CoreService.class, mockCoreService);
Ray Milkey2b217142014-12-15 09:24:24 -0800257
258 BaseResource.setServiceDirectory(testDirectory);
259
Ray Milkey7c251822016-04-06 17:38:25 -0700260 bindNewGenerator();
Ray Milkey2b217142014-12-15 09:24:24 -0800261 }
262
Ray Milkeyed0b1662015-02-05 09:34:29 -0800263 /**
264 * Tears down and verifies test mocks and environment.
265 */
Ray Milkey2b217142014-12-15 09:24:24 -0800266 @After
Ray Milkeyed0b1662015-02-05 09:34:29 -0800267 public void tearDownTest() {
Ray Milkey2b217142014-12-15 09:24:24 -0800268 verify(mockIntentService);
Ray Milkey2b217142014-12-15 09:24:24 -0800269 }
270
271 /**
272 * Tests the result of the rest api GET when there are no intents.
273 */
274 @Test
275 public void testIntentsEmptyArray() {
276 replay(mockIntentService);
Jian Li9d616492016-03-09 10:52:49 -0800277 final WebTarget wt = target();
278 final String response = wt.path("intents").request().get(String.class);
Ray Milkey2b217142014-12-15 09:24:24 -0800279 assertThat(response, is("{\"intents\":[]}"));
280 }
281
282 /**
283 * Tests the result of the rest api GET when intents are defined.
284 */
285 @Test
286 public void testIntentsArray() {
287 replay(mockIntentService);
288
Ray Milkey43a28222015-02-23 13:57:58 -0800289 final Intent intent1 = new MockIntent(1L, Collections.emptyList());
Ray Milkey2b217142014-12-15 09:24:24 -0800290 final HashSet<NetworkResource> resources = new HashSet<>();
291 resources.add(new MockResource(1));
292 resources.add(new MockResource(2));
293 resources.add(new MockResource(3));
Ray Milkey43a28222015-02-23 13:57:58 -0800294 final Intent intent2 = new MockIntent(2L, resources);
Ray Milkey2b217142014-12-15 09:24:24 -0800295
296 intents.add(intent1);
297 intents.add(intent2);
Jian Li9d616492016-03-09 10:52:49 -0800298 final WebTarget wt = target();
299 final String response = wt.path("intents").request().get(String.class);
Ray Milkey2b217142014-12-15 09:24:24 -0800300 assertThat(response, containsString("{\"intents\":["));
301
Jian Li80cfe452016-01-14 16:04:58 -0800302 final JsonObject result = Json.parse(response).asObject();
Ray Milkey2b217142014-12-15 09:24:24 -0800303 assertThat(result, notNullValue());
304
305 assertThat(result.names(), hasSize(1));
306 assertThat(result.names().get(0), is("intents"));
307
308 final JsonArray jsonIntents = result.get("intents").asArray();
309 assertThat(jsonIntents, notNullValue());
310
311 assertThat(jsonIntents, hasIntent(intent1));
312 assertThat(jsonIntents, hasIntent(intent2));
313 }
314
315 /**
316 * Tests the result of a rest api GET for a single intent.
317 */
318 @Test
319 public void testIntentsSingle() {
320 final HashSet<NetworkResource> resources = new HashSet<>();
321 resources.add(new MockResource(1));
322 resources.add(new MockResource(2));
323 resources.add(new MockResource(3));
Ray Milkey43a28222015-02-23 13:57:58 -0800324 final Intent intent = new MockIntent(3L, resources);
Ray Milkey2b217142014-12-15 09:24:24 -0800325
326 intents.add(intent);
327
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800328 expect(mockIntentService.getIntent(Key.of(0, APP_ID)))
Ray Milkey2b217142014-12-15 09:24:24 -0800329 .andReturn(intent)
330 .anyTimes();
Ayaka Koshibec06c89b2015-02-10 19:25:41 -0800331 expect(mockIntentService.getIntent(Key.of("0", APP_ID)))
332 .andReturn(intent)
333 .anyTimes();
Ray Milkey05b169d2015-08-13 14:33:33 -0700334 expect(mockIntentService.getIntent(Key.of(0, APP_ID)))
335 .andReturn(intent)
336 .anyTimes();
337 expect(mockIntentService.getIntent(Key.of("0x0", APP_ID)))
338 .andReturn(null)
339 .anyTimes();
Ray Milkey2b217142014-12-15 09:24:24 -0800340 replay(mockIntentService);
Ray Milkeyf7cb4012015-07-20 13:01:07 -0700341 expect(mockCoreService.getAppId(APP_ID.name()))
Ayaka Koshibec06c89b2015-02-10 19:25:41 -0800342 .andReturn(APP_ID).anyTimes();
343 replay(mockCoreService);
Jian Li9d616492016-03-09 10:52:49 -0800344 final WebTarget wt = target();
Ray Milkey05b169d2015-08-13 14:33:33 -0700345
346 // Test get using key string
Jian Li9d616492016-03-09 10:52:49 -0800347 final String response = wt.path("intents/" + APP_ID.name()
348 + "/0").request().get(String.class);
Jian Li80cfe452016-01-14 16:04:58 -0800349 final JsonObject result = Json.parse(response).asObject();
Ray Milkey2b217142014-12-15 09:24:24 -0800350 assertThat(result, matchesIntent(intent));
Ray Milkey05b169d2015-08-13 14:33:33 -0700351
352 // Test get using numeric value
Jian Li9d616492016-03-09 10:52:49 -0800353 final String responseNumeric = wt.path("intents/" + APP_ID.name()
354 + "/0x0").request().get(String.class);
Jian Li80cfe452016-01-14 16:04:58 -0800355 final JsonObject resultNumeric = Json.parse(responseNumeric).asObject();
Ray Milkey05b169d2015-08-13 14:33:33 -0700356 assertThat(resultNumeric, matchesIntent(intent));
Ray Milkey2b217142014-12-15 09:24:24 -0800357 }
358
359 /**
360 * Tests that a fetch of a non-existent intent object throws an exception.
361 */
362 @Test
363 public void testBadGet() {
364
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800365 expect(mockIntentService.getIntent(Key.of(0, APP_ID)))
Ray Milkey2b217142014-12-15 09:24:24 -0800366 .andReturn(null)
367 .anyTimes();
368 replay(mockIntentService);
369
Jian Li9d616492016-03-09 10:52:49 -0800370 WebTarget wt = target();
Ray Milkey2b217142014-12-15 09:24:24 -0800371 try {
Jian Li9d616492016-03-09 10:52:49 -0800372 wt.path("intents/0").request().get(String.class);
Ray Milkey2b217142014-12-15 09:24:24 -0800373 fail("Fetch of non-existent intent did not throw an exception");
Jian Li9d616492016-03-09 10:52:49 -0800374 } catch (NotFoundException ex) {
Ray Milkey2b217142014-12-15 09:24:24 -0800375 assertThat(ex.getMessage(),
Jian Li9d616492016-03-09 10:52:49 -0800376 containsString("HTTP 404 Not Found"));
Ray Milkey2b217142014-12-15 09:24:24 -0800377 }
378 }
Ray Milkeyb82c42b2015-06-30 09:42:20 -0700379
380 /**
381 * Tests creating an intent with POST.
382 */
383 @Test
384 public void testPost() {
Ray Milkeyf7cb4012015-07-20 13:01:07 -0700385 ApplicationId testId = new DefaultApplicationId(2, "myApp");
386 expect(mockCoreService.getAppId("myApp"))
387 .andReturn(testId);
Ray Milkeyb82c42b2015-06-30 09:42:20 -0700388 replay(mockCoreService);
389
390 mockIntentService.submit(anyObject());
391 expectLastCall();
392 replay(mockIntentService);
393
394 InputStream jsonStream = IntentsResourceTest.class
395 .getResourceAsStream("post-intent.json");
Jian Li9d616492016-03-09 10:52:49 -0800396 WebTarget wt = target();
Ray Milkeyb82c42b2015-06-30 09:42:20 -0700397
Jian Li9d616492016-03-09 10:52:49 -0800398 Response response = wt.path("intents")
399 .request(MediaType.APPLICATION_JSON_TYPE)
400 .post(Entity.json(jsonStream));
Ray Milkeyb82c42b2015-06-30 09:42:20 -0700401 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
402 String location = response.getLocation().getPath();
Ray Milkey8d076402015-08-31 15:43:18 -0700403 assertThat(location, Matchers.startsWith("/intents/myApp/"));
Ray Milkeyb82c42b2015-06-30 09:42:20 -0700404 }
Ray Milkey7b158512015-07-21 16:32:43 -0700405
406 /**
Ray Milkey05b169d2015-08-13 14:33:33 -0700407 * Tests creating an intent with POST and illegal JSON.
408 */
409 @Test
410 public void testBadPost() {
411 replay(mockCoreService);
412 replay(mockIntentService);
413
414 String json = "this is invalid!";
Jian Li9d616492016-03-09 10:52:49 -0800415 WebTarget wt = target();
Ray Milkey05b169d2015-08-13 14:33:33 -0700416
Jian Li9d616492016-03-09 10:52:49 -0800417 Response response = wt.path("intents")
418 .request(MediaType.APPLICATION_JSON_TYPE)
419 .post(Entity.json(json));
Ray Milkey05b169d2015-08-13 14:33:33 -0700420 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_BAD_REQUEST));
421 }
422
423 /**
Ray Milkey7b158512015-07-21 16:32:43 -0700424 * Tests removing an intent with DELETE.
425 */
426 @Test
427 public void testRemove() {
428 final HashSet<NetworkResource> resources = new HashSet<>();
429 resources.add(new MockResource(1));
430 resources.add(new MockResource(2));
431 resources.add(new MockResource(3));
432 final Intent intent = new MockIntent(3L, resources);
433 final ApplicationId appId = new DefaultApplicationId(2, "app");
434 IntentService fakeManager = new FakeIntentManager();
435
436 expect(mockCoreService.getAppId("app"))
437 .andReturn(appId).once();
438 replay(mockCoreService);
439
440 mockIntentService.withdraw(anyObject());
441 expectLastCall().andDelegateTo(fakeManager).once();
442 expect(mockIntentService.getIntent(Key.of(2, appId)))
443 .andReturn(intent)
444 .once();
445 expect(mockIntentService.getIntent(Key.of("0x2", appId)))
446 .andReturn(null)
447 .once();
448
449 mockIntentService.addListener(anyObject());
450 expectLastCall().andDelegateTo(fakeManager).once();
451 mockIntentService.removeListener(anyObject());
452 expectLastCall().andDelegateTo(fakeManager).once();
453
454 replay(mockIntentService);
455
Jian Li9d616492016-03-09 10:52:49 -0800456 WebTarget wt = target();
Ray Milkey7b158512015-07-21 16:32:43 -0700457
Jian Li9d616492016-03-09 10:52:49 -0800458 Response response = wt.path("intents/app/0x2")
Ray Milkey7c251822016-04-06 17:38:25 -0700459 .request(MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN)
Jian Li9d616492016-03-09 10:52:49 -0800460 .delete();
Ray Milkey7b158512015-07-21 16:32:43 -0700461 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_NO_CONTENT));
462 }
Ray Milkey05b169d2015-08-13 14:33:33 -0700463
464 /**
465 * Tests removal of a non existent intent with DELETE.
466 */
467 @Test
468 public void testBadRemove() {
469 final ApplicationId appId = new DefaultApplicationId(2, "app");
470
471 expect(mockCoreService.getAppId("app"))
472 .andReturn(appId).once();
473 replay(mockCoreService);
474
475 expect(mockIntentService.getIntent(Key.of(2, appId)))
476 .andReturn(null)
477 .once();
478 expect(mockIntentService.getIntent(Key.of("0x2", appId)))
479 .andReturn(null)
480 .once();
481
482 replay(mockIntentService);
483
Jian Li9d616492016-03-09 10:52:49 -0800484 WebTarget wt = target();
Ray Milkey05b169d2015-08-13 14:33:33 -0700485
Jian Li9d616492016-03-09 10:52:49 -0800486 Response response = wt.path("intents/app/0x2")
Ray Milkey7c251822016-04-06 17:38:25 -0700487 .request(MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN)
Jian Li9d616492016-03-09 10:52:49 -0800488 .delete();
Ray Milkey05b169d2015-08-13 14:33:33 -0700489 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_NO_CONTENT));
490 }
491
Ray Milkey2b217142014-12-15 09:24:24 -0800492}