package com.temenos.interaction.core.hypermedia;
/*
* #%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.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.junit.Test;
public class TestResourceState {
@Test
public void testRels() {
String ENTITY_NAME = "entity";
String linkRels = "self geospatial";
ResourceState initial = new ResourceState(ENTITY_NAME, "begin", new ArrayList<Action>(), "/", linkRels.split(" "));
assertEquals(2, initial.getRels().length);
assertEquals("self", initial.getRels()[0]);
assertEquals("geospatial", initial.getRels()[1]);
}
@Test
public void testRel() {
String ENTITY_NAME = "entity";
String linkRels = "self geospatial";
ResourceState initial = new ResourceState(ENTITY_NAME, "begin", new ArrayList<Action>(), "/", linkRels.split(" "));
assertEquals("self geospatial", initial.getRel());
}
@Test
public void testDefaultRel() {
String ENTITY_NAME = "entity";
ResourceState initial = new ResourceState(ENTITY_NAME, "begin", new ArrayList<Action>(), "/");
// assertEquals("item self", initial.getRel());
assertEquals("item", initial.getRel());
}
@Test
public void testId() {
String ENTITY_NAME = "entity";
ResourceState initial = new ResourceState(ENTITY_NAME, "begin", new ArrayList<Action>(), "/");
assertEquals("entity.begin", initial.getId());
}
@Test
public void testCollection() {
String ENTITY_NAME = "entity";
ResourceState begin = new ResourceState(ENTITY_NAME, "begin", new ArrayList<Action>(), "/");
ResourceState exists = new ResourceState(ENTITY_NAME, "exists", new ArrayList<Action>(), "{id}");
ResourceState end = new ResourceState(ENTITY_NAME, "end", new ArrayList<Action>(), "/");
Set<ResourceState> states = new HashSet<ResourceState>();
states.add(begin);
states.add(exists);
states.add(end);
Set<ResourceState> states2 = new HashSet<ResourceState>();
states2.add(begin);
states2.add(exists);
states2.add(end);
states.removeAll(states2);
assertEquals(0, states.size());
}
/**
* Each resource state must have a unique path. Test the constructor
* that uses the state name as the path.
*/
@Test
public void testSelfStatePath() {
String ENTITY_NAME = "entity";
ResourceState initial = new ResourceState(ENTITY_NAME, "initial", new ArrayList<Action>(), "/test");
ResourceState exists = new ResourceState(initial, "exists", new ArrayList<Action>(), "/exists");
ResourceState root = new ResourceState(ENTITY_NAME, "root", new ArrayList<Action>(), "/");
ResourceState archived = new ResourceState(ENTITY_NAME, "archived", new ArrayList<Action>(), "/archived");
assertEquals("/test", initial.getPath());
assertEquals("/test/exists", exists.getPath());
assertEquals("/", root.getPath());
assertEquals("/archived", archived.getPath());
}
@Test
public void testGetCommand() {
String ENTITY_NAME = "entity";
ResourceState begin = new ResourceState(ENTITY_NAME, "begin", new ArrayList<Action>(), "{id}");
ResourceState exists = new ResourceState(ENTITY_NAME, "exists", new ArrayList<Action>(), "{id}");
begin.addTransition(new Transition.Builder().method("PUT").target(exists).build());
assertEquals("PUT", begin.getTransition(exists).getCommand().getMethod());
assertEquals("{id}", begin.getTransition(exists).getTarget().getPath());
}
@Test
public void testAutoTransition() {
String ENTITY_NAME = "entity";
ResourceState begin = new ResourceState(ENTITY_NAME, "begin", new ArrayList<Action>(), "{id}");
ResourceState exists = new ResourceState(ENTITY_NAME, "exists", new ArrayList<Action>(), "{id}");
begin.addTransition(new Transition.Builder().target(exists).flags(Transition.AUTO).build());
assertTrue(begin.getTransition(exists).getCommand().isAutoTransition());
}
@Test (expected = IllegalArgumentException.class)
public void testInvalidAutoTransition() {
String ENTITY_NAME = "entity";
ResourceState begin = new ResourceState(ENTITY_NAME, "begin", new ArrayList<Action>(), "{id}");
ResourceState exists = new ResourceState(ENTITY_NAME, "exists", new ArrayList<Action>(), "{id}");
begin.addTransition(new Transition.Builder().method("PUT").target(exists).flags(Transition.AUTO).build());
}
@Test
public void testAddTransitionLinkageMap() {
// define a linkage map (target URI element, source entity element)
Map<String, String> uriLinkageMap = new HashMap<String, String>();
// uri defines a template with {id}, our entity needs to supply {NoteId} as the id
uriLinkageMap.put("id", "NoteId");
String ENTITY_NAME = "entity";
ResourceState begin = new ResourceState("SomeEntity", "initial", new ArrayList<Action>(), "/tests");
ResourceState exists = new ResourceState(ENTITY_NAME, "exists", new ArrayList<Action>(), "/test/{id}");
begin.addTransition(new Transition.Builder().method("PUT").target(exists).uriParameters(uriLinkageMap).build());
assertEquals("/test/{id}", begin.getTransition(exists).getTarget().getPath());
assertTrue(begin.getTransition(exists).getCommand().getUriParameters().containsKey("id"));
}
@Test
public void testAddMultiplTransitionsNoPath() {
ResourceState initial = new ResourceState("ENTITY", "initial", new ArrayList<Action>(), "/");
ResourceState A = new ResourceState("ENTITY", "initial", new ArrayList<Action>(), "/A");
ResourceState B = new ResourceState("ENTITY", "initial", new ArrayList<Action>(), "/B");
initial.addTransition(new Transition.Builder().flags(Transition.AUTO).target(A).build());
initial.addTransition(new Transition.Builder().flags(Transition.AUTO).target(B).build());
assertEquals(2, initial.getAllTargets().size());
}
@Test(expected = AssertionError.class)
public void testResourceStateNoPath() {
new ResourceState("ENTITY", "initial", new ArrayList<Action>(), "");
}
@Test
public void testTransitionToStateMachine() {
String ENTITY_NAME1 = "entity1";
ResourceState initial = new ResourceState(ENTITY_NAME1, "initial", new ArrayList<Action>(), "/test/{id}");
ResourceState exists = new ResourceState(initial, "exists", new ArrayList<Action>());
ResourceState deleted = new ResourceState(initial, "deleted", new ArrayList<Action>());
initial.addTransition(new Transition.Builder().method("PUT").target(exists).build());
exists.addTransition(new Transition.Builder().method("DELETE").target(deleted).build());
String ENTITY_NAME2 = "entity2";
ResourceState initial2 = new ResourceState(ENTITY_NAME2, "initial", new ArrayList<Action>(), "/entity/2");
ResourceState exists2 = new ResourceState(initial2, "exists", new ArrayList<Action>());
ResourceState deleted2 = new ResourceState(initial2, "deleted", new ArrayList<Action>());
initial2.addTransition(new Transition.Builder().method("PUT").target(exists2).build());
exists2.addTransition(new Transition.Builder().method("DELETE").target(deleted2).build());
ResourceStateMachine rsm1 = new ResourceStateMachine(initial);
ResourceStateMachine rsm2 = new ResourceStateMachine(initial2);
exists.addTransition("GET", rsm2);
exists2.addTransition("GET", rsm1);
assertEquals("GET", exists.getTransition(initial2).getCommand().getMethod());
assertEquals("/entity/2", exists.getTransition(initial2).getTarget().getPath());
}
@Test
public void testEquality() {
String ENTITY_NAME = "entity";
ResourceState begin = new ResourceState(ENTITY_NAME, "begin", new ArrayList<Action>(), "/");
ResourceState begin2 = new ResourceState(ENTITY_NAME, "begin", new ArrayList<Action>(), "/");
assertEquals(begin, begin2);
assertEquals(begin.hashCode(), begin2.hashCode());
}
@Test
public void testInequality() {
String ENTITY_NAME = "entity";
ResourceState begin = new ResourceState(ENTITY_NAME, "begin", new ArrayList<Action>(), "/");
ResourceState end = new ResourceState(ENTITY_NAME, "end", new ArrayList<Action>(), "/");
assertFalse(begin.equals(end));
assertFalse(begin.hashCode() == end.hashCode());
}
@Test
public void testEqualityEntity() {
String STATE_NAME = "pseudo";
ResourceState one = new ResourceState("entity1", STATE_NAME, new ArrayList<Action>(), "/");
ResourceState two = new ResourceState("entity1", STATE_NAME, new ArrayList<Action>(), "/");
assertEquals(one, two);
assertEquals(one.hashCode(), two.hashCode());
}
@Test
public void testInequalityEntity() {
String STATE_NAME = "pseudo";
ResourceState one = new ResourceState("entity1", STATE_NAME, new ArrayList<Action>(), "/");
ResourceState two = new ResourceState("entity2", STATE_NAME, new ArrayList<Action>(), "/");
assertFalse(one.equals(two));
assertFalse(one.hashCode() == two.hashCode());
}
@Test
public void testEndState() {
String ENTITY_NAME = "entity";
ResourceState begin = new ResourceState(ENTITY_NAME, "begin", new ArrayList<Action>(), "/");
ResourceState end = new ResourceState(ENTITY_NAME, "end", new ArrayList<Action>(), "/");
begin.addTransition(new Transition.Builder().method("DELETE").target(end).build());
assertFalse(begin.isFinalState());
assertTrue(end.isFinalState());
}
/**
* A pseudo state is a resource state without a resource to represent its state.
* e.g. a deleted state is a state of an entity resource.
*/
@Test
public void testPseudoState() {
ResourceState exists = new ResourceState("entity", "exists", new ArrayList<Action>(), "/exists");
ResourceState deleted = new ResourceState(exists, "deleted", new ArrayList<Action>());
assertTrue(deleted.isPseudoState());
}
/**
* A transient state is a resource state with a single AUTO transition.
*/
@Test
public void testTransientState() {
ResourceState home = new ResourceState("root", "root", new ArrayList<Action>(), "/");
ResourceState reboot = new ResourceState("entity", "reboot", new ArrayList<Action>(), "/reboot");
home.addTransition(new Transition.Builder().method("POST").target(reboot).build());
reboot.addTransition(new Transition.Builder().flags(Transition.REDIRECT).target(home).build());
assertTrue(reboot.isTransientState());
}
/**
* A transient state is a resource state with a single REDIRECT transition, get the
* target state.
*/
@Test
public void testTransientTarget() {
ResourceState home = new ResourceState("root", "root", new ArrayList<Action>(), "/");
ResourceState reboot = new ResourceState("entity", "reboot", new ArrayList<Action>(), "/reboot");
home.addTransition(new Transition.Builder().method("POST").target(reboot).build());
reboot.addTransition(new Transition.Builder().flags(Transition.REDIRECT).target(home).build());
assertEquals(home, reboot.getRedirectTransition().getTarget());
}
@Test
public void testAddMultipleTransitionsToSameState() {
ResourceState airport = new ResourceState("Airport", "airport", new ArrayList<Action>(), "/Airports('{id}')", null, new UriSpecification("airport", "/Airports('{id}')"));
CollectionResourceState flights = new CollectionResourceState("Flight", "Flights", new ArrayList<Action>(), "/Flights()", null, null);
Map<String, String> uriLinkageMap = new HashMap<String, String>();
uriLinkageMap.put("filter", "arrivalAirportCode eq '{code}'");
airport.addTransition(new Transition.Builder().method("GET").target(flights).uriParameters(uriLinkageMap).build());
uriLinkageMap.put("filter", "departureAirportCode eq '{code}'");
airport.addTransition(new Transition.Builder().method("GET").target(flights).uriParameters(uriLinkageMap).build());
airport.addTransition(new Transition.Builder().method("PUT").target(flights).build());
assertEquals(3, airport.getTransitions(flights).size());
List<Transition> transitions = airport.getTransitions(flights);
assertTrue(containsTransitionWithLinkParameter(transitions, "Airport.airport>GET>Flight.Flights", "filter", "departureAirportCode eq '{code}'"));
assertTrue(containsTransitionWithLinkParameter(transitions, "Airport.airport>GET>Flight.Flights", "filter", "arrivalAirportCode eq '{code}'"));
assertTrue(containsTransition(transitions, "Airport.airport>PUT>Flight.Flights", null));
}
@Test
public void testLinkProperty() {
ResourceState airport = new ResourceState("Airport", "airport", new ArrayList<Action>(), "/Airports('{id}')", null, new UriSpecification("airport", "/Airports('{id}')"));
CollectionResourceState flights = new CollectionResourceState("Flight", "Flights", new ArrayList<Action>(), "/Flights()", null, null);
Map<String, String> uriLinkageMap = new HashMap<String, String>();
uriLinkageMap.put("filter", "arrivalAirportCode eq '{code}'");
airport.addTransition(new Transition.Builder().method("GET").target(flights).uriParameters(uriLinkageMap).build());
uriLinkageMap.put("filter", "departureAirportCode eq '{code}'");
airport.addTransition(new Transition.Builder().method("GET").target(flights).uriParameters(uriLinkageMap).build());
assertEquals(2, airport.getTransitions(flights).size());
List<Transition> transitions = airport.getTransitions(flights);
assertTrue(containsTransitionWithLinkParameter(transitions, "Airport.airport>GET>Flight.Flights", "filter", "departureAirportCode eq '{code}'"));
assertTrue(containsTransitionWithLinkParameter(transitions, "Airport.airport>GET>Flight.Flights", "filter", "arrivalAirportCode eq '{code}'"));
}
@Test
public void testReplaceLinkPropertyTemplates() {
ResourceState airport = new ResourceState("Airport", "airport", new ArrayList<Action>(), "/Airports('{id}')", null, new UriSpecification("airport", "/Airports('{id}')"));
CollectionResourceState flights = new CollectionResourceState("Flight", "Flights", new ArrayList<Action>(), "/Airports('{id}')/Flights", null, null);
Map<String, String> uriLinkageMap = new HashMap<String, String>();
uriLinkageMap.put("filter", "arrivalAirportCode eq '{code}'");
uriLinkageMap.put("id", "{code}");
airport.addTransition(new Transition.Builder().method("GET").target(flights).uriParameters(uriLinkageMap).build());
assertEquals(1, airport.getTransitions(flights).size());
List<Transition> transitions = airport.getTransitions(flights);
assertTrue(containsTransitionWithLinkParameter(transitions, "Airport.airport>GET>Flight.Flights", "filter", "arrivalAirportCode eq '{code}'"));
}
@Test
public void testReplaceMultipleLinkPropertyTemplates() {
ResourceState airport = new ResourceState("Airport", "airport", new ArrayList<Action>(), "/Airports('{id}')", null, new UriSpecification("airport", "/Airports('{id}')"));
CollectionResourceState flights = new CollectionResourceState("Flight", "Flights", new ArrayList<Action>(), "/Flights()", null, null);
Map<String, String> uriLinkageMap = new HashMap<String, String>();
uriLinkageMap.put("filter", "arrivalAirportCode eq '{code}'");
uriLinkageMap.put("id", "{code}");
airport.addTransition(new Transition.Builder().method("GET").target(flights).uriParameters(uriLinkageMap).build());
uriLinkageMap.put("filter", "departureAirportCode eq '{code}'");
uriLinkageMap.put("id", "{code}");
airport.addTransition(new Transition.Builder().method("GET").target(flights).uriParameters(uriLinkageMap).build());
assertEquals(2, airport.getTransitions(flights).size());
List<Transition> transitions = airport.getTransitions(flights);
assertTrue(containsTransitionWithLinkParameter(transitions, "Airport.airport>GET>Flight.Flights", "filter", "departureAirportCode eq '{code}'"));
assertTrue(containsTransitionWithLinkParameter(transitions, "Airport.airport>GET>Flight.Flights", "filter", "arrivalAirportCode eq '{code}'"));
}
@Test
public void testActionParameters() {
ResourceState airport = new ResourceState("Airport", "airport", new ArrayList<Action>(), "/Airports('{id}')", null, new UriSpecification("airport", "/Airports('{id}')"));
List<Action> flightsActions = new ArrayList<Action>();
Properties actionViewProperties = new Properties();
actionViewProperties.put("entity", "Customer");
actionViewProperties.put("filter", "myfilter");
flightsActions.add(new Action("GetMyEntities", Action.TYPE.VIEW, actionViewProperties));
CollectionResourceState flights = new CollectionResourceState("Flight", "Flights", flightsActions, "/Flights()", null, null);
Map<String, String> uriLinkageMap = new HashMap<String, String>();
uriLinkageMap.put("myfilter", "arrivalAirportCode eq '{code}'");
uriLinkageMap.put("id", "{code}");
airport.addTransition(new Transition.Builder().method("GET").target(flights).uriParameters(uriLinkageMap).build());
List<Action> actions = flights.getActions();
assertEquals(1, actions.size());
assertEquals("GetMyEntities", actions.get(0).getName());
Properties props = actions.get(0).getProperties();
assertEquals(2, props.size());
assertEquals("Customer", props.get("entity"));
assertTrue(props.get("filter") instanceof ActionPropertyReference);
assertEquals("myfilter", ((ActionPropertyReference) props.get("filter")).getKey());
assertEquals("arrivalAirportCode eq '{code}'", ((ActionPropertyReference) props.get("filter")).getProperty("__code"));
}
@Test
public void testActionPropertiesReferecingMultipleParameters() {
ResourceState airport = new ResourceState("Airport", "airport", new ArrayList<Action>(), "/Airports('{id}')", null, new UriSpecification("airport", "/Airports('{id}')"));
List<Action> flightsActions = new ArrayList<Action>();
Properties actionViewProperties = new Properties();
actionViewProperties.put("entity", "Customer");
actionViewProperties.put("filter", "myfilter");
flightsActions.add(new Action("GetMyEntities", Action.TYPE.VIEW, actionViewProperties));
CollectionResourceState flights = new CollectionResourceState("Flight", "Flights", flightsActions, "/Flights()", null, null);
Map<String, String> uriLinkageMap = new HashMap<String, String>();
uriLinkageMap.put("myfilter", "arrivalAirportCode eq '{arrivalAirportCode}'");
airport.addTransition(new Transition.Builder().method("GET").target(flights).uriParameters(uriLinkageMap).build());
uriLinkageMap.put("myfilter", "departureAirportCode eq '{departureAirportCode}'");
airport.addTransition(new Transition.Builder().method("GET").target(flights).uriParameters(uriLinkageMap).build());
List<Action> actions = flights.getActions();
assertEquals(1, actions.size());
assertEquals("GetMyEntities", actions.get(0).getName());
Properties props = actions.get(0).getProperties();
assertEquals(2, props.size());
assertEquals("Customer", props.get("entity"));
assertTrue(props.get("filter") instanceof ActionPropertyReference);
assertEquals("myfilter", ((ActionPropertyReference) props.get("filter")).getKey());
assertEquals("arrivalAirportCode eq '{arrivalAirportCode}'", ((ActionPropertyReference) props.get("filter")).getProperty("__arrivalAirportCode"));
assertEquals("departureAirportCode eq '{departureAirportCode}'", ((ActionPropertyReference) props.get("filter")).getProperty("__departureAirportCode"));
}
private boolean containsTransition(List<Transition> transitions, String id, String label) {
for(Transition t : transitions) {
if(t.getId().equals(id) && (t.getLabel() == null && label == null || t.getLabel().equals(label))) {
return true;
}
}
System.out.println("NOT FOUND id: ["+id+"], label: ["+label+"]");
for(Transition t : transitions) {
System.out.println("id: ["+t.getId()+"], label: ["+t.getLabel()+"]");
}
return false;
}
private boolean containsTransitionWithLinkParameter(List<Transition> transitions, String id, String linkParam, String expectedLinkParamValue) {
for(Transition t : transitions) {
Map<String, String> linkParameters = t.getCommand().getUriParameters();
if(t.getId().equals(id) && linkParameters.get(linkParam).equals(expectedLinkParamValue)) {
return true;
}
}
System.out.println("NOT FOUND id: ["+id+"], linkParam: ["+linkParam+"], expectedLinkParamValue: ["+expectedLinkParamValue+"]");
for(Transition t : transitions) {
Map<String, String> linkParameters = t.getCommand().getUriParameters();
System.out.println("id: ["+t.getId()+"], linkParam: ["+linkParam+"], expectedLinkParamValue: ["+linkParameters.get(linkParam)+"]");
}
return false;
}
}