package com.temenos.interaction.core.hypermedia.expression; /* * #%L * interaction-core * %% * Copyright (C) 2012 - 2013 Temenos Holdings N.V. * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * #L% */ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.UriInfo; import org.junit.Assert; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import com.temenos.interaction.core.command.CommandController; import com.temenos.interaction.core.command.InteractionCommand; import com.temenos.interaction.core.command.InteractionCommand.Result; import com.temenos.interaction.core.command.InteractionContext; import com.temenos.interaction.core.command.InteractionException; import com.temenos.interaction.core.command.NewCommandController; import com.temenos.interaction.core.command.NoopGETCommand; import com.temenos.interaction.core.entity.EntityMetadata; import com.temenos.interaction.core.entity.Metadata; import com.temenos.interaction.core.hypermedia.Action; import com.temenos.interaction.core.hypermedia.Action.TYPE; import com.temenos.interaction.core.hypermedia.ResourceState; import com.temenos.interaction.core.hypermedia.ResourceStateMachine; import com.temenos.interaction.core.hypermedia.Transition; import com.temenos.interaction.core.hypermedia.expression.ResourceGETExpression.Function; import com.temenos.interaction.core.rim.HTTPHypermediaRIM; import com.temenos.interaction.core.web.RequestContext; public class TestResourceGETExpression { @Before public void setup() { // initialise the thread local request context with requestUri and baseUri RequestContext ctx = new RequestContext("/baseuri", "/requesturi", null); RequestContext.setRequestContext(ctx); } private CommandController mockCommandController() { NewCommandController cc = new NewCommandController(); try { InteractionCommand dosomthing = mock(InteractionCommand.class); when(dosomthing.execute(any(InteractionContext.class))).thenThrow(new RuntimeException("Should not execute this command")); InteractionCommand notfound = mock(InteractionCommand.class); when(notfound.execute(any(InteractionContext.class))).thenReturn(Result.FAILURE); InteractionCommand found = new NoopGETCommand(); cc.addCommand("dosomething", dosomthing); cc.addCommand("notfound", notfound); cc.addCommand("found", found); } catch(InteractionException ie) { Assert.fail(ie.getMessage()); } return cc; } private HTTPHypermediaRIM mockRimHandler() { CommandController mockCommandController = mockCommandController(); Metadata mockMetadata = mock(Metadata.class); when(mockMetadata.getEntityMetadata(anyString())).thenReturn(mock(EntityMetadata.class)); return new HTTPHypermediaRIM(mockCommandController, mockRIM(), mockMetadata); } private ResourceStateMachine mockRIM() { String rootResourcePath = "/bookings/{bookingId}"; ResourceState initial = new ResourceState("BOOKING", "initial", new ArrayList<Action>(), rootResourcePath); // room reserved for the booking ResourceState room = new ResourceState(initial, "room", new ArrayList<Action>(), "/room"); // booking cancelled ResourceState cancelled = new ResourceState(initial, "cancelled", new ArrayList<Action>(), "/cancelled", "cancel".split(" ")); // payment information ResourceState paid = new ResourceState(initial, "paid", new ArrayList<Action>(), "/payment", "pay".split(" ")); // waiting for merchant confirmation List<Action> mockNotFound = new ArrayList<Action>(); mockNotFound.add(new Action("notfound", TYPE.VIEW, null, "GET")); ResourceState pwaiting = new ResourceState(paid, "pwaiting", mockNotFound, "/pwaiting", "wait".split(" ")); // merchant confirmed List<Action> mockFound = new ArrayList<Action>(); mockFound.add(new Action("found", TYPE.VIEW)); mockFound.add(new Action("dosomething", TYPE.VIEW, null, "POST")); ResourceState pconfirmed = new ResourceState(paid, "pconfirmed", mockFound, "/pconfirmed", "confirmed".split(" ")); // create transitions that indicate state initial.addTransition(new Transition.Builder().flags(Transition.AUTO).target(room).build()); initial.addTransition(new Transition.Builder().flags(Transition.AUTO).target(cancelled).build()); initial.addTransition(new Transition.Builder().flags(Transition.AUTO).target(paid).build()); // TODO, expressions should also be followed in determining resource state graph initial.addTransition(new Transition.Builder().flags(Transition.AUTO).target(pwaiting).build()); initial.addTransition(new Transition.Builder().flags(Transition.AUTO).target(pconfirmed).build()); // pseudo states that do the processing ResourceState cancel = new ResourceState(cancelled, "psuedo_cancel", new ArrayList<Action>()); ResourceState assignRoom = new ResourceState(room, "psuedo_assignroom", new ArrayList<Action>()); ResourceState paymentDetails = new ResourceState(paid, "psuedo_setcarddetails", new ArrayList<Action>()); Map<String, String> uriLinkageMap = new HashMap<String, String>(); int transitionFlags = 0; // regular transition // create the transitions (links) initial.addTransition(new Transition.Builder().method("POST").target(cancel).build()); initial.addTransition(new Transition.Builder().method("PUT").target(assignRoom).build()); List<Expression> expressions = new ArrayList<Expression>(); expressions.add(new ResourceGETExpression(pconfirmed, Function.NOT_FOUND)); expressions.add(new ResourceGETExpression(pwaiting, Function.NOT_FOUND)); initial.addTransition(new Transition.Builder().method("PUT").target(paymentDetails).uriParameters(uriLinkageMap).flags(transitionFlags).evaluation(new SimpleLogicalExpressionEvaluator(expressions)).label("Make a payment").build()); return new ResourceStateMachine(initial); } @SuppressWarnings("unchecked") @Test public void testLookupResourceThenGET_404() { HTTPHypermediaRIM rimHandler = mockRimHandler(); ResourceStateMachine rsm = rimHandler.getHypermediaEngine(); rsm.setCommandController(mockCommandController()); InteractionContext ctx = new InteractionContext(mock(UriInfo.class), mock(HttpHeaders.class), mock(MultivaluedMap.class), mock(MultivaluedMap.class), rsm.getInitial(), mock(Metadata.class)); ResourceGETExpression rgeOK = new ResourceGETExpression(rsm.getResourceStateByName("pwaiting"), Function.OK); boolean result1 = rgeOK.evaluate(rimHandler, ctx, null); assertFalse("We did a GET on 'pwaiting' and it was NOT_FOUND(404), therefore OK link condition evaluates to 'false'", result1); ResourceGETExpression rgeNOT_FOUND = new ResourceGETExpression(rsm.getResourceStateByName("pwaiting"), Function.NOT_FOUND); boolean result2 = rgeNOT_FOUND.evaluate(rimHandler, ctx, null); assertTrue("We did a GET on 'pwaiting' and it was NOT_FOUND(404), therefore NOT_FOUND link condition evaluates to 'true'", result2); } @SuppressWarnings("unchecked") @Test @Ignore public void testLookupResourceThenGET_200() { HTTPHypermediaRIM rimHandler = mockRimHandler(); ResourceStateMachine rsm = rimHandler.getHypermediaEngine(); InteractionContext ctx = new InteractionContext(mock(UriInfo.class), mock(HttpHeaders.class), mock(MultivaluedMap.class), mock(MultivaluedMap.class), rsm.getInitial(), mock(Metadata.class)); ResourceGETExpression rgeOK = new ResourceGETExpression(rsm.getResourceStateByName("pconfirmed"), Function.OK); boolean result1 = rgeOK.evaluate(rimHandler, ctx, null); assertTrue("We did a GET on 'pconfirmed' and it was OK(200), therefore OK link condition evaluates to 'true'", result1); ResourceGETExpression rgeNOT_FOUND = new ResourceGETExpression(rsm.getResourceStateByName("pconfirmed"), Function.NOT_FOUND); boolean result2 = rgeNOT_FOUND.evaluate(rimHandler, ctx, null); assertFalse("We did a GET on 'pconfirmed' and it was OK(200), therefore NOT_FOUND link condition evaluates to 'false'", result2); } }