blob: ddbf1977d61483d66dfeafbe90f084f82e8923ba [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
Thomas Vachuskaa026be72015-12-07 16:00:37 -080018import com.eclipsesource.json.JsonArray;
19import com.eclipsesource.json.JsonObject;
20import com.eclipsesource.json.JsonValue;
21import com.sun.jersey.api.client.ClientResponse;
22import com.sun.jersey.api.client.UniformInterfaceException;
23import com.sun.jersey.api.client.WebResource;
Ray Milkey2b217142014-12-15 09:24:24 -080024import org.hamcrest.Description;
Ray Milkeyb82c42b2015-06-30 09:42:20 -070025import org.hamcrest.Matchers;
Ray Milkey2b217142014-12-15 09:24:24 -080026import org.hamcrest.TypeSafeMatcher;
27import org.junit.After;
28import org.junit.Before;
29import org.junit.Test;
30import org.onlab.osgi.ServiceDirectory;
31import org.onlab.osgi.TestServiceDirectory;
32import org.onlab.rest.BaseResource;
33import org.onosproject.codec.CodecService;
34import org.onosproject.codec.impl.CodecManager;
35import org.onosproject.core.ApplicationId;
Ayaka Koshibec06c89b2015-02-10 19:25:41 -080036import org.onosproject.core.CoreService;
Ray Milkey2b217142014-12-15 09:24:24 -080037import org.onosproject.core.DefaultApplicationId;
38import org.onosproject.core.IdGenerator;
39import org.onosproject.net.NetworkResource;
Ray Milkey7b158512015-07-21 16:32:43 -070040import org.onosproject.net.intent.FakeIntentManager;
Ray Milkey2b217142014-12-15 09:24:24 -080041import org.onosproject.net.intent.Intent;
Ray Milkey2b217142014-12-15 09:24:24 -080042import org.onosproject.net.intent.IntentService;
Ray Milkey1534f8d2015-05-13 15:42:50 -070043import org.onosproject.net.intent.IntentState;
Ray Milkeyf9af43c2015-02-09 16:45:48 -080044import org.onosproject.net.intent.Key;
Ray Milkey43a28222015-02-23 13:57:58 -080045import org.onosproject.net.intent.MockIdGenerator;
Thomas Vachuskaa026be72015-12-07 16:00:37 -080046import org.onosproject.rest.resources.CoreWebApplication;
Ray Milkey2b217142014-12-15 09:24:24 -080047
Thomas Vachuskaa026be72015-12-07 16:00:37 -080048import javax.ws.rs.core.MediaType;
49import java.io.InputStream;
50import java.net.HttpURLConnection;
51import java.util.Collections;
52import java.util.HashSet;
Ray Milkey2b217142014-12-15 09:24:24 -080053
Thomas Vachuskaa026be72015-12-07 16:00:37 -080054import static org.easymock.EasyMock.*;
55import static org.hamcrest.Matchers.*;
Ray Milkey2b217142014-12-15 09:24:24 -080056import static org.junit.Assert.assertThat;
57import static org.junit.Assert.fail;
Ray Milkey43a28222015-02-23 13:57:58 -080058import static org.onosproject.net.intent.IntentTestsMocks.MockIntent;
Ray Milkey2b217142014-12-15 09:24:24 -080059
60/**
61 * Unit tests for Intents REST APIs.
62 */
Ray Milkey9c3d3362015-01-28 10:39:56 -080063public class IntentsResourceTest extends ResourceTest {
Ray Milkey2b217142014-12-15 09:24:24 -080064 final IntentService mockIntentService = createMock(IntentService.class);
Ayaka Koshibec06c89b2015-02-10 19:25:41 -080065 final CoreService mockCoreService = createMock(CoreService.class);
Ray Milkey2b217142014-12-15 09:24:24 -080066 final HashSet<Intent> intents = new HashSet<>();
Thomas Vachuska02aeb032015-01-06 22:36:30 -080067 private static final ApplicationId APP_ID = new DefaultApplicationId(1, "test");
Ray Milkey2b217142014-12-15 09:24:24 -080068 private IdGenerator mockGenerator;
69
Thomas Vachuskaa026be72015-12-07 16:00:37 -080070 public IntentsResourceTest() {
71 super(CoreWebApplication.class);
72 }
73
Ray Milkey2b217142014-12-15 09:24:24 -080074 private class MockResource implements NetworkResource {
75 int id;
76
77 MockResource(int id) {
78 this.id = id;
79 }
80
Ayaka Koshibec06c89b2015-02-10 19:25:41 -080081 @Override
Ray Milkey2b217142014-12-15 09:24:24 -080082 public String toString() {
83 return "Resource " + Integer.toString(id);
84 }
85 }
86
Ray Milkey2b217142014-12-15 09:24:24 -080087 /**
88 * Hamcrest matcher to check that an intent representation in JSON matches
89 * the actual intent.
90 */
91 public static class IntentJsonMatcher extends TypeSafeMatcher<JsonObject> {
92 private final Intent intent;
93 private String reason = "";
94
95 public IntentJsonMatcher(Intent intentValue) {
96 intent = intentValue;
97 }
98
99 @Override
100 public boolean matchesSafely(JsonObject jsonIntent) {
101 // check id
102 final String jsonId = jsonIntent.get("id").asString();
103 if (!jsonId.equals(intent.id().toString())) {
104 reason = "id " + intent.id().toString();
105 return false;
106 }
107
108 // check application id
Ray Milkeyf7cb4012015-07-20 13:01:07 -0700109
Ray Milkey2b217142014-12-15 09:24:24 -0800110 final String jsonAppId = jsonIntent.get("appId").asString();
Ray Milkeyf7cb4012015-07-20 13:01:07 -0700111 final String appId = intent.appId().name();
112 if (!jsonAppId.equals(appId)) {
113 reason = "appId was " + jsonAppId;
Ray Milkey2b217142014-12-15 09:24:24 -0800114 return false;
115 }
116
117 // check intent type
118 final String jsonType = jsonIntent.get("type").asString();
119 if (!jsonType.equals("MockIntent")) {
120 reason = "type MockIntent";
121 return false;
122 }
123
Ray Milkey1534f8d2015-05-13 15:42:50 -0700124 // check state field
125 final String jsonState = jsonIntent.get("state").asString();
126 if (!jsonState.equals("INSTALLED")) {
127 reason = "state INSTALLED";
128 return false;
129 }
130
Ray Milkey2b217142014-12-15 09:24:24 -0800131 // check resources array
132 final JsonArray jsonResources = jsonIntent.get("resources").asArray();
133 if (intent.resources() != null) {
134 if (intent.resources().size() != jsonResources.size()) {
135 reason = "resources array size of " + Integer.toString(intent.resources().size());
136 return false;
137 }
138 for (final NetworkResource resource : intent.resources()) {
139 boolean resourceFound = false;
140 final String resourceString = resource.toString();
141 for (int resourceIndex = 0; resourceIndex < jsonResources.size(); resourceIndex++) {
142 final JsonValue value = jsonResources.get(resourceIndex);
143 if (value.asString().equals(resourceString)) {
144 resourceFound = true;
145 }
146 }
147 if (!resourceFound) {
148 reason = "resource " + resourceString;
149 return false;
150 }
151 }
152 } else if (jsonResources.size() != 0) {
153 reason = "resources array empty";
154 return false;
155 }
156 return true;
157 }
158
159 @Override
160 public void describeTo(Description description) {
161 description.appendText(reason);
162 }
163 }
164
165 /**
166 * Factory to allocate an intent matcher.
167 *
168 * @param intent intent object we are looking for
169 * @return matcher
170 */
171 private static IntentJsonMatcher matchesIntent(Intent intent) {
172 return new IntentJsonMatcher(intent);
173 }
174
175 /**
176 * Hamcrest matcher to check that an intent is represented properly in a JSON
177 * array of intents.
178 */
179 public static class IntentJsonArrayMatcher extends TypeSafeMatcher<JsonArray> {
180 private final Intent intent;
181 private String reason = "";
182
183 public IntentJsonArrayMatcher(Intent intentValue) {
184 intent = intentValue;
185 }
186
187 @Override
188 public boolean matchesSafely(JsonArray json) {
189 boolean intentFound = false;
Ray Milkeyf7cb4012015-07-20 13:01:07 -0700190 final int expectedAttributes = 5;
Ray Milkey2b217142014-12-15 09:24:24 -0800191 for (int jsonIntentIndex = 0; jsonIntentIndex < json.size();
192 jsonIntentIndex++) {
193
194 final JsonObject jsonIntent = json.get(jsonIntentIndex).asObject();
195
196 if (jsonIntent.names().size() != expectedAttributes) {
197 reason = "Found an intent with the wrong number of attributes";
198 return false;
199 }
200
201 final String jsonIntentId = jsonIntent.get("id").asString();
202 if (jsonIntentId.equals(intent.id().toString())) {
203 intentFound = true;
204
205 // We found the correct intent, check attribute values
206 assertThat(jsonIntent, matchesIntent(intent));
207 }
208 }
209 if (!intentFound) {
210 reason = "Intent with id " + intent.id().toString() + " not found";
211 return false;
212 } else {
213 return true;
214 }
215 }
216
217 @Override
218 public void describeTo(Description description) {
219 description.appendText(reason);
220 }
221 }
222
223 /**
224 * Factory to allocate an intent array matcher.
225 *
226 * @param intent intent object we are looking for
227 * @return matcher
228 */
229 private static IntentJsonArrayMatcher hasIntent(Intent intent) {
230 return new IntentJsonArrayMatcher(intent);
231 }
Ray Milkey4f5de002014-12-17 19:26:11 -0800232
Ray Milkeyed0b1662015-02-05 09:34:29 -0800233 /**
234 * Initializes test mocks and environment.
235 */
Ray Milkey2b217142014-12-15 09:24:24 -0800236 @Before
Ray Milkeyed0b1662015-02-05 09:34:29 -0800237 public void setUpTest() {
Ray Milkey2b217142014-12-15 09:24:24 -0800238 expect(mockIntentService.getIntents()).andReturn(intents).anyTimes();
Ray Milkey1534f8d2015-05-13 15:42:50 -0700239 expect(mockIntentService.getIntentState(anyObject()))
240 .andReturn(IntentState.INSTALLED)
241 .anyTimes();
Ray Milkey2b217142014-12-15 09:24:24 -0800242 // Register the services needed for the test
243 final CodecManager codecService = new CodecManager();
244 codecService.activate();
245 ServiceDirectory testDirectory =
246 new TestServiceDirectory()
247 .add(IntentService.class, mockIntentService)
Ayaka Koshibec06c89b2015-02-10 19:25:41 -0800248 .add(CodecService.class, codecService)
249 .add(CoreService.class, mockCoreService);
Ray Milkey2b217142014-12-15 09:24:24 -0800250
251 BaseResource.setServiceDirectory(testDirectory);
252
253 mockGenerator = new MockIdGenerator();
254 Intent.bindIdGenerator(mockGenerator);
255 }
256
Ray Milkeyed0b1662015-02-05 09:34:29 -0800257 /**
258 * Tears down and verifies test mocks and environment.
259 */
Ray Milkey2b217142014-12-15 09:24:24 -0800260 @After
Ray Milkeyed0b1662015-02-05 09:34:29 -0800261 public void tearDownTest() {
Ray Milkey2b217142014-12-15 09:24:24 -0800262 verify(mockIntentService);
263 Intent.unbindIdGenerator(mockGenerator);
264 }
265
266 /**
267 * Tests the result of the rest api GET when there are no intents.
268 */
269 @Test
270 public void testIntentsEmptyArray() {
271 replay(mockIntentService);
272 final WebResource rs = resource();
273 final String response = rs.path("intents").get(String.class);
274 assertThat(response, is("{\"intents\":[]}"));
275 }
276
277 /**
278 * Tests the result of the rest api GET when intents are defined.
279 */
280 @Test
281 public void testIntentsArray() {
282 replay(mockIntentService);
283
Ray Milkey43a28222015-02-23 13:57:58 -0800284 final Intent intent1 = new MockIntent(1L, Collections.emptyList());
Ray Milkey2b217142014-12-15 09:24:24 -0800285 final HashSet<NetworkResource> resources = new HashSet<>();
286 resources.add(new MockResource(1));
287 resources.add(new MockResource(2));
288 resources.add(new MockResource(3));
Ray Milkey43a28222015-02-23 13:57:58 -0800289 final Intent intent2 = new MockIntent(2L, resources);
Ray Milkey2b217142014-12-15 09:24:24 -0800290
291 intents.add(intent1);
292 intents.add(intent2);
293 final WebResource rs = resource();
294 final String response = rs.path("intents").get(String.class);
295 assertThat(response, containsString("{\"intents\":["));
296
297 final JsonObject result = JsonObject.readFrom(response);
298 assertThat(result, notNullValue());
299
300 assertThat(result.names(), hasSize(1));
301 assertThat(result.names().get(0), is("intents"));
302
303 final JsonArray jsonIntents = result.get("intents").asArray();
304 assertThat(jsonIntents, notNullValue());
305
306 assertThat(jsonIntents, hasIntent(intent1));
307 assertThat(jsonIntents, hasIntent(intent2));
308 }
309
310 /**
311 * Tests the result of a rest api GET for a single intent.
312 */
313 @Test
314 public void testIntentsSingle() {
315 final HashSet<NetworkResource> resources = new HashSet<>();
316 resources.add(new MockResource(1));
317 resources.add(new MockResource(2));
318 resources.add(new MockResource(3));
Ray Milkey43a28222015-02-23 13:57:58 -0800319 final Intent intent = new MockIntent(3L, resources);
Ray Milkey2b217142014-12-15 09:24:24 -0800320
321 intents.add(intent);
322
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800323 expect(mockIntentService.getIntent(Key.of(0, APP_ID)))
Ray Milkey2b217142014-12-15 09:24:24 -0800324 .andReturn(intent)
325 .anyTimes();
Ayaka Koshibec06c89b2015-02-10 19:25:41 -0800326 expect(mockIntentService.getIntent(Key.of("0", APP_ID)))
327 .andReturn(intent)
328 .anyTimes();
Ray Milkey05b169d2015-08-13 14:33:33 -0700329 expect(mockIntentService.getIntent(Key.of(0, APP_ID)))
330 .andReturn(intent)
331 .anyTimes();
332 expect(mockIntentService.getIntent(Key.of("0x0", APP_ID)))
333 .andReturn(null)
334 .anyTimes();
Ray Milkey2b217142014-12-15 09:24:24 -0800335 replay(mockIntentService);
Ray Milkeyf7cb4012015-07-20 13:01:07 -0700336 expect(mockCoreService.getAppId(APP_ID.name()))
Ayaka Koshibec06c89b2015-02-10 19:25:41 -0800337 .andReturn(APP_ID).anyTimes();
338 replay(mockCoreService);
Ray Milkey2b217142014-12-15 09:24:24 -0800339 final WebResource rs = resource();
Ray Milkey05b169d2015-08-13 14:33:33 -0700340
341 // Test get using key string
Ray Milkeyf7cb4012015-07-20 13:01:07 -0700342 final String response = rs.path("intents/" + APP_ID.name()
343 + "/0").get(String.class);
Ray Milkey2b217142014-12-15 09:24:24 -0800344 final JsonObject result = JsonObject.readFrom(response);
345 assertThat(result, matchesIntent(intent));
Ray Milkey05b169d2015-08-13 14:33:33 -0700346
347 // Test get using numeric value
348 final String responseNumeric = rs.path("intents/" + APP_ID.name()
349 + "/0x0").get(String.class);
350 final JsonObject resultNumeric = JsonObject.readFrom(responseNumeric);
351 assertThat(resultNumeric, matchesIntent(intent));
Ray Milkey2b217142014-12-15 09:24:24 -0800352 }
353
354 /**
355 * Tests that a fetch of a non-existent intent object throws an exception.
356 */
357 @Test
358 public void testBadGet() {
359
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800360 expect(mockIntentService.getIntent(Key.of(0, APP_ID)))
Ray Milkey2b217142014-12-15 09:24:24 -0800361 .andReturn(null)
362 .anyTimes();
363 replay(mockIntentService);
364
365 WebResource rs = resource();
366 try {
367 rs.path("intents/0").get(String.class);
368 fail("Fetch of non-existent intent did not throw an exception");
369 } catch (UniformInterfaceException ex) {
370 assertThat(ex.getMessage(),
371 containsString("returned a response status of"));
372 }
373 }
Ray Milkeyb82c42b2015-06-30 09:42:20 -0700374
375 /**
376 * Tests creating an intent with POST.
377 */
378 @Test
379 public void testPost() {
Ray Milkeyf7cb4012015-07-20 13:01:07 -0700380 ApplicationId testId = new DefaultApplicationId(2, "myApp");
381 expect(mockCoreService.getAppId("myApp"))
382 .andReturn(testId);
Ray Milkeyb82c42b2015-06-30 09:42:20 -0700383 replay(mockCoreService);
384
385 mockIntentService.submit(anyObject());
386 expectLastCall();
387 replay(mockIntentService);
388
389 InputStream jsonStream = IntentsResourceTest.class
390 .getResourceAsStream("post-intent.json");
391 WebResource rs = resource();
392
393 ClientResponse response = rs.path("intents")
394 .type(MediaType.APPLICATION_JSON_TYPE)
395 .post(ClientResponse.class, jsonStream);
396 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
397 String location = response.getLocation().getPath();
Ray Milkey8d076402015-08-31 15:43:18 -0700398 assertThat(location, Matchers.startsWith("/intents/myApp/"));
Ray Milkeyb82c42b2015-06-30 09:42:20 -0700399 }
Ray Milkey7b158512015-07-21 16:32:43 -0700400
401 /**
Ray Milkey05b169d2015-08-13 14:33:33 -0700402 * Tests creating an intent with POST and illegal JSON.
403 */
404 @Test
405 public void testBadPost() {
406 replay(mockCoreService);
407 replay(mockIntentService);
408
409 String json = "this is invalid!";
410 WebResource rs = resource();
411
412 ClientResponse response = rs.path("intents")
413 .type(MediaType.APPLICATION_JSON_TYPE)
414 .post(ClientResponse.class, json);
415 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_BAD_REQUEST));
416 }
417
418 /**
Ray Milkey7b158512015-07-21 16:32:43 -0700419 * Tests removing an intent with DELETE.
420 */
421 @Test
422 public void testRemove() {
423 final HashSet<NetworkResource> resources = new HashSet<>();
424 resources.add(new MockResource(1));
425 resources.add(new MockResource(2));
426 resources.add(new MockResource(3));
427 final Intent intent = new MockIntent(3L, resources);
428 final ApplicationId appId = new DefaultApplicationId(2, "app");
429 IntentService fakeManager = new FakeIntentManager();
430
431 expect(mockCoreService.getAppId("app"))
432 .andReturn(appId).once();
433 replay(mockCoreService);
434
435 mockIntentService.withdraw(anyObject());
436 expectLastCall().andDelegateTo(fakeManager).once();
437 expect(mockIntentService.getIntent(Key.of(2, appId)))
438 .andReturn(intent)
439 .once();
440 expect(mockIntentService.getIntent(Key.of("0x2", appId)))
441 .andReturn(null)
442 .once();
443
444 mockIntentService.addListener(anyObject());
445 expectLastCall().andDelegateTo(fakeManager).once();
446 mockIntentService.removeListener(anyObject());
447 expectLastCall().andDelegateTo(fakeManager).once();
448
449 replay(mockIntentService);
450
451 WebResource rs = resource();
452
453 ClientResponse response = rs.path("intents/app/0x2")
454 .type(MediaType.APPLICATION_JSON_TYPE)
455 .delete(ClientResponse.class);
456 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_NO_CONTENT));
457 }
Ray Milkey05b169d2015-08-13 14:33:33 -0700458
459 /**
460 * Tests removal of a non existent intent with DELETE.
461 */
462 @Test
463 public void testBadRemove() {
464 final ApplicationId appId = new DefaultApplicationId(2, "app");
465
466 expect(mockCoreService.getAppId("app"))
467 .andReturn(appId).once();
468 replay(mockCoreService);
469
470 expect(mockIntentService.getIntent(Key.of(2, appId)))
471 .andReturn(null)
472 .once();
473 expect(mockIntentService.getIntent(Key.of("0x2", appId)))
474 .andReturn(null)
475 .once();
476
477 replay(mockIntentService);
478
479 WebResource rs = resource();
480
481 ClientResponse response = rs.path("intents/app/0x2")
482 .type(MediaType.APPLICATION_JSON_TYPE)
483 .delete(ClientResponse.class);
484 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_NO_CONTENT));
485 }
486
Ray Milkey2b217142014-12-15 09:24:24 -0800487}