blob: 46ecbeb0aaad37bb649fe0b8cb4fe64e51ea3c9a [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;
22import com.sun.jersey.api.client.ClientResponse;
23import com.sun.jersey.api.client.UniformInterfaceException;
24import com.sun.jersey.api.client.WebResource;
Ray Milkey2b217142014-12-15 09:24:24 -080025import org.hamcrest.Description;
Ray Milkeyb82c42b2015-06-30 09:42:20 -070026import org.hamcrest.Matchers;
Ray Milkey2b217142014-12-15 09:24:24 -080027import org.hamcrest.TypeSafeMatcher;
28import org.junit.After;
29import org.junit.Before;
30import org.junit.Test;
31import org.onlab.osgi.ServiceDirectory;
32import org.onlab.osgi.TestServiceDirectory;
33import org.onlab.rest.BaseResource;
34import org.onosproject.codec.CodecService;
35import org.onosproject.codec.impl.CodecManager;
36import 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;
39import org.onosproject.core.IdGenerator;
40import org.onosproject.net.NetworkResource;
Ray Milkey7b158512015-07-21 16:32:43 -070041import org.onosproject.net.intent.FakeIntentManager;
Ray Milkey2b217142014-12-15 09:24:24 -080042import org.onosproject.net.intent.Intent;
Ray Milkey2b217142014-12-15 09:24:24 -080043import org.onosproject.net.intent.IntentService;
Ray Milkey1534f8d2015-05-13 15:42:50 -070044import org.onosproject.net.intent.IntentState;
Ray Milkeyf9af43c2015-02-09 16:45:48 -080045import org.onosproject.net.intent.Key;
Ray Milkey43a28222015-02-23 13:57:58 -080046import org.onosproject.net.intent.MockIdGenerator;
Thomas Vachuskaa026be72015-12-07 16:00:37 -080047import org.onosproject.rest.resources.CoreWebApplication;
Ray Milkey2b217142014-12-15 09:24:24 -080048
Thomas Vachuskaa026be72015-12-07 16:00:37 -080049import javax.ws.rs.core.MediaType;
50import java.io.InputStream;
51import java.net.HttpURLConnection;
52import java.util.Collections;
53import java.util.HashSet;
Ray Milkey2b217142014-12-15 09:24:24 -080054
Thomas Vachuskaa026be72015-12-07 16:00:37 -080055import static org.easymock.EasyMock.*;
56import static org.hamcrest.Matchers.*;
Ray Milkey2b217142014-12-15 09:24:24 -080057import static org.junit.Assert.assertThat;
58import static org.junit.Assert.fail;
Ray Milkey43a28222015-02-23 13:57:58 -080059import static org.onosproject.net.intent.IntentTestsMocks.MockIntent;
Ray Milkey2b217142014-12-15 09:24:24 -080060
61/**
62 * Unit tests for Intents REST APIs.
63 */
Ray Milkey9c3d3362015-01-28 10:39:56 -080064public class IntentsResourceTest extends ResourceTest {
Ray Milkey2b217142014-12-15 09:24:24 -080065 final IntentService mockIntentService = createMock(IntentService.class);
Ayaka Koshibec06c89b2015-02-10 19:25:41 -080066 final CoreService mockCoreService = createMock(CoreService.class);
Ray Milkey2b217142014-12-15 09:24:24 -080067 final HashSet<Intent> intents = new HashSet<>();
Thomas Vachuska02aeb032015-01-06 22:36:30 -080068 private static final ApplicationId APP_ID = new DefaultApplicationId(1, "test");
Ray Milkey2b217142014-12-15 09:24:24 -080069 private IdGenerator mockGenerator;
70
Thomas Vachuskaa026be72015-12-07 16:00:37 -080071 public IntentsResourceTest() {
72 super(CoreWebApplication.class);
73 }
74
Ray Milkey2b217142014-12-15 09:24:24 -080075 private class MockResource implements NetworkResource {
76 int id;
77
78 MockResource(int id) {
79 this.id = id;
80 }
81
Ayaka Koshibec06c89b2015-02-10 19:25:41 -080082 @Override
Ray Milkey2b217142014-12-15 09:24:24 -080083 public String toString() {
84 return "Resource " + Integer.toString(id);
85 }
86 }
87
Ray Milkey2b217142014-12-15 09:24:24 -080088 /**
89 * Hamcrest matcher to check that an intent representation in JSON matches
90 * the actual intent.
91 */
92 public static class IntentJsonMatcher extends TypeSafeMatcher<JsonObject> {
93 private final Intent intent;
94 private String reason = "";
95
96 public IntentJsonMatcher(Intent intentValue) {
97 intent = intentValue;
98 }
99
100 @Override
101 public boolean matchesSafely(JsonObject jsonIntent) {
102 // check id
103 final String jsonId = jsonIntent.get("id").asString();
104 if (!jsonId.equals(intent.id().toString())) {
105 reason = "id " + intent.id().toString();
106 return false;
107 }
108
109 // check application id
Ray Milkeyf7cb4012015-07-20 13:01:07 -0700110
Ray Milkey2b217142014-12-15 09:24:24 -0800111 final String jsonAppId = jsonIntent.get("appId").asString();
Ray Milkeyf7cb4012015-07-20 13:01:07 -0700112 final String appId = intent.appId().name();
113 if (!jsonAppId.equals(appId)) {
114 reason = "appId was " + jsonAppId;
Ray Milkey2b217142014-12-15 09:24:24 -0800115 return false;
116 }
117
118 // check intent type
119 final String jsonType = jsonIntent.get("type").asString();
120 if (!jsonType.equals("MockIntent")) {
121 reason = "type MockIntent";
122 return false;
123 }
124
Ray Milkey1534f8d2015-05-13 15:42:50 -0700125 // check state field
126 final String jsonState = jsonIntent.get("state").asString();
127 if (!jsonState.equals("INSTALLED")) {
128 reason = "state INSTALLED";
129 return false;
130 }
131
Ray Milkey2b217142014-12-15 09:24:24 -0800132 // check resources array
133 final JsonArray jsonResources = jsonIntent.get("resources").asArray();
134 if (intent.resources() != null) {
135 if (intent.resources().size() != jsonResources.size()) {
136 reason = "resources array size of " + Integer.toString(intent.resources().size());
137 return false;
138 }
139 for (final NetworkResource resource : intent.resources()) {
140 boolean resourceFound = false;
141 final String resourceString = resource.toString();
142 for (int resourceIndex = 0; resourceIndex < jsonResources.size(); resourceIndex++) {
143 final JsonValue value = jsonResources.get(resourceIndex);
144 if (value.asString().equals(resourceString)) {
145 resourceFound = true;
146 }
147 }
148 if (!resourceFound) {
149 reason = "resource " + resourceString;
150 return false;
151 }
152 }
153 } else if (jsonResources.size() != 0) {
154 reason = "resources array empty";
155 return false;
156 }
157 return true;
158 }
159
160 @Override
161 public void describeTo(Description description) {
162 description.appendText(reason);
163 }
164 }
165
166 /**
167 * Factory to allocate an intent matcher.
168 *
169 * @param intent intent object we are looking for
170 * @return matcher
171 */
172 private static IntentJsonMatcher matchesIntent(Intent intent) {
173 return new IntentJsonMatcher(intent);
174 }
175
176 /**
177 * Hamcrest matcher to check that an intent is represented properly in a JSON
178 * array of intents.
179 */
180 public static class IntentJsonArrayMatcher extends TypeSafeMatcher<JsonArray> {
181 private final Intent intent;
182 private String reason = "";
183
184 public IntentJsonArrayMatcher(Intent intentValue) {
185 intent = intentValue;
186 }
187
188 @Override
189 public boolean matchesSafely(JsonArray json) {
190 boolean intentFound = false;
Ray Milkeyf7cb4012015-07-20 13:01:07 -0700191 final int expectedAttributes = 5;
Ray Milkey2b217142014-12-15 09:24:24 -0800192 for (int jsonIntentIndex = 0; jsonIntentIndex < json.size();
193 jsonIntentIndex++) {
194
195 final JsonObject jsonIntent = json.get(jsonIntentIndex).asObject();
196
197 if (jsonIntent.names().size() != expectedAttributes) {
198 reason = "Found an intent with the wrong number of attributes";
199 return false;
200 }
201
202 final String jsonIntentId = jsonIntent.get("id").asString();
203 if (jsonIntentId.equals(intent.id().toString())) {
204 intentFound = true;
205
206 // We found the correct intent, check attribute values
207 assertThat(jsonIntent, matchesIntent(intent));
208 }
209 }
210 if (!intentFound) {
211 reason = "Intent with id " + intent.id().toString() + " not found";
212 return false;
213 } else {
214 return true;
215 }
216 }
217
218 @Override
219 public void describeTo(Description description) {
220 description.appendText(reason);
221 }
222 }
223
224 /**
225 * Factory to allocate an intent array matcher.
226 *
227 * @param intent intent object we are looking for
228 * @return matcher
229 */
230 private static IntentJsonArrayMatcher hasIntent(Intent intent) {
231 return new IntentJsonArrayMatcher(intent);
232 }
Ray Milkey4f5de002014-12-17 19:26:11 -0800233
Ray Milkeyed0b1662015-02-05 09:34:29 -0800234 /**
235 * Initializes test mocks and environment.
236 */
Ray Milkey2b217142014-12-15 09:24:24 -0800237 @Before
Ray Milkeyed0b1662015-02-05 09:34:29 -0800238 public void setUpTest() {
Ray Milkey2b217142014-12-15 09:24:24 -0800239 expect(mockIntentService.getIntents()).andReturn(intents).anyTimes();
Ray Milkey1534f8d2015-05-13 15:42:50 -0700240 expect(mockIntentService.getIntentState(anyObject()))
241 .andReturn(IntentState.INSTALLED)
242 .anyTimes();
Ray Milkey2b217142014-12-15 09:24:24 -0800243 // Register the services needed for the test
244 final CodecManager codecService = new CodecManager();
245 codecService.activate();
246 ServiceDirectory testDirectory =
247 new TestServiceDirectory()
248 .add(IntentService.class, mockIntentService)
Ayaka Koshibec06c89b2015-02-10 19:25:41 -0800249 .add(CodecService.class, codecService)
250 .add(CoreService.class, mockCoreService);
Ray Milkey2b217142014-12-15 09:24:24 -0800251
252 BaseResource.setServiceDirectory(testDirectory);
253
254 mockGenerator = new MockIdGenerator();
255 Intent.bindIdGenerator(mockGenerator);
256 }
257
Ray Milkeyed0b1662015-02-05 09:34:29 -0800258 /**
259 * Tears down and verifies test mocks and environment.
260 */
Ray Milkey2b217142014-12-15 09:24:24 -0800261 @After
Ray Milkeyed0b1662015-02-05 09:34:29 -0800262 public void tearDownTest() {
Ray Milkey2b217142014-12-15 09:24:24 -0800263 verify(mockIntentService);
264 Intent.unbindIdGenerator(mockGenerator);
265 }
266
267 /**
268 * Tests the result of the rest api GET when there are no intents.
269 */
270 @Test
271 public void testIntentsEmptyArray() {
272 replay(mockIntentService);
273 final WebResource rs = resource();
274 final String response = rs.path("intents").get(String.class);
275 assertThat(response, is("{\"intents\":[]}"));
276 }
277
278 /**
279 * Tests the result of the rest api GET when intents are defined.
280 */
281 @Test
282 public void testIntentsArray() {
283 replay(mockIntentService);
284
Ray Milkey43a28222015-02-23 13:57:58 -0800285 final Intent intent1 = new MockIntent(1L, Collections.emptyList());
Ray Milkey2b217142014-12-15 09:24:24 -0800286 final HashSet<NetworkResource> resources = new HashSet<>();
287 resources.add(new MockResource(1));
288 resources.add(new MockResource(2));
289 resources.add(new MockResource(3));
Ray Milkey43a28222015-02-23 13:57:58 -0800290 final Intent intent2 = new MockIntent(2L, resources);
Ray Milkey2b217142014-12-15 09:24:24 -0800291
292 intents.add(intent1);
293 intents.add(intent2);
294 final WebResource rs = resource();
295 final String response = rs.path("intents").get(String.class);
296 assertThat(response, containsString("{\"intents\":["));
297
Jian Li80cfe452016-01-14 16:04:58 -0800298 final JsonObject result = Json.parse(response).asObject();
Ray Milkey2b217142014-12-15 09:24:24 -0800299 assertThat(result, notNullValue());
300
301 assertThat(result.names(), hasSize(1));
302 assertThat(result.names().get(0), is("intents"));
303
304 final JsonArray jsonIntents = result.get("intents").asArray();
305 assertThat(jsonIntents, notNullValue());
306
307 assertThat(jsonIntents, hasIntent(intent1));
308 assertThat(jsonIntents, hasIntent(intent2));
309 }
310
311 /**
312 * Tests the result of a rest api GET for a single intent.
313 */
314 @Test
315 public void testIntentsSingle() {
316 final HashSet<NetworkResource> resources = new HashSet<>();
317 resources.add(new MockResource(1));
318 resources.add(new MockResource(2));
319 resources.add(new MockResource(3));
Ray Milkey43a28222015-02-23 13:57:58 -0800320 final Intent intent = new MockIntent(3L, resources);
Ray Milkey2b217142014-12-15 09:24:24 -0800321
322 intents.add(intent);
323
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800324 expect(mockIntentService.getIntent(Key.of(0, APP_ID)))
Ray Milkey2b217142014-12-15 09:24:24 -0800325 .andReturn(intent)
326 .anyTimes();
Ayaka Koshibec06c89b2015-02-10 19:25:41 -0800327 expect(mockIntentService.getIntent(Key.of("0", APP_ID)))
328 .andReturn(intent)
329 .anyTimes();
Ray Milkey05b169d2015-08-13 14:33:33 -0700330 expect(mockIntentService.getIntent(Key.of(0, APP_ID)))
331 .andReturn(intent)
332 .anyTimes();
333 expect(mockIntentService.getIntent(Key.of("0x0", APP_ID)))
334 .andReturn(null)
335 .anyTimes();
Ray Milkey2b217142014-12-15 09:24:24 -0800336 replay(mockIntentService);
Ray Milkeyf7cb4012015-07-20 13:01:07 -0700337 expect(mockCoreService.getAppId(APP_ID.name()))
Ayaka Koshibec06c89b2015-02-10 19:25:41 -0800338 .andReturn(APP_ID).anyTimes();
339 replay(mockCoreService);
Ray Milkey2b217142014-12-15 09:24:24 -0800340 final WebResource rs = resource();
Ray Milkey05b169d2015-08-13 14:33:33 -0700341
342 // Test get using key string
Ray Milkeyf7cb4012015-07-20 13:01:07 -0700343 final String response = rs.path("intents/" + APP_ID.name()
344 + "/0").get(String.class);
Jian Li80cfe452016-01-14 16:04:58 -0800345 final JsonObject result = Json.parse(response).asObject();
Ray Milkey2b217142014-12-15 09:24:24 -0800346 assertThat(result, matchesIntent(intent));
Ray Milkey05b169d2015-08-13 14:33:33 -0700347
348 // Test get using numeric value
349 final String responseNumeric = rs.path("intents/" + APP_ID.name()
350 + "/0x0").get(String.class);
Jian Li80cfe452016-01-14 16:04:58 -0800351 final JsonObject resultNumeric = Json.parse(responseNumeric).asObject();
Ray Milkey05b169d2015-08-13 14:33:33 -0700352 assertThat(resultNumeric, matchesIntent(intent));
Ray Milkey2b217142014-12-15 09:24:24 -0800353 }
354
355 /**
356 * Tests that a fetch of a non-existent intent object throws an exception.
357 */
358 @Test
359 public void testBadGet() {
360
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800361 expect(mockIntentService.getIntent(Key.of(0, APP_ID)))
Ray Milkey2b217142014-12-15 09:24:24 -0800362 .andReturn(null)
363 .anyTimes();
364 replay(mockIntentService);
365
366 WebResource rs = resource();
367 try {
368 rs.path("intents/0").get(String.class);
369 fail("Fetch of non-existent intent did not throw an exception");
370 } catch (UniformInterfaceException ex) {
371 assertThat(ex.getMessage(),
372 containsString("returned a response status of"));
373 }
374 }
Ray Milkeyb82c42b2015-06-30 09:42:20 -0700375
376 /**
377 * Tests creating an intent with POST.
378 */
379 @Test
380 public void testPost() {
Ray Milkeyf7cb4012015-07-20 13:01:07 -0700381 ApplicationId testId = new DefaultApplicationId(2, "myApp");
382 expect(mockCoreService.getAppId("myApp"))
383 .andReturn(testId);
Ray Milkeyb82c42b2015-06-30 09:42:20 -0700384 replay(mockCoreService);
385
386 mockIntentService.submit(anyObject());
387 expectLastCall();
388 replay(mockIntentService);
389
390 InputStream jsonStream = IntentsResourceTest.class
391 .getResourceAsStream("post-intent.json");
392 WebResource rs = resource();
393
394 ClientResponse response = rs.path("intents")
395 .type(MediaType.APPLICATION_JSON_TYPE)
396 .post(ClientResponse.class, jsonStream);
397 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
398 String location = response.getLocation().getPath();
Ray Milkey8d076402015-08-31 15:43:18 -0700399 assertThat(location, Matchers.startsWith("/intents/myApp/"));
Ray Milkeyb82c42b2015-06-30 09:42:20 -0700400 }
Ray Milkey7b158512015-07-21 16:32:43 -0700401
402 /**
Ray Milkey05b169d2015-08-13 14:33:33 -0700403 * Tests creating an intent with POST and illegal JSON.
404 */
405 @Test
406 public void testBadPost() {
407 replay(mockCoreService);
408 replay(mockIntentService);
409
410 String json = "this is invalid!";
411 WebResource rs = resource();
412
413 ClientResponse response = rs.path("intents")
414 .type(MediaType.APPLICATION_JSON_TYPE)
415 .post(ClientResponse.class, json);
416 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_BAD_REQUEST));
417 }
418
419 /**
Ray Milkey7b158512015-07-21 16:32:43 -0700420 * Tests removing an intent with DELETE.
421 */
422 @Test
423 public void testRemove() {
424 final HashSet<NetworkResource> resources = new HashSet<>();
425 resources.add(new MockResource(1));
426 resources.add(new MockResource(2));
427 resources.add(new MockResource(3));
428 final Intent intent = new MockIntent(3L, resources);
429 final ApplicationId appId = new DefaultApplicationId(2, "app");
430 IntentService fakeManager = new FakeIntentManager();
431
432 expect(mockCoreService.getAppId("app"))
433 .andReturn(appId).once();
434 replay(mockCoreService);
435
436 mockIntentService.withdraw(anyObject());
437 expectLastCall().andDelegateTo(fakeManager).once();
438 expect(mockIntentService.getIntent(Key.of(2, appId)))
439 .andReturn(intent)
440 .once();
441 expect(mockIntentService.getIntent(Key.of("0x2", appId)))
442 .andReturn(null)
443 .once();
444
445 mockIntentService.addListener(anyObject());
446 expectLastCall().andDelegateTo(fakeManager).once();
447 mockIntentService.removeListener(anyObject());
448 expectLastCall().andDelegateTo(fakeManager).once();
449
450 replay(mockIntentService);
451
452 WebResource rs = resource();
453
454 ClientResponse response = rs.path("intents/app/0x2")
455 .type(MediaType.APPLICATION_JSON_TYPE)
456 .delete(ClientResponse.class);
457 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_NO_CONTENT));
458 }
Ray Milkey05b169d2015-08-13 14:33:33 -0700459
460 /**
461 * Tests removal of a non existent intent with DELETE.
462 */
463 @Test
464 public void testBadRemove() {
465 final ApplicationId appId = new DefaultApplicationId(2, "app");
466
467 expect(mockCoreService.getAppId("app"))
468 .andReturn(appId).once();
469 replay(mockCoreService);
470
471 expect(mockIntentService.getIntent(Key.of(2, appId)))
472 .andReturn(null)
473 .once();
474 expect(mockIntentService.getIntent(Key.of("0x2", appId)))
475 .andReturn(null)
476 .once();
477
478 replay(mockIntentService);
479
480 WebResource rs = resource();
481
482 ClientResponse response = rs.path("intents/app/0x2")
483 .type(MediaType.APPLICATION_JSON_TYPE)
484 .delete(ClientResponse.class);
485 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_NO_CONTENT));
486 }
487
Ray Milkey2b217142014-12-15 09:24:24 -0800488}