http://git-wip-us.apache.org/repos/asf/isis/blob/a4ec0b72/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/pom.xml ---------------------------------------------------------------------- diff --git a/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/pom.xml b/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/pom.xml new file mode 100644 index 0000000..1ec97ae --- /dev/null +++ b/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/pom.xml @@ -0,0 +1,122 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +--><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>${groupId}</groupId> + <artifactId>${rootArtifactId}</artifactId> + <version>${version}</version> + </parent> + + <artifactId>${artifactId}</artifactId> + <name>ToDo App Integration Tests</name> + + <build> + <testResources> + <testResource> + <directory>src/test/resources</directory> + </testResource> + <testResource> + <directory>src/test/java</directory> + <includes> + <include>**</include> + </includes> + <excludes> + <exclude>**/*.java</exclude> + </excludes> + </testResource> + </testResources> + </build> + <dependencies> + + <!-- other modules in this project --> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>${rootArtifactId}-fixture</artifactId> + </dependency> + + <dependency> + <groupId>org.apache.isis.core</groupId> + <artifactId>isis-core-unittestsupport</artifactId> + </dependency> + + <dependency> + <groupId>org.apache.isis.core</groupId> + <artifactId>isis-core-integtestsupport</artifactId> + </dependency> + <dependency> + <groupId>org.apache.isis.core</groupId> + <artifactId>isis-core-specsupport</artifactId> + </dependency> + + <dependency> + <groupId>org.hamcrest</groupId> + <artifactId>hamcrest-library</artifactId> + </dependency> + + <dependency> + <groupId>org.apache.isis.core</groupId> + <artifactId>isis-core-wrapper</artifactId> + </dependency> + <dependency> + <groupId>org.apache.isis.core</groupId> + <artifactId>isis-core-runtime</artifactId> + </dependency> + + <dependency> + <groupId>org.hsqldb</groupId> + <artifactId>hsqldb</artifactId> + </dependency> + + <!-- + uncomment to enable enhanced cucumber-jvm reporting + http://www.masterthought.net/section/cucumber-reporting + <dependency> + <groupId>com.googlecode.totallylazy</groupId> + <artifactId>totallylazy</artifactId> + <version>991</version> + </dependency> + + <dependency> + <groupId>net.masterthought</groupId> + <artifactId>cucumber-reporting</artifactId> + <version>0.0.21</version> + </dependency> + <dependency> + <groupId>net.masterthought</groupId> + <artifactId>maven-cucumber-reporting</artifactId> + <version>0.0.4</version> + </dependency> + --> + </dependencies> + + <!-- + uncomment for enhanced cucumber-jvm reporting + http://www.masterthought.net/section/cucumber-reporting + <repositories> + <repository> + <id>repo.bodar.com</id> + <url>http://repo.bodar.com</url> + </repository> + </repositories> + --> + + +</project>
http://git-wip-us.apache.org/repos/asf/isis/blob/a4ec0b72/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/ToDoAppSystemInitializer.java ---------------------------------------------------------------------- diff --git a/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/ToDoAppSystemInitializer.java b/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/ToDoAppSystemInitializer.java new file mode 100644 index 0000000..9e0e408 --- /dev/null +++ b/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/ToDoAppSystemInitializer.java @@ -0,0 +1,80 @@ +#set( $symbol_pound = '#' ) +#set( $symbol_dollar = '$' ) +#set( $symbol_escape = '\' ) +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package integration; + +import org.apache.isis.core.commons.config.IsisConfiguration; +import org.apache.isis.core.integtestsupport.IsisSystemForTest; +import org.apache.isis.core.runtime.persistence.PersistenceConstants; +import org.apache.isis.objectstore.jdo.datanucleus.DataNucleusPersistenceMechanismInstaller; +import org.apache.isis.objectstore.jdo.datanucleus.IsisConfigurationForJdoIntegTests; + +/** + * Holds an instance of an {@link IsisSystemForTest} as a {@link ThreadLocal} on the current thread, + * initialized with ToDo app's domain services. + */ +public class ToDoAppSystemInitializer { + + private ToDoAppSystemInitializer(){} + + public static IsisSystemForTest initIsft() { + IsisSystemForTest isft = IsisSystemForTest.getElseNull(); + if(isft == null) { + isft = new ToDoSystemBuilder().build().setUpSystem(); + IsisSystemForTest.set(isft); + } + return isft; + } + + private static class ToDoSystemBuilder extends IsisSystemForTest.Builder { + + public ToDoSystemBuilder() { + withLoggingAt(org.apache.log4j.Level.INFO); + with(testConfiguration()); + with(new DataNucleusPersistenceMechanismInstaller()); + + // services annotated with @DomainService + withServicesIn("app" + ,"dom.todo" + ,"fixture.todo" + ,"webapp.admin" + ,"webapp.prototyping" + ,"org.apache.isis.core.wrapper" + ,"org.apache.isis.applib" + ,"org.apache.isis.core.metamodel.services" + ,"org.apache.isis.core.runtime.services" + ,"org.apache.isis.objectstore.jdo.datanucleus.service.support" // IsisJdoSupportImpl + ,"org.apache.isis.objectstore.jdo.datanucleus.service.eventbus" // EventBusServiceJdo + ); + } + + private static IsisConfiguration testConfiguration() { + final IsisConfigurationForJdoIntegTests testConfiguration = new IsisConfigurationForJdoIntegTests(); + testConfiguration.addRegisterEntitiesPackagePrefix("dom"); + + // enable stricter checking + // + // the consequence of this is having to call 'nextTransaction()' between most of the given/when/then's + // because the command2 only ever refers to the event of the originating action. + testConfiguration.put(PersistenceConstants.ENFORCE_SAFE_SEMANTICS, "true"); + + return testConfiguration; + } + } +} http://git-wip-us.apache.org/repos/asf/isis/blob/a4ec0b72/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/glue/BootstrappingGlue.java ---------------------------------------------------------------------- diff --git a/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/glue/BootstrappingGlue.java b/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/glue/BootstrappingGlue.java new file mode 100644 index 0000000..250c0d5 --- /dev/null +++ b/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/glue/BootstrappingGlue.java @@ -0,0 +1,56 @@ +#set( $symbol_pound = '#' ) +#set( $symbol_dollar = '$' ) +#set( $symbol_escape = '\' ) +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package integration.glue; + +import cucumber.api.java.After; +import cucumber.api.java.Before; +import integration.ToDoAppSystemInitializer; + +import org.apache.isis.core.specsupport.scenarios.ScenarioExecutionScope; +import org.apache.isis.core.specsupport.specs.CukeGlueAbstract; + +public class BootstrappingGlue extends CukeGlueAbstract { + + // ////////////////////////////////////// + + @Before(value={"@unit"}, order=100) + public void beforeScenarioUnitScope() { + before(ScenarioExecutionScope.UNIT); + } + + @Before(value={"@integration"}, order=100) + public void beforeScenarioIntegrationScope() { + org.apache.log4j.PropertyConfigurator.configure("logging.properties"); + ToDoAppSystemInitializer.initIsft(); + + before(ScenarioExecutionScope.INTEGRATION); + } + + @After + public void afterScenario(cucumber.api.Scenario sc) { + assertMocksSatisfied(); + after(sc); + } + + // ////////////////////////////////////// + + + +} http://git-wip-us.apache.org/repos/asf/isis/blob/a4ec0b72/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/glue/CatalogOfFixturesGlue.java ---------------------------------------------------------------------- diff --git a/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/glue/CatalogOfFixturesGlue.java b/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/glue/CatalogOfFixturesGlue.java new file mode 100644 index 0000000..ca6d940 --- /dev/null +++ b/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/glue/CatalogOfFixturesGlue.java @@ -0,0 +1,50 @@ +#set( $symbol_pound = '#' ) +#set( $symbol_dollar = '$' ) +#set( $symbol_escape = '\' ) +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package integration.glue; + +import cucumber.api.java.Before; +import dom.todo.ToDoItem; +import fixture.todo.integtests.ToDoItemsIntegTestFixture; + +import org.apache.isis.core.specsupport.scenarios.InMemoryDB; +import org.apache.isis.core.specsupport.specs.CukeGlueAbstract; + +public class CatalogOfFixturesGlue extends CukeGlueAbstract { + + + @Before(value={"@unit", "@ToDoItemsFixture"}, order=20000) + public void unitFixtures() throws Throwable { + final InMemoryDB inMemoryDB = new InMemoryDBForToDoApp(this.scenarioExecution()); + inMemoryDB.getElseCreate(ToDoItem.class, "Write blog post"); + inMemoryDB.getElseCreate(ToDoItem.class, "Pick up bread"); + final ToDoItem t3 = inMemoryDB.getElseCreate(ToDoItem.class, "Pick up butter"); + t3.setComplete(true); + putVar("isis", "in-memory-db", inMemoryDB); + } + + // ////////////////////////////////////// + + @Before(value={"@integration", "@ToDoItemsFixture"}, order=20000) + public void integrationFixtures() throws Throwable { + scenarioExecution().install(new ToDoItemsIntegTestFixture()); + } + + +} http://git-wip-us.apache.org/repos/asf/isis/blob/a4ec0b72/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/glue/InMemoryDBForToDoApp.java ---------------------------------------------------------------------- diff --git a/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/glue/InMemoryDBForToDoApp.java b/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/glue/InMemoryDBForToDoApp.java new file mode 100644 index 0000000..4d8af87 --- /dev/null +++ b/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/glue/InMemoryDBForToDoApp.java @@ -0,0 +1,43 @@ +#set( $symbol_pound = '#' ) +#set( $symbol_dollar = '$' ) +#set( $symbol_escape = '\' ) +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package integration.glue; + +import dom.todo.ToDoItem; + +import org.apache.isis.core.specsupport.scenarios.InMemoryDB; +import org.apache.isis.core.specsupport.scenarios.ScenarioExecution; + +public class InMemoryDBForToDoApp extends InMemoryDB { + + public InMemoryDBForToDoApp(ScenarioExecution scenarioExecution) { + super(scenarioExecution); + } + + /** + * Hook to initialize if possible. + */ + @Override + protected void init(Object obj, String str) { + if(obj instanceof ToDoItem) { + ToDoItem toDoItem = (ToDoItem) obj; + toDoItem.setDescription(str); + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/isis/blob/a4ec0b72/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/glue/todoitem/ToDoItemGlue.java ---------------------------------------------------------------------- diff --git a/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/glue/todoitem/ToDoItemGlue.java b/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/glue/todoitem/ToDoItemGlue.java new file mode 100644 index 0000000..3db7fa1 --- /dev/null +++ b/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/glue/todoitem/ToDoItemGlue.java @@ -0,0 +1,168 @@ +#set( $symbol_pound = '#' ) +#set( $symbol_dollar = '$' ) +#set( $symbol_escape = '\' ) +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package integration.glue.todoitem; + +import cucumber.api.java.en.Given; +import cucumber.api.java.en.Then; +import cucumber.api.java.en.When; +import dom.todo.ToDoItem; +import dom.todo.ToDoItems; + +import java.util.List; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import org.jmock.Expectations; +import org.junit.Assert; +import org.apache.isis.applib.services.actinvoc.ActionInvocationContext; +import org.apache.isis.applib.services.eventbus.EventBusService; +import org.apache.isis.core.specsupport.scenarios.InMemoryDB; +import org.apache.isis.core.specsupport.specs.CukeGlueAbstract; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +public class ToDoItemGlue extends CukeGlueAbstract { + + @Given("^there are a number of incomplete ToDo items${symbol_dollar}") + public void there_are_a_number_of_incomplete_ToDo_items() throws Throwable { + if(supportsMocks()) { + checking(new Expectations() { + { + allowing(service(ToDoItems.class)).notYetComplete(); + will(returnValue(notYetCompleteItems())); + } + }); + } + try { + final List<ToDoItem> notYetComplete = service(ToDoItems.class).notYetComplete(); + assertThat(notYetComplete.isEmpty(), is(false)); + putVar("list", "notYetCompleteItems", notYetComplete); + + } finally { + assertMocksSatisfied(); + } + } + + @When("^I choose the first of the incomplete items${symbol_dollar}") + public void I_choose_the_first_one() throws Throwable { + @SuppressWarnings("unchecked") + List<ToDoItem> notYetComplete = getVar(null, "notYetCompleteItems", List.class); + assertThat(notYetComplete.isEmpty(), is(false)); + + putVar("todo", "toDoItem", notYetComplete.get(0)); + } + + @When("^mark the item as complete${symbol_dollar}") + public void mark_it_as_complete() throws Throwable { + final ToDoItem toDoItem = getVar(null, "toDoItem", ToDoItem.class); + if(supportsMocks()) { + final ActionInvocationContext actionInvocationContext = service(ActionInvocationContext.class); + final EventBusService eventBusService = service(EventBusService.class); + checking(new Expectations() { + { + allowing(actionInvocationContext); + allowing(eventBusService); + } + }); + toDoItem.actionInvocationContext = actionInvocationContext; + toDoItem.eventBusService = eventBusService; + } + wrap(toDoItem).completed(); + } + + @Then("^the item is no longer listed as incomplete${symbol_dollar}") + public void the_item_is_no_longer_listed_as_incomplete() throws Throwable { + ToDoItem toDoItem = getVar(null, "toDoItem", ToDoItem.class); + whetherNotYetCompletedContains(toDoItem, false); + } + + @Given("^.*completed .*item${symbol_dollar}") + public void a_completed_ToDo_item() throws Throwable { + if(supportsMocks()) { + checking(new Expectations(){{ + allowing(service(ToDoItems.class)).allToDos(); + will(returnValue(findItems(Predicates.<ToDoItem>alwaysTrue()) )); + }}); + } + try { + final List<ToDoItem> allToDos = service(ToDoItems.class).allToDos(); + for (ToDoItem toDoItem : allToDos) { + if(toDoItem.isComplete()) { + putVar("todo", "toDoItem", toDoItem); + return; + } + } + Assert.fail("could not locate any completed ToDo items"); + } finally { + assertMocksSatisfied(); + } + } + + @When("^I mark the .*item as not yet complete${symbol_dollar}") + public void I_mark_it_as_not_yet_complete() throws Throwable { + ToDoItem toDoItem = getVar(null, "toDoItem", ToDoItem.class); + assertThat(toDoItem.isComplete(), is(true)); + + toDoItem.setComplete(false); + } + + @Then("^the .*item is listed as incomplete${symbol_dollar}") + public void the_item_is_listed_as_incomplete() throws Throwable { + ToDoItem toDoItem = getVar(null, "toDoItem", ToDoItem.class); + whetherNotYetCompletedContains(toDoItem, true); + } + + private void whetherNotYetCompletedContains(ToDoItem toDoItem, final boolean whetherContained) { + if(supportsMocks()) { + final List<ToDoItem> notYetCompleteItems = notYetCompleteItems(); + checking(new Expectations() { + { + oneOf(service(ToDoItems.class)).notYetComplete(); + will(returnValue(notYetCompleteItems)); + } + }); + } + try { + final List<ToDoItem> notYetComplete = service(ToDoItems.class).notYetComplete(); + assertThat(notYetComplete.contains(toDoItem), is(whetherContained)); + } finally { + assertMocksSatisfied(); + } + } + + + // helper + private List<ToDoItem> notYetCompleteItems() { + return findItems(new Predicate<ToDoItem>(){ + @Override + public boolean apply(ToDoItem input) { + return !input.isComplete(); + } + }); + } + + private List<ToDoItem> findItems(final Predicate<ToDoItem> predicate) { + final InMemoryDB inMemoryDB = getVar("isis", "in-memory-db", InMemoryDB.class); + final List<ToDoItem> items = inMemoryDB.findAll(ToDoItem.class); + return Lists.newArrayList(Iterables.filter(items, predicate)); + } +} http://git-wip-us.apache.org/repos/asf/isis/blob/a4ec0b72/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/specs/todoitem/RunSpecs.java ---------------------------------------------------------------------- diff --git a/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/specs/todoitem/RunSpecs.java b/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/specs/todoitem/RunSpecs.java new file mode 100644 index 0000000..a0d30e2 --- /dev/null +++ b/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/specs/todoitem/RunSpecs.java @@ -0,0 +1,41 @@ +#set( $symbol_pound = '#' ) +#set( $symbol_dollar = '$' ) +#set( $symbol_escape = '\' ) +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package integration.specs.todoitem; + +import cucumber.api.junit.Cucumber; + +import org.junit.runner.RunWith; + + +/** + * Runs scenarios in all <tt>.feature</tt> files (this package and any subpackages). + */ +@RunWith(Cucumber.class) [email protected]( + format = { + "html:target/cucumber-html-report" + ,"json:target/cucumber.json" + }, + glue={"classpath:integration.glue"}, + strict = true, + tags = { "~@backlog", "~@ignore" }) +public class RunSpecs { + // intentionally empty +} http://git-wip-us.apache.org/repos/asf/isis/blob/a4ec0b72/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/specs/todoitem/ToDoItemSpec_findAndComplete.feature ---------------------------------------------------------------------- diff --git a/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/specs/todoitem/ToDoItemSpec_findAndComplete.feature b/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/specs/todoitem/ToDoItemSpec_findAndComplete.feature new file mode 100644 index 0000000..9fc1596 --- /dev/null +++ b/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/specs/todoitem/ToDoItemSpec_findAndComplete.feature @@ -0,0 +1,39 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +@ToDoItemsFixture +Feature: Find And Complete ToDo Items + + # the scenario is listed twice here just to demonstrate that it + # can be run either at @unit-level scope (using mocks) or + # at @integration-level scope (against the running system). + + @unit + Scenario: Todo items once completed are no longer listed + Given there are a number of incomplete ToDo items + When I choose the first of the incomplete items + And mark the item as complete + Then the item is no longer listed as incomplete + + + @integration + Scenario: Todo items once completed are no longer listed + Given there are a number of incomplete ToDo items + When I choose the first of the incomplete items + And mark the item as complete + Then the item is no longer listed as incomplete + + \ No newline at end of file http://git-wip-us.apache.org/repos/asf/isis/blob/a4ec0b72/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/specs/todoitem/ToDoItemSpec_findCompletedAndMarkAsNotYetComplete.feature ---------------------------------------------------------------------- diff --git a/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/specs/todoitem/ToDoItemSpec_findCompletedAndMarkAsNotYetComplete.feature b/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/specs/todoitem/ToDoItemSpec_findCompletedAndMarkAsNotYetComplete.feature new file mode 100644 index 0000000..1f5f73e --- /dev/null +++ b/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/specs/todoitem/ToDoItemSpec_findCompletedAndMarkAsNotYetComplete.feature @@ -0,0 +1,35 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +@ToDoItemsFixture +Feature: Find completed ToDoItem and mark as not yet complete + + # the scenario is listed twice here just to demonstrate that it + # can be run either at @unit-level scope (using mocks) or + # at @integration-level scope (against the running system). + + @integration + Scenario: Todo items can be uncompleted + Given a completed item + When I mark the item as not yet complete + Then the item is listed as incomplete + + + @unit + Scenario: Todo items can be uncompleted + Given a completed ToDo item + When I mark the item as not yet complete + Then the item is listed as incomplete http://git-wip-us.apache.org/repos/asf/isis/blob/a4ec0b72/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/tests/AbstractToDoIntegTest.java ---------------------------------------------------------------------- diff --git a/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/tests/AbstractToDoIntegTest.java b/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/tests/AbstractToDoIntegTest.java new file mode 100644 index 0000000..6776992 --- /dev/null +++ b/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/tests/AbstractToDoIntegTest.java @@ -0,0 +1,41 @@ +#set( $symbol_pound = '#' ) +#set( $symbol_dollar = '$' ) +#set( $symbol_escape = '\' ) +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package integration.tests; + +import integration.ToDoAppSystemInitializer; + +import org.junit.BeforeClass; +import org.apache.isis.core.integtestsupport.IntegrationTestAbstract; +import org.apache.isis.core.integtestsupport.scenarios.ScenarioExecutionForIntegration; + +public abstract class AbstractToDoIntegTest extends IntegrationTestAbstract { + + @BeforeClass + public static void initClass() { + org.apache.log4j.PropertyConfigurator.configure("logging.properties"); + ToDoAppSystemInitializer.initIsft(); + + // instantiating will install onto ThreadLocal + new ScenarioExecutionForIntegration(); + } + +} http://git-wip-us.apache.org/repos/asf/isis/blob/a4ec0b72/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/tests/ToDoItemContributionsIntegTest.java ---------------------------------------------------------------------- diff --git a/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/tests/ToDoItemContributionsIntegTest.java b/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/tests/ToDoItemContributionsIntegTest.java new file mode 100644 index 0000000..7440088 --- /dev/null +++ b/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/tests/ToDoItemContributionsIntegTest.java @@ -0,0 +1,161 @@ +#set( $symbol_pound = '#' ) +#set( $symbol_dollar = '$' ) +#set( $symbol_escape = '\' ) +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package integration.tests; + +import dom.todo.ToDoItem; +import dom.todo.ToDoItemContributions; +import dom.todo.ToDoItems; +import fixture.todo.scenarios.ToDoItemsRecreateAndCompleteSeveral; + +import java.util.List; +import javax.inject.Inject; +import org.junit.Before; +import org.junit.Test; +import org.apache.isis.applib.fixturescripts.FixtureScripts; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.junit.Assert.assertThat; + +public abstract class ToDoItemContributionsIntegTest extends AbstractToDoIntegTest { + + ToDoItemsRecreateAndCompleteSeveral fixtureScript; + + @Before + public void setUpData() throws Exception { + fixtureScript = new ToDoItemsRecreateAndCompleteSeveral(); + fixtureScripts.runFixtureScript(fixtureScript, null); + } + + @Inject + FixtureScripts fixtureScripts; + @Inject + ToDoItems toDoItems; + @Inject + ToDoItemContributions toDoItemContributions; + + ToDoItemContributions toDoItemContributionsWrapped; + ToDoItem toDoItem; + + @Before + public void setUp() throws Exception { + + toDoItem = wrap(fixtureScript.lookup("to-do-items-recreate-and-complete-several/to-do-item-complete-for-buy-stamps/item-1", ToDoItem.class)); + assertThat(toDoItem, is(not(nullValue()))); + + toDoItemContributionsWrapped = wrap(toDoItemContributions); + } + + public static class Actions { + public static class UpdateCategory extends ToDoItemContributionsIntegTest { + + @Test + public void happyCase() throws Exception { + + // when + toDoItemContributionsWrapped.updateCategory(toDoItem, ToDoItem.Category.Professional, ToDoItem.Subcategory.Consulting); + + // then + assertThat(toDoItem.getCategory(), is(ToDoItem.Category.Professional)); + assertThat(toDoItem.getSubcategory(), is(ToDoItem.Subcategory.Consulting)); + + // when + toDoItemContributionsWrapped.updateCategory(toDoItem, ToDoItem.Category.Domestic, ToDoItem.Subcategory.Chores); + + // then + assertThat(toDoItem.getCategory(), is(ToDoItem.Category.Domestic)); + assertThat(toDoItem.getSubcategory(), is(ToDoItem.Subcategory.Chores)); + } + + + @Test + public void categoryCannotBeNull() throws Exception { + + // when, then + expectedExceptions.expectMessage("'Category' is mandatory"); + toDoItemContributionsWrapped.updateCategory(toDoItem, null, ToDoItem.Subcategory.Chores); + } + + @Test + public void subcategoryCanBeNull() throws Exception { + + // when, then + toDoItemContributionsWrapped.updateCategory(toDoItem, ToDoItem.Category.Professional, null); + } + + @Test + public void subcategoryMustBelongToCategory() throws Exception { + + // when, then + expectedExceptions.expectMessage(containsString("Invalid subcategory")); + toDoItemContributionsWrapped.updateCategory(toDoItem, ToDoItem.Category.Professional, ToDoItem.Subcategory.Chores); + } + } + + public static class SimilarTo extends ToDoItemContributionsIntegTest { + + @Test + public void happyCase() throws Exception { + + // when + List<ToDoItem> similarItems = toDoItemContributionsWrapped.similarTo(toDoItem); + + // then + assertThat(similarItems.size(), is(6)); + } + + } + } + + public static class Properties { + public static class Priority extends ToDoItemContributionsIntegTest { + + private List<ToDoItem> notYetComplete; + + @Before + public void setUp() throws Exception { + notYetComplete = wrap(toDoItems).notYetComplete(); + } + + @Test + public void happyCase() throws Exception { + assertPriority(0, 1); + assertPriority(1, 2); + assertPriority(2, 4); + assertPriority(3, 6); + assertPriority(4, 5); + assertPriority(5, 7); + assertPriority(6, 9); + assertPriority(7, 8); + assertPriority(8, 3); + assertPriority(9, 10); + } + + private void assertPriority(final int n, final int priority) { + assertThat(toDoItemContributions.relativePriority(notYetComplete.get(n)), is(Integer.valueOf(priority))); + } + } + } + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/isis/blob/a4ec0b72/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/tests/ToDoItemIntegTest.java ---------------------------------------------------------------------- diff --git a/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/tests/ToDoItemIntegTest.java b/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/tests/ToDoItemIntegTest.java new file mode 100644 index 0000000..84d3d16 --- /dev/null +++ b/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/tests/ToDoItemIntegTest.java @@ -0,0 +1,1060 @@ +#set( $symbol_pound = '#' ) +#set( $symbol_dollar = '$' ) +#set( $symbol_escape = '\' ) +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package integration.tests; + +import dom.todo.ToDoItem; +import dom.todo.ToDoItemSubscriptions; +import dom.todo.ToDoItems; +import fixture.todo.scenarios.ToDoItemsRecreateAndCompleteSeveral; + +import java.math.BigDecimal; +import java.nio.charset.Charset; +import java.util.EventObject; +import java.util.List; +import javax.activation.MimeType; +import javax.inject.Inject; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeMatcher; +import org.jmock.Expectations; +import org.jmock.Sequence; +import org.jmock.auto.Mock; +import org.joda.time.LocalDate; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.apache.isis.applib.NonRecoverableException; +import org.apache.isis.applib.RecoverableException; +import org.apache.isis.applib.clock.Clock; +import org.apache.isis.applib.fixturescripts.FixtureScripts; +import org.apache.isis.applib.services.clock.ClockService; +import org.apache.isis.applib.services.eventbus.AbstractInteractionEvent; +import org.apache.isis.applib.services.eventbus.ActionDomainEvent; +import org.apache.isis.applib.services.eventbus.CollectionDomainEvent; +import org.apache.isis.applib.services.eventbus.EventBusService; +import org.apache.isis.applib.services.eventbus.PropertyDomainEvent; +import org.apache.isis.applib.value.Blob; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.junit.Assert.assertThat; + +public class ToDoItemIntegTest extends AbstractToDoIntegTest { + + ToDoItemsRecreateAndCompleteSeveral fixtureScript; + + @Before + public void setUpData() throws Exception { + fixtureScript = new ToDoItemsRecreateAndCompleteSeveral(); + fixtureScripts.runFixtureScript(fixtureScript, null); + } + + @Inject + FixtureScripts fixtureScripts; + @Inject + ToDoItems toDoItems; + @Inject + ToDoItemSubscriptions toDoItemSubscriptions; + + ToDoItem toDoItem; + + @Before + public void setUp() throws Exception { + final List<ToDoItem> all = toDoItems.notYetComplete(); + toDoItem = wrap(all.get(0)); + } + + @After + public void tearDown() throws Exception { + toDoItemSubscriptions.reset(); + } + + + public static class Title extends ToDoItemIntegTest { + + private LocalDate dueBy; + + @Before + public void setUp() throws Exception { + super.setUp(); + final List<ToDoItem> all = wrap(toDoItems).notYetComplete(); + toDoItem = wrap(all.get(0)); + + toDoItem = wrap(fixtureScript.lookup("to-do-items-recreate-and-complete-several/to-do-item-for-buy-bread/item-1", ToDoItem.class)); + assertThat(toDoItem, is(not(nullValue()))); + + nextTransaction(); + + dueBy = toDoItem.getDueBy(); + } + + + @Test + public void includesDescription() throws Exception { + + // given + assertThat(container().titleOf(toDoItem), containsString("Buy bread due by")); + + // when + unwrap(toDoItem).setDescription("Buy bread and butter"); + + // then + assertThat(container().titleOf(toDoItem), containsString("Buy bread and butter due by")); + } + + @Test + public void includesDueDateIfAny() throws Exception { + + // given + assertThat(container().titleOf(toDoItem), containsString("due by " + dueBy.toString("yyyy-MM-dd"))); + + // when + final LocalDate fiveDaysFromNow = Clock.getTimeAsLocalDate().plusDays(5); + unwrap(toDoItem).setDueBy(fiveDaysFromNow); + + // then + assertThat(container().titleOf(toDoItem), containsString("due by " + fiveDaysFromNow.toString("yyyy-MM-dd"))); + } + + + @Test + public void ignoresDueDateIfNone() throws Exception { + + // when + // (since wrapped, will call clearDueBy) + toDoItem.setDueBy(null); + + // then + assertThat(container().titleOf(toDoItem), not(containsString("due by"))); + } + + @Test + public void usesWhetherCompleted() throws Exception { + + // given + assertThat(container().titleOf(toDoItem), not(containsString("Completed!"))); + + // when + toDoItem.completed(); + + // then + assertThat(container().titleOf(toDoItem), not(containsString("due by"))); + assertThat(container().titleOf(toDoItem), containsString("Buy bread - Completed!")); + } + } + + public static class Actions { + + public static class Completed extends ToDoItemIntegTest { + + @Test + public void happyCase() throws Exception { + + // given + assertThat(toDoItem.isComplete(), is(false)); + + // when + toDoItem.completed(); + + // then + assertThat(toDoItem.isComplete(), is(true)); + } + + @Test + public void cannotCompleteIfAlreadyCompleted() throws Exception { + + // given + unwrap(toDoItem).setComplete(true); + + // when, then should fail + expectedExceptions.expectMessage("Already completed"); + toDoItem.completed(); + + // and then + final EventObject ev = toDoItemSubscriptions.mostRecentlyReceivedEvent(EventObject.class); + assertThat(ev, is(nullValue())); + } + + + @Test + public void cannotSetPropertyDirectly() throws Exception { + + // given + + // when, then should fail + expectedExceptions.expectMessage("Always disabled"); + toDoItem.setComplete(true); + + // and then + final EventObject ev = toDoItemSubscriptions.mostRecentlyReceivedEvent(EventObject.class); + assertThat(ev, is(nullValue())); + } + + @Test + public void subscriberReceivesEvents() throws Exception { + + // given + toDoItemSubscriptions.reset(); + assertThat(toDoItemSubscriptions.getSubscriberBehaviour(), is(ToDoItemSubscriptions.Behaviour.AnyExecuteAccept)); + assertThat(unwrap(toDoItem).isComplete(), is(false)); + + // when + toDoItem.completed(); + + // then + assertThat(unwrap(toDoItem).isComplete(), is(true)); + + // and then + final List<ToDoItem.CompletedEvent> receivedEvents = toDoItemSubscriptions.receivedEvents(ToDoItem.CompletedEvent.class); + + // hide, disable, validate, executing, executed + // sent to both the general on(ActionInteractionEvent ev) + // and also the specific on(final ToDoItem.CompletedEvent ev) + assertThat(receivedEvents.size(), is(5*2)); + final ToDoItem.CompletedEvent ev = receivedEvents.get(0); + + ToDoItem source = ev.getSource(); + assertThat(source, is(equalTo(unwrap(toDoItem)))); + assertThat(ev.getIdentifier().getMemberName(), is("completed")); + } + + @Test + public void subscriberVetoesEventWithRecoverableException() throws Exception { + + // given + toDoItemSubscriptions.subscriberBehaviour(ToDoItemSubscriptions.Behaviour.AnyExecuteVetoWithRecoverableException); + + // then + expectedExceptions.expect(RecoverableException.class); + + // when + toDoItem.completed(); + } + + @Test + public void subscriberVetoesEventWithNonRecoverableException() throws Exception { + + // given + toDoItemSubscriptions.subscriberBehaviour(ToDoItemSubscriptions.Behaviour.AnyExecuteVetoWithNonRecoverableException); + + // then + expectedExceptions.expect(NonRecoverableException.class); + + // when + toDoItem.completed(); + } + + @Test + public void subscriberVetoesEventWithAnyOtherException() throws Exception { + + // given + toDoItemSubscriptions.subscriberBehaviour(ToDoItemSubscriptions.Behaviour.AnyExecuteVetoWithOtherException); + + // then + expectedExceptions.expect(RuntimeException.class); + + // when + toDoItem.completed(); + } + + } + + /** + * This test demonstrates how a single service can be replaced, eg to use a mock. + */ + public static class Completed_withMockService extends ToDoItemIntegTest { + + private EventBusService originalEventBusService; + @Mock + private EventBusService mockEventBusService; + + @Before + public void setUpMockEventBusService() throws Exception { + originalEventBusService = scenarioExecution().service(EventBusService.class); + + context.checking(new Expectations() {{ + ignoring(mockEventBusService).register(with(any(Object.class))); + ignoring(mockEventBusService).unregister(with(any(Object.class))); + }}); + + scenarioExecution().replaceService(originalEventBusService, mockEventBusService); + scenarioExecution().closeSession(); + scenarioExecution().openSession(); + + final List<ToDoItem> all = toDoItems.notYetComplete(); + toDoItem = wrap(all.get(0)); + } + + + @After + public void reinstateOriginalEventBusService() throws Exception { + scenarioExecution().replaceService(mockEventBusService, originalEventBusService); + } + + @Test + public void raisesEvent() throws Exception { + + final Sequence busRulesThenExec = context.sequence("busRulesThenExec"); + // then + context.checking(new Expectations() {{ + oneOf(mockEventBusService).post(with(completedEvent(AbstractInteractionEvent.Phase.HIDE))); + inSequence(busRulesThenExec); + oneOf(mockEventBusService).post(with(completedEvent(AbstractInteractionEvent.Phase.DISABLE))); + inSequence(busRulesThenExec); + oneOf(mockEventBusService).post(with(completedEvent(AbstractInteractionEvent.Phase.VALIDATE))); + inSequence(busRulesThenExec); + oneOf(mockEventBusService).post(with(completedEvent(AbstractInteractionEvent.Phase.EXECUTING))); + inSequence(busRulesThenExec); + oneOf(mockEventBusService).post(with(completedEvent(AbstractInteractionEvent.Phase.EXECUTED))); + inSequence(busRulesThenExec); + }}); + + // when + toDoItem.completed(); + } + + private Matcher<Object> completedEvent(final AbstractInteractionEvent.Phase phase) { + return new TypeSafeMatcher<Object>() { + @Override + protected boolean matchesSafely(Object item) { + if (!(item instanceof ToDoItem.CompletedEvent)) { + return false; + } + + final ToDoItem.CompletedEvent completedEvent = (ToDoItem.CompletedEvent) item; + return completedEvent.getPhase() == phase; + + } + + @Override + public void describeTo(Description description) { + description.appendText(" instance of a ToDoItem.CompletedEvent, " + phase); + } + }; + } + } + + + public static class Duplicate extends ToDoItemIntegTest { + + ToDoItem duplicateToDoItem; + + @Inject + private ClockService clockService; + + @Test + public void happyCase() throws Exception { + + // given + final LocalDate todaysDate = clockService.now(); + toDoItem.setDueBy(todaysDate); + toDoItem.updateCost(new BigDecimal("123.45")); + + duplicateToDoItem = toDoItem.duplicate( + unwrap(toDoItem).default0Duplicate(), + unwrap(toDoItem).default1Duplicate(), + unwrap(toDoItem).default2Duplicate(), + unwrap(toDoItem).default3Duplicate(), + new BigDecimal("987.65")); + + // then + assertThat(duplicateToDoItem.getDescription(), is(toDoItem.getDescription() + " - Copy")); + assertThat(duplicateToDoItem.getCategory(), is(toDoItem.getCategory())); + assertThat(duplicateToDoItem.getDueBy(), is(todaysDate)); + assertThat(duplicateToDoItem.getCost(), is(new BigDecimal("987.65"))); + } + } + + public static class NotYetCompleted extends ToDoItemIntegTest { + + @Test + public void happyCase() throws Exception { + + // given + unwrap(toDoItem).setComplete(true); + + // when + toDoItem.notYetCompleted(); + + // then + assertThat(toDoItem.isComplete(), is(false)); + } + + @Test + public void cannotUndoIfNotYetCompleted() throws Exception { + + // given + assertThat(toDoItem.isComplete(), is(false)); + + // when, then should fail + expectedExceptions.expectMessage("Not yet completed"); + toDoItem.notYetCompleted(); + } + + /** + * Even though {@link dom.todo.ToDoItem${symbol_pound}notYetCompleted()} is not annotated with + * {@link org.apache.isis.applib.annotation.ActionInteraction}, an event is still raised. + */ + @Test + public void subscriberReceivesEvent() throws Exception { + + // given + assertThat(toDoItemSubscriptions.getSubscriberBehaviour(), is(ToDoItemSubscriptions.Behaviour.AnyExecuteAccept)); + unwrap(toDoItem).setComplete(true); + + // when + toDoItem.notYetCompleted(); + + // then + assertThat(unwrap(toDoItem).isComplete(), is(false)); + + // and then + final ActionDomainEvent<ToDoItem> ev = toDoItemSubscriptions.mostRecentlyReceivedEvent(ActionDomainEvent.class); + assertThat(ev, is(not(nullValue()))); + + ToDoItem source = ev.getSource(); + assertThat(source, is(equalTo(unwrap(toDoItem)))); + assertThat(ev.getIdentifier().getMemberName(), is("notYetCompleted")); + } + } + } + + public static class Collections { + + public static class Dependencies { + public static class Add extends ToDoItemIntegTest { + + private ToDoItem otherToDoItem; + + @Before + public void setUp() throws Exception { + super.setUp(); + final List<ToDoItem> items = wrap(toDoItems).notYetComplete(); + otherToDoItem = wrap(items.get(1)); + } + + @After + public void tearDown() throws Exception { + unwrap(toDoItem).getDependencies().clear(); + super.tearDown(); + } + + @Test + public void happyCase() throws Exception { + + // given + assertThat(toDoItem.getDependencies().size(), is(0)); + + // when + toDoItem.add(otherToDoItem); + + // then + assertThat(toDoItem.getDependencies().size(), is(1)); + assertThat(toDoItem.getDependencies().iterator().next(), is(unwrap(otherToDoItem))); + } + + + @Test + public void cannotDependOnSelf() throws Exception { + + // then + expectedExceptions.expectMessage("Can't set up a dependency to self"); + + // when + toDoItem.add(toDoItem); + } + + @Test + public void cannotAddIfComplete() throws Exception { + + // given + unwrap(toDoItem).setComplete(true); + + // then + expectedExceptions.expectMessage("Cannot add dependencies for items that are complete"); + + // when + toDoItem.add(otherToDoItem); + } + + + @Test + public void subscriberReceivesEvent() throws Exception { + + // given + toDoItemSubscriptions.reset(); + + // when + toDoItem.add(otherToDoItem); + + // then received events + @SuppressWarnings("unchecked") + final List<EventObject> receivedEvents = toDoItemSubscriptions.receivedEvents(); + + assertThat(receivedEvents.size(), is(7)); + assertThat(receivedEvents.get(0) instanceof ActionDomainEvent, is(true)); // ToDoItem${symbol_pound}add() executed + assertThat(receivedEvents.get(1) instanceof CollectionDomainEvent, is(true)); // ToDoItem${symbol_pound}dependencies add, executed + assertThat(receivedEvents.get(2) instanceof CollectionDomainEvent, is(true)); // ToDoItem${symbol_pound}dependencies add, executing + assertThat(receivedEvents.get(3) instanceof ActionDomainEvent, is(true)); // ToDoItem${symbol_pound}add executing + assertThat(receivedEvents.get(4) instanceof ActionDomainEvent, is(true)); // ToDoItem${symbol_pound}add validate + assertThat(receivedEvents.get(5) instanceof ActionDomainEvent, is(true)); // ToDoItem${symbol_pound}add disable + assertThat(receivedEvents.get(6) instanceof ActionDomainEvent, is(true)); // ToDoItem${symbol_pound}add hide + + // inspect the collection interaction (posted programmatically in ToDoItem${symbol_pound}add) + final CollectionDomainEvent<ToDoItem,ToDoItem> ciEv = (CollectionDomainEvent<ToDoItem, ToDoItem>) toDoItemSubscriptions.mostRecentlyReceivedEvent(CollectionDomainEvent.class); + assertThat(ciEv, is(notNullValue())); + + assertThat(ciEv.getSource(), is(equalTo(unwrap(toDoItem)))); + assertThat(ciEv.getIdentifier().getMemberName(), is("dependencies")); + assertThat(ciEv.getOf(), is(CollectionDomainEvent.Of.ADD_TO)); + assertThat(ciEv.getValue(), is(unwrap(otherToDoItem))); + + // inspect the action interaction (posted declaratively by framework) + final ActionDomainEvent<ToDoItem> aiEv = (ActionDomainEvent<ToDoItem>) toDoItemSubscriptions.mostRecentlyReceivedEvent(ActionDomainEvent.class); + assertThat(aiEv, is(notNullValue())); + + assertThat(aiEv.getSource(), is(equalTo(unwrap(toDoItem)))); + assertThat(aiEv.getIdentifier().getMemberName(), is("add")); + assertThat(aiEv.getArguments().size(), is(1)); + assertThat(aiEv.getArguments().get(0), is(unwrap((Object)otherToDoItem))); + assertThat(aiEv.getCommand(), is(notNullValue())); + } + + @Test + public void subscriberVetoesEventWithRecoverableException() throws Exception { + + // given + toDoItemSubscriptions.subscriberBehaviour(ToDoItemSubscriptions.Behaviour.AnyExecuteVetoWithRecoverableException); + + // then + expectedExceptions.expect(RecoverableException.class); + + // when + toDoItem.add(otherToDoItem); + } + + @Test + public void subscriberVetoesEventWithNonRecoverableException() throws Exception { + + // given + toDoItemSubscriptions.subscriberBehaviour(ToDoItemSubscriptions.Behaviour.AnyExecuteVetoWithNonRecoverableException); + + // then + expectedExceptions.expect(NonRecoverableException.class); + + // when + toDoItem.add(otherToDoItem); + } + + @Test + public void subscriberVetoesEventWithAnyOtherException() throws Exception { + + // given + toDoItemSubscriptions.subscriberBehaviour(ToDoItemSubscriptions.Behaviour.AnyExecuteVetoWithOtherException); + + // then + expectedExceptions.expect(RuntimeException.class); + + // when + toDoItem.add(otherToDoItem); + } + } + public static class Remove extends ToDoItemIntegTest { + + private ToDoItem otherToDoItem; + private ToDoItem yetAnotherToDoItem; + + @Before + public void setUp() throws Exception { + super.setUp(); + final List<ToDoItem> items = wrap(toDoItems).notYetComplete(); + otherToDoItem = wrap(items.get(1)); + yetAnotherToDoItem = wrap(items.get(2)); + + toDoItem.add(otherToDoItem); + } + + @After + public void tearDown() throws Exception { + unwrap(toDoItem).getDependencies().clear(); + super.tearDown(); + } + + @Test + public void happyCase() throws Exception { + + // given + assertThat(toDoItem.getDependencies().size(), is(1)); + + // when + toDoItem.remove(otherToDoItem); + + // then + assertThat(toDoItem.getDependencies().size(), is(0)); + } + + + @Test + public void cannotRemoveItemIfNotADependency() throws Exception { + + // then + expectedExceptions.expectMessage("Not a dependency"); + + // when + toDoItem.remove(yetAnotherToDoItem); + } + + @Test + public void cannotRemoveDependencyIfComplete() throws Exception { + + // given + unwrap(toDoItem).setComplete(true); + + // then + expectedExceptions.expectMessage("Cannot remove dependencies for items that are complete"); + + // when + toDoItem.remove(otherToDoItem); + } + + @Test + public void subscriberVetoesEventWithRecoverableException() throws Exception { + + // given + toDoItemSubscriptions.subscriberBehaviour(ToDoItemSubscriptions.Behaviour.AnyExecuteVetoWithRecoverableException); + + // then + expectedExceptions.expect(RecoverableException.class); + + // when + toDoItem.remove(otherToDoItem); + } + + @Test + public void subscriberVetoesEventWithNonRecoverableException() throws Exception { + + // given + toDoItemSubscriptions.subscriberBehaviour(ToDoItemSubscriptions.Behaviour.AnyExecuteVetoWithNonRecoverableException); + + // then + expectedExceptions.expect(NonRecoverableException.class); + + // when + toDoItem.remove(otherToDoItem); + } + + @Test + public void subscriberVetoesEventWithAnyOtherException() throws Exception { + + // given + toDoItemSubscriptions.subscriberBehaviour(ToDoItemSubscriptions.Behaviour.AnyExecuteVetoWithOtherException); + + // then + expectedExceptions.expect(RuntimeException.class); + + // when + toDoItem.remove(otherToDoItem); + } + } + } + + } + + public static class Properties { + + public static class Attachment extends ToDoItemIntegTest { + + @Test + public void happyCase() throws Exception { + + byte[] bytes = "{${symbol_escape}"foo${symbol_escape}": ${symbol_escape}"bar${symbol_escape}"}".getBytes(Charset.forName("UTF-8")); + final Blob newAttachment = new Blob("myfile.json", new MimeType("application/json"), bytes); + + // when + toDoItem.setAttachment(newAttachment); + + // then + assertThat(toDoItem.getAttachment(), is(newAttachment)); + } + + @Test + public void canBeNull() throws Exception { + + // when + toDoItem.setAttachment((Blob)null); + + // then + assertThat(toDoItem.getAttachment(), is((Blob)null)); + } + } + + public static class Category extends ToDoItemIntegTest { + + @Test + public void cannotModify() throws Exception { + + // when, then + expectedExceptions.expectMessage(containsString("Reason: Use action to update both category and subcategory.")); + toDoItem.setCategory(ToDoItem.Category.Professional); + } + } + + public static class Cost extends ToDoItemIntegTest { + + private BigDecimal cost; + + @Before + public void setUp() throws Exception { + super.setUp(); + cost = toDoItem.getCost(); + } + + @Test + public void happyCaseUsingProperty() throws Exception { + + final BigDecimal newCost = new BigDecimal("123.45"); + + // when + toDoItem.updateCost(newCost); + + // then + assertThat(toDoItem.getCost(), is(newCost)); + } + + @Test + public void happyCaseUsingAction() throws Exception { + + final BigDecimal newCost = new BigDecimal("123.45"); + + // when + toDoItem.updateCost(newCost); + + // then + assertThat(toDoItem.getCost(), is(newCost)); + } + + @Test + public void canBeNull() throws Exception { + + // when + toDoItem.updateCost((BigDecimal)null); + + // then + assertThat(toDoItem.getCost(), is((BigDecimal)null)); + } + + @Test + public void defaultForAction() throws Exception { + + // then + assertThat(unwrap(toDoItem).default0UpdateCost(), is(cost)); + } + + } + + public static class Description extends ToDoItemIntegTest { + + @Test + public void happyCase() throws Exception { + + // given + assertThat(toDoItem.getDescription(), is("Buy bread")); + + // when + toDoItem.setDescription("Buy bread and butter"); + + // then + assertThat(toDoItem.getDescription(), is("Buy bread and butter")); + } + + + @Test + public void failsRegex() throws Exception { + + // when + expectedExceptions.expectMessage("Doesn't match pattern"); + toDoItem.setDescription("exclamation marks are not allowed!!!"); + } + + @Test + public void cannotBeNull() throws Exception { + + // when, then + expectedExceptions.expectMessage("Mandatory"); + toDoItem.setDescription(null); + } + + @Test + public void cannotUseModify() throws Exception { + + expectedExceptions.expectMessage("Cannot invoke supporting method for 'Description'; use only property accessor/mutator"); + + // given + assertThat(toDoItem.getDescription(), is("Buy bread")); + + // when + toDoItem.modifyDescription("Buy bread and butter"); + + // then + assertThat(toDoItem.getDescription(), is("Buy bread")); + } + + @Test + public void cannotUseClear() throws Exception { + + expectedExceptions.expectMessage("Cannot invoke supporting method for 'Description'; use only property accessor/mutator"); + + // given + assertThat(toDoItem.getDescription(), is("Buy bread")); + + // when + toDoItem.clearDescription(); + + // then + assertThat(toDoItem.getDescription(), is("Buy bread")); + } + + + @Test + public void onlyJustShortEnough() throws Exception { + + // when, then + toDoItem.setDescription(characters(100)); + } + + @Test + public void tooLong() throws Exception { + + // then + expectedExceptions.expectMessage("The value proposed exceeds the maximum length of 100"); + + // when + toDoItem.setDescription(characters(101)); + } + + + @Test + public void subscriberReceivesEvent() throws Exception { + + // given + assertThat(toDoItemSubscriptions.getSubscriberBehaviour(), is(ToDoItemSubscriptions.Behaviour.AnyExecuteAccept)); + assertThat(toDoItem.getDescription(), is("Buy bread")); + + // when + toDoItem.setDescription("Buy bread and butter"); + + // then published and received + @SuppressWarnings("unchecked") + final PropertyDomainEvent<ToDoItem,String> ev = toDoItemSubscriptions.mostRecentlyReceivedEvent(PropertyDomainEvent.class); + assertThat(ev, is(not(nullValue()))); + + ToDoItem source = ev.getSource(); + assertThat(source, is(equalTo(unwrap(toDoItem)))); + assertThat(ev.getIdentifier().getMemberName(), is("description")); + assertThat(ev.getOldValue(), is("Buy bread")); + assertThat(ev.getNewValue(), is("Buy bread and butter")); + } + + @Test + public void subscriberVetoesEventWithRecoverableException() throws Exception { + + // given + toDoItemSubscriptions.subscriberBehaviour(ToDoItemSubscriptions.Behaviour.AnyExecuteVetoWithRecoverableException); + + // then + expectedExceptions.expect(RecoverableException.class); + + // when + toDoItem.setDescription("Buy bread and butter"); + } + + + @Test + public void subscriberVetoesEventWithNonRecoverableException() throws Exception { + + // given + toDoItemSubscriptions.subscriberBehaviour(ToDoItemSubscriptions.Behaviour.AnyExecuteVetoWithNonRecoverableException); + + // then + expectedExceptions.expect(NonRecoverableException.class); + + // when + toDoItem.setDescription("Buy bread and butter"); + } + + + @Test + public void subscriberVetoesEventWithAnyOtherException() throws Exception { + + // given + toDoItemSubscriptions.subscriberBehaviour(ToDoItemSubscriptions.Behaviour.AnyExecuteVetoWithOtherException); + + // then + expectedExceptions.expect(RuntimeException.class); + + // when + toDoItem.setDescription("Buy bread and butter"); + } + + + private static String characters(final int n) { + StringBuffer buf = new StringBuffer(); + for(int i=0; i<n; i++) { + buf.append("a"); + } + return buf.toString(); + } + } + + public static class DueBy extends ToDoItemIntegTest { + + @Inject + private ClockService clockService; + + @Test + public void happyCase() throws Exception { + + // when + final LocalDate fiveDaysFromNow = clockService.now().plusDays(5); + toDoItem.setDueBy(fiveDaysFromNow); + + // then + assertThat(toDoItem.getDueBy(), is(fiveDaysFromNow)); + } + + + @Test + public void canBeNull() throws Exception { + + // when + toDoItem.setDueBy((LocalDate)null); + + // then + assertThat(toDoItem.getDueBy(), is((LocalDate)null)); + } + + @Test + public void canBeUpToSixDaysInPast() throws Exception { + + final LocalDate nowAsLocalDate = clockService.now(); + final LocalDate sixDaysAgo = nowAsLocalDate.plusDays(-5); + + // when + toDoItem.setDueBy(sixDaysAgo); + + // then + assertThat(toDoItem.getDueBy(), is(sixDaysAgo)); + } + + + @Test + public void cannotBeMoreThanSixDaysInPast() throws Exception { + + final LocalDate sevenDaysAgo = Clock.getTimeAsLocalDate().plusDays(-7); + + // when, then + expectedExceptions.expectMessage("Due by date cannot be more than one week old"); + toDoItem.setDueBy(sevenDaysAgo); + } + } + + public static class Notes extends ToDoItemIntegTest { + + @Test + public void happyCase() throws Exception { + + final String newNotes = "Lorem ipsum yada yada"; + + // when + toDoItem.setNotes(newNotes); + + // then + assertThat(toDoItem.getNotes(), is(newNotes)); + } + + @Test + public void canBeNull() throws Exception { + + // when + toDoItem.setNotes((String)null); + + // then + assertThat(toDoItem.getNotes(), is((String)null)); + } + + @Test + public void suscriberReceivedDefaultEvent() throws Exception { + + final String newNotes = "Lorem ipsum yada yada"; + + // when + toDoItem.setNotes(newNotes); + + // then + assertThat(unwrap(toDoItem).getNotes(), is(newNotes)); + + // and then receive the default event. + @SuppressWarnings("unchecked") + final PropertyDomainEvent.Default ev = toDoItemSubscriptions.mostRecentlyReceivedEvent(PropertyDomainEvent.Default.class); + assertThat(ev, is(notNullValue())); + + assertThat(ev.getSource(), is((Object)unwrap(toDoItem))); + assertThat(ev.getNewValue(), is((Object)newNotes)); + } + + + } + + public static class OwnedBy extends ToDoItemIntegTest { + + @Test + public void cannotModify() throws Exception { + + // when, then + expectedExceptions.expectMessage("Reason: Hidden on Everywhere. Identifier: dom.todo.ToDoItem${symbol_pound}ownedBy()"); + toDoItem.setOwnedBy("other"); + } + + + } + + public static class Subcategory extends ToDoItemIntegTest { + + @Test + public void cannotModify() throws Exception { + + // when, then + expectedExceptions.expectMessage(containsString("Reason: Use action to update both category and subcategory.")); + toDoItem.setSubcategory(ToDoItem.Subcategory.Chores); + } + } + + } + + + + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/isis/blob/a4ec0b72/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/tests/ToDoItemsIntegTest.java ---------------------------------------------------------------------- diff --git a/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/tests/ToDoItemsIntegTest.java b/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/tests/ToDoItemsIntegTest.java new file mode 100644 index 0000000..203cdfe --- /dev/null +++ b/mothballed/example/archetype/todoapp/src/main/resources/archetype-resources/integtests/src/test/java/integration/tests/ToDoItemsIntegTest.java @@ -0,0 +1,135 @@ +#set( $symbol_pound = '#' ) +#set( $symbol_dollar = '$' ) +#set( $symbol_escape = '\' ) +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package integration.tests; + +import dom.todo.ToDoItem; +import dom.todo.ToDoItems; +import fixture.todo.integtests.ToDoItemsIntegTestFixture; + +import java.util.List; +import javax.inject.Inject; +import org.hamcrest.Matchers; +import org.junit.Before; +import org.junit.Test; +import org.apache.isis.applib.fixturescripts.FixtureScripts; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +public class ToDoItemsIntegTest extends AbstractToDoIntegTest { + + @Inject + FixtureScripts fixtureScripts; + @Inject + ToDoItems toDoItems; + + public static class Finders extends ToDoItemsIntegTest { + + ToDoItemsIntegTestFixture fixture; + + @Before + public void setUpData() throws Exception { + // executing the fixtures directly allows us to look up the results later. + fixtureScripts.runFixtureScript(fixture = new ToDoItemsIntegTestFixture(), null); + } + + private int notYetCompletedSize; + private int completedSize; + + @Before + public void setUp() throws Exception { + + // could use fixture${symbol_pound}lookup(...), but can also just search directly. + final List<ToDoItem> notYetCompleteItems = wrap(toDoItems).notYetComplete(); + final List<ToDoItem> completedItems = wrap(toDoItems).complete(); + + notYetCompletedSize = notYetCompleteItems.size(); + completedSize = completedItems.size(); + + assertThat(notYetCompletedSize, is(Matchers.greaterThan(5))); + } + + @Test + public void complete_and_notYetComplete() throws Exception { + + // given + List<ToDoItem> notYetCompleteItems = wrap(toDoItems).notYetComplete(); + final ToDoItem toDoItem = wrap(notYetCompleteItems.get(0)); + nextTransaction(); + + // when + toDoItem.completed(); + nextTransaction(); + + // then + assertThat(wrap(toDoItems).notYetComplete().size(), is(notYetCompletedSize-1)); + assertThat(wrap(toDoItems).complete().size(), is(completedSize+1)); + nextTransaction(); + + // and when + toDoItem.notYetCompleted(); + nextTransaction(); + + // then + assertThat(wrap(toDoItems).notYetComplete().size(), is(notYetCompletedSize)); + assertThat(wrap(toDoItems).complete().size(), is(completedSize)); + } + } + + public static class NewToDo_and_Delete extends ToDoItemsIntegTest { + + @Before + public void setUpData() throws Exception { + // none + } + + @Test + public void happyCase() throws Exception { + + // given + int size = wrap(toDoItems).notYetComplete().size(); + nextTransaction(); + + // when + final ToDoItem newToDo = toDoItems.newToDo("new todo", ToDoItem.Category.Professional, ToDoItem.Subcategory.OpenSource, null, null); + nextTransaction(); + + // then + assertThat(newToDo.getDescription(), is("new todo")); + assertThat(newToDo.getCategory(), is(ToDoItem.Category.Professional)); + assertThat(wrap(toDoItems).notYetComplete().size(), is(size+1)); + assertThat(container().isPersistent(newToDo), is(true)); + assertThat(container().isPersistent(wrap(newToDo)), is(true)); + + nextTransaction(); + + // when + newToDo.delete(); + nextTransaction(); + + // then + assertThat(wrap(toDoItems).notYetComplete().size(), is(size)); + } + + } + +} \ No newline at end of file
