blob: c3b7069da19a0145665337d4f8f2cea6c7ed2b9e [file] [log] [blame]
Ray Milkey2b217142014-12-15 09:24:24 -08001/*
Ray Milkey34c95902015-04-15 09:47:53 -07002 * Copyright 2014-2015 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 */
16package org.onosproject.rest;
17
Jian Li80cfe452016-01-14 16:04:58 -080018import com.eclipsesource.json.Json;
Thomas Vachuskaa026be72015-12-07 16:00:37 -080019import com.eclipsesource.json.JsonArray;
20import com.eclipsesource.json.JsonObject;
21import com.eclipsesource.json.JsonValue;
Ray Milkey2b217142014-12-15 09:24:24 -080022import org.hamcrest.Description;
Ray Milkeyb82c42b2015-06-30 09:42:20 -070023import org.hamcrest.Matchers;
Ray Milkey2b217142014-12-15 09:24:24 -080024import org.hamcrest.TypeSafeMatcher;
25import org.junit.After;
26import org.junit.Before;
27import org.junit.Test;
28import org.onlab.osgi.ServiceDirectory;
29import org.onlab.osgi.TestServiceDirectory;
30import org.onlab.rest.BaseResource;
31import org.onosproject.codec.CodecService;
32import org.onosproject.codec.impl.CodecManager;
33import org.onosproject.core.ApplicationId;
Ayaka Koshibec06c89b2015-02-10 19:25:41 -080034import org.onosproject.core.CoreService;
Ray Milkey2b217142014-12-15 09:24:24 -080035import org.onosproject.core.DefaultApplicationId;
36import org.onosproject.core.IdGenerator;
37import org.onosproject.net.NetworkResource;
Ray Milkey7b158512015-07-21 16:32:43 -070038import org.onosproject.net.intent.FakeIntentManager;
Ray Milkey2b217142014-12-15 09:24:24 -080039import org.onosproject.net.intent.Intent;
Ray Milkey2b217142014-12-15 09:24:24 -080040import org.onosproject.net.intent.IntentService;
Ray Milkey1534f8d2015-05-13 15:42:50 -070041import org.onosproject.net.intent.IntentState;
Ray Milkeyf9af43c2015-02-09 16:45:48 -080042import org.onosproject.net.intent.Key;
Ray Milkey43a28222015-02-23 13:57:58 -080043import org.onosproject.net.intent.MockIdGenerator;
Ray Milkey2b217142014-12-15 09:24:24 -080044
Jian Li9d616492016-03-09 10:52:49 -080045import javax.ws.rs.NotFoundException;
46import javax.ws.rs.client.Entity;
47import javax.ws.rs.client.WebTarget;
Thomas Vachuskaa026be72015-12-07 16:00:37 -080048import javax.ws.rs.core.MediaType;
Jian Li9d616492016-03-09 10:52:49 -080049import javax.ws.rs.core.Response;
Thomas Vachuskaa026be72015-12-07 16:00:37 -080050import java.io.InputStream;
51import java.net.HttpURLConnection;
52import java.util.Collections;
53import java.util.HashSet;
Ray Milkey2b217142014-12-15 09:24:24 -080054
Jian Li9d616492016-03-09 10:52:49 -080055import static org.easymock.EasyMock.anyObject;
56import static org.easymock.EasyMock.createMock;
57import static org.easymock.EasyMock.expect;
58import static org.easymock.EasyMock.expectLastCall;
59import static org.easymock.EasyMock.replay;
60import static org.easymock.EasyMock.verify;
61import static org.hamcrest.Matchers.containsString;
62import static org.hamcrest.Matchers.hasSize;
63import static org.hamcrest.Matchers.is;
64import static org.hamcrest.Matchers.notNullValue;
Ray Milkey2b217142014-12-15 09:24:24 -080065import static org.junit.Assert.assertThat;
66import static org.junit.Assert.fail;
Ray Milkey43a28222015-02-23 13:57:58 -080067import static org.onosproject.net.intent.IntentTestsMocks.MockIntent;
Ray Milkey2b217142014-12-15 09:24:24 -080068
69/**
70 * Unit tests for Intents REST APIs.
71 */
Ray Milkey9c3d3362015-01-28 10:39:56 -080072public class IntentsResourceTest extends ResourceTest {
Ray Milkey2b217142014-12-15 09:24:24 -080073 final IntentService mockIntentService = createMock(IntentService.class);
Ayaka Koshibec06c89b2015-02-10 19:25:41 -080074 final CoreService mockCoreService = createMock(CoreService.class);
Ray Milkey2b217142014-12-15 09:24:24 -080075 final HashSet<Intent> intents = new HashSet<>();
Thomas Vachuska02aeb032015-01-06 22:36:30 -080076 private static final ApplicationId APP_ID = new DefaultApplicationId(1, "test");
Ray Milkey2b217142014-12-15 09:24:24 -080077 private IdGenerator mockGenerator;
78
Ray Milkey2b217142014-12-15 09:24:24 -080079 private class MockResource implements NetworkResource {
80 int id;
81
82 MockResource(int id) {
83 this.id = id;
84 }
85
Ayaka Koshibec06c89b2015-02-10 19:25:41 -080086 @Override
Ray Milkey2b217142014-12-15 09:24:24 -080087 public String toString() {
88 return "Resource " + Integer.toString(id);
89 }
90 }
91
Ray Milkey2b217142014-12-15 09:24:24 -080092 /**
93 * Hamcrest matcher to check that an intent representation in JSON matches
94 * the actual intent.
95 */
96 public static class IntentJsonMatcher extends TypeSafeMatcher<JsonObject> {
97 private final Intent intent;
98 private String reason = "";
99
100 public IntentJsonMatcher(Intent intentValue) {
101 intent = intentValue;
102 }
103
104 @Override
105 public boolean matchesSafely(JsonObject jsonIntent) {
106 // check id
107 final String jsonId = jsonIntent.get("id").asString();
108 if (!jsonId.equals(intent.id().toString())) {
109 reason = "id " + intent.id().toString();
110 return false;
111 }
112
113 // check application id
Ray Milkeyf7cb4012015-07-20 13:01:07 -0700114
Ray Milkey2b217142014-12-15 09:24:24 -0800115 final String jsonAppId = jsonIntent.get("appId").asString();
Ray Milkeyf7cb4012015-07-20 13:01:07 -0700116 final String appId = intent.appId().name();
117 if (!jsonAppId.equals(appId)) {
118 reason = "appId was " + jsonAppId;
Ray Milkey2b217142014-12-15 09:24:24 -0800119 return false;
120 }
121
122 // check intent type
123 final String jsonType = jsonIntent.get("type").asString();
124 if (!jsonType.equals("MockIntent")) {
125 reason = "type MockIntent";
126 return false;
127 }
128
Ray Milkey1534f8d2015-05-13 15:42:50 -0700129 // check state field
130 final String jsonState = jsonIntent.get("state").asString();
131 if (!jsonState.equals("INSTALLED")) {
132 reason = "state INSTALLED";
133 return false;
134 }
135
Ray Milkey2b217142014-12-15 09:24:24 -0800136 // check resources array
137 final JsonArray jsonResources = jsonIntent.get("resources").asArray();
138 if (intent.resources() != null) {
139 if (intent.resources().size() != jsonResources.size()) {
140 reason = "resources array size of " + Integer.toString(intent.resources().size());
141 return false;
142 }
143 for (final NetworkResource resource : intent.resources()) {
144 boolean resourceFound = false;
145 final String resourceString = resource.toString();
146 for (int resourceIndex = 0; resourceIndex < jsonResources.size(); resourceIndex++) {
147 final JsonValue value = jsonResources.get(resourceIndex);
148 if (value.asString().equals(resourceString)) {
149 resourceFound = true;
150 }
151 }
152 if (!resourceFound) {
153 reason = "resource " + resourceString;
154 return false;
155 }
156 }
157 } else if (jsonResources.size() != 0) {
158 reason = "resources array empty";
159 return false;
160 }
161 return true;
162 }
163
164 @Override
165 public void describeTo(Description description) {
166 description.appendText(reason);
167 }
168 }
169
170 /**
171 * Factory to allocate an intent matcher.
172 *
173 * @param intent intent object we are looking for
174 * @return matcher
175 */
176 private static IntentJsonMatcher matchesIntent(Intent intent) {
177 return new IntentJsonMatcher(intent);
178 }
179
180 /**
181 * Hamcrest matcher to check that an intent is represented properly in a JSON
182 * array of intents.
183 */
184 public static class IntentJsonArrayMatcher extends TypeSafeMatcher<JsonArray> {
185 private final Intent intent;
186 private String reason = "";
187
188 public IntentJsonArrayMatcher(Intent intentValue) {
189 intent = intentValue;
190 }
191
192 @Override
193 public boolean matchesSafely(JsonArray json) {
194 boolean intentFound = false;
Ray Milkeyf7cb4012015-07-20 13:01:07 -0700195 final int expectedAttributes = 5;
Ray Milkey2b217142014-12-15 09:24:24 -0800196 for (int jsonIntentIndex = 0; jsonIntentIndex < json.size();
197 jsonIntentIndex++) {
198
199 final JsonObject jsonIntent = json.get(jsonIntentIndex).asObject();
200
201 if (jsonIntent.names().size() != expectedAttributes) {
202 reason = "Found an intent with the wrong number of attributes";
203 return false;
204 }
205
206 final String jsonIntentId = jsonIntent.get("id").asString();
207 if (jsonIntentId.equals(intent.id().toString())) {
208 intentFound = true;
209
210 // We found the correct intent, check attribute values
211 assertThat(jsonIntent, matchesIntent(intent));
212 }
213 }
214 if (!intentFound) {
215 reason = "Intent with id " + intent.id().toString() + " not found";
216 return false;
217 } else {
218 return true;
219 }
220 }
221
222 @Override
223 public void describeTo(Description description) {
224 description.appendText(reason);
225 }
226 }
227
228 /**
229 * Factory to allocate an intent array matcher.
230 *
231 * @param intent intent object we are looking for
232 * @return matcher
233 */
234 private static IntentJsonArrayMatcher hasIntent(Intent intent) {
235 return new IntentJsonArrayMatcher(intent);
236 }
Ray Milkey4f5de002014-12-17 19:26:11 -0800237
Ray Milkeyed0b1662015-02-05 09:34:29 -0800238 /**
239 * Initializes test mocks and environment.
240 */
Ray Milkey2b217142014-12-15 09:24:24 -0800241 @Before
Ray Milkeyed0b1662015-02-05 09:34:29 -0800242 public void setUpTest() {
Ray Milkey2b217142014-12-15 09:24:24 -0800243 expect(mockIntentService.getIntents()).andReturn(intents).anyTimes();
Ray Milkey1534f8d2015-05-13 15:42:50 -0700244 expect(mockIntentService.getIntentState(anyObject()))
245 .andReturn(IntentState.INSTALLED)
246 .anyTimes();
Ray Milkey2b217142014-12-15 09:24:24 -0800247 // Register the services needed for the test
248 final CodecManager codecService = new CodecManager();
249 codecService.activate();
250 ServiceDirectory testDirectory =
251 new TestServiceDirectory()
252 .add(IntentService.class, mockIntentService)
Ayaka Koshibec06c89b2015-02-10 19:25:41 -0800253 .add(CodecService.class, codecService)
254 .add(CoreService.class, mockCoreService);
Ray Milkey2b217142014-12-15 09:24:24 -0800255
256 BaseResource.setServiceDirectory(testDirectory);
257
258 mockGenerator = new MockIdGenerator();
259 Intent.bindIdGenerator(mockGenerator);
260 }
261
Ray Milkeyed0b1662015-02-05 09:34:29 -0800262 /**
263 * Tears down and verifies test mocks and environment.
264 */
Ray Milkey2b217142014-12-15 09:24:24 -0800265 @After
Ray Milkeyed0b1662015-02-05 09:34:29 -0800266 public void tearDownTest() {
Ray Milkey2b217142014-12-15 09:24:24 -0800267 verify(mockIntentService);
268 Intent.unbindIdGenerator(mockGenerator);
269 }
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")
459 .request(MediaType.APPLICATION_JSON_TYPE)
460 .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")
487 .request(MediaType.APPLICATION_JSON_TYPE)
488 .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}