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

Reply via email to