From 5f68be4aabd988c2a52714d96d2d8f26c91b6b15 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Tue, 13 Sep 2016 14:10:41 +0300 Subject: [PATCH 01/98] Add creation test covering various Builder params. --- .../server/stand/StandFunnelShould.java | 36 ++++++++++++++++++- .../org/spine3/testdata/TestStandFactory.java | 2 +- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index 9e42f37e68e..5eb8fb42bdf 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -27,6 +27,8 @@ import org.spine3.testdata.TestStandFactory; import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.spy; @@ -40,7 +42,6 @@ public class StandFunnelShould { // **** Positive scenarios (unit) **** /** - * - Initialize properly with various Builder options; * - deliver mock updates to the stand (invoke proper methods with particular arguments) - test the delivery only. */ @@ -53,6 +54,39 @@ public void initialize_properly_with_stand_only() { Assert.assertNotNull(standFunnel); } + @Test + public void initialize_properly_with_various_builder_options() { + final Stand stand = TestStandFactory.create(); + final Executor executor = Executors.newSingleThreadExecutor(new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + return Thread.currentThread(); + } + }); + + final StandFunnel blockingFunnel = StandFunnel.newBuilder() + .setStand(stand) + .setExecutor(executor) + .build(); + Assert.assertNotNull(blockingFunnel); + + final StandFunnel funnelForBusyStand = StandFunnel.newBuilder() + .setStand(stand) + .setExecutor(Executors.newSingleThreadExecutor()) + .build(); + Assert.assertNotNull(funnelForBusyStand); + + + final StandFunnel emptyExecutorFunnel = StandFunnel.newBuilder() + .setStand(TestStandFactory.create()) + .setExecutor(new Executor() { + @Override + public void execute(Runnable neverCalled) { } + }) + .build(); + Assert.assertNotNull(emptyExecutorFunnel); + } + @Test public void use_executor_from_builder() { diff --git a/server/src/test/java/org/spine3/testdata/TestStandFactory.java b/server/src/test/java/org/spine3/testdata/TestStandFactory.java index d54df4fae6d..474a6226b30 100644 --- a/server/src/test/java/org/spine3/testdata/TestStandFactory.java +++ b/server/src/test/java/org/spine3/testdata/TestStandFactory.java @@ -22,13 +22,13 @@ package org.spine3.testdata; import org.spine3.server.stand.Stand; -import org.spine3.server.storage.memory.InMemoryStandStorage; /** * Creates stands for tests. * * @author Alex Tymchenko */ +@SuppressWarnings("UtilityClass") public class TestStandFactory { private TestStandFactory() {} From cad1d94bbafe30991974db1eea60aba4693d5ab3 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Tue, 13 Sep 2016 14:11:56 +0300 Subject: [PATCH 02/98] Bump gradle version, --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 58e7b927f5d..6d19c1542b6 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-3.0-bin.zip From d700a419894388c31b86d78c973c1fe847c3f506 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Tue, 13 Sep 2016 14:37:55 +0300 Subject: [PATCH 03/98] Add some more tests (need to be processed). --- .../org/spine3/server/stand/StandFunnel.java | 2 ++ .../server/stand/StandFunnelShould.java | 35 +++++++++++++++++-- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/server/src/main/java/org/spine3/server/stand/StandFunnel.java b/server/src/main/java/org/spine3/server/stand/StandFunnel.java index 9e3cc659ce0..44997adbdf8 100644 --- a/server/src/main/java/org/spine3/server/stand/StandFunnel.java +++ b/server/src/main/java/org/spine3/server/stand/StandFunnel.java @@ -109,6 +109,7 @@ public Stand getStand() { *

The value must not be null. * *

If this method is not used, a default value will be used. + * // TODO:13-09-16:dmytro.dashenkov: Correct docs. No default value for Stand is used, null value leads to a fail instead. * * @param stand the instance of {@link Stand}. * @return {@code this} instance of {@code Builder} @@ -122,6 +123,7 @@ public Executor getExecutor() { return executor; } + // TODO:13-09-16:dmytro.dashenkov: Complete docs. Here is the place where a default value is used in case of not using the method. /** * Set the {@code Executor} instance for this {@code StandFunnel}. * diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index 5eb8fb42bdf..da5f2bffdfd 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -87,6 +87,21 @@ public void execute(Runnable neverCalled) { } Assert.assertNotNull(emptyExecutorFunnel); } + @Test + public void deliver_mock_updates_to_stand() { + final Stand stand = spy(TestStandFactory.create()); + + final StandFunnel funnel = StandFunnel.newBuilder() + .setStand(stand) + .build(); + + final Object id = new Object(); + final Any state = Any.getDefaultInstance(); + funnel.post(id, state); + + verify(stand).update(id, state); + } + @Test public void use_executor_from_builder() { @@ -114,9 +129,23 @@ public void execute(Runnable command) { // **** Negative scenarios (unit) **** - /** - * - Fail to initialise with improper stand. - */ + @SuppressWarnings("ResultOfMethodCallIgnored") + @Test(expected = NullPointerException.class) + public void fail_to_initialize_with_null_stand() { + @SuppressWarnings("ConstantConditions") + final StandFunnel.Builder builder = StandFunnel.newBuilder().setStand(null); + + builder.build(); + } + + @SuppressWarnings("ResultOfMethodCallIgnored") + @Test(expected = NullPointerException.class) + public void fail_to_initialize_with_importer_stand() { + // TODO:13-09-16:dmytro.dashenkov: Implement. +// @SuppressWarnings("ConstantConditions") +// final StandFunnel.Builder builder = StandFunnel.newBuilder().setStand(null); +// builder.build(); + } @SuppressWarnings("ResultOfMethodCallIgnored") @Test(expected = IllegalStateException.class) From 09b2231c0d70f4335bcd147435ae09ad012000f7 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Tue, 13 Sep 2016 15:16:06 +0300 Subject: [PATCH 04/98] Fix deliver mock updates test. --- .../server/stand/StandFunnelShould.java | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index da5f2bffdfd..ef7ba6c2b08 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -31,6 +31,8 @@ import java.util.concurrent.ThreadFactory; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -89,14 +91,16 @@ public void execute(Runnable neverCalled) { } @Test public void deliver_mock_updates_to_stand() { - final Stand stand = spy(TestStandFactory.create()); + final Object id = new Object(); + final Any state = Any.getDefaultInstance(); + + final Stand stand = mock(Stand.class); + doNothing().when(stand).update(id, state); final StandFunnel funnel = StandFunnel.newBuilder() .setStand(stand) .build(); - final Object id = new Object(); - final Any state = Any.getDefaultInstance(); funnel.post(id, state); verify(stand).update(id, state); @@ -131,22 +135,13 @@ public void execute(Runnable command) { @SuppressWarnings("ResultOfMethodCallIgnored") @Test(expected = NullPointerException.class) - public void fail_to_initialize_with_null_stand() { + public void fail_to_initialize_with_inproper_stand() { @SuppressWarnings("ConstantConditions") final StandFunnel.Builder builder = StandFunnel.newBuilder().setStand(null); builder.build(); } - @SuppressWarnings("ResultOfMethodCallIgnored") - @Test(expected = NullPointerException.class) - public void fail_to_initialize_with_importer_stand() { - // TODO:13-09-16:dmytro.dashenkov: Implement. -// @SuppressWarnings("ConstantConditions") -// final StandFunnel.Builder builder = StandFunnel.newBuilder().setStand(null); -// builder.build(); - } - @SuppressWarnings("ResultOfMethodCallIgnored") @Test(expected = IllegalStateException.class) public void fail_to_initialize_from_empty_builder() { @@ -162,5 +157,10 @@ public void fail_to_initialize_from_empty_builder() { * - deliver the updates from several projection and aggregate repositories. */ + @Test + public void deliver_updates_from_projection_repo() { + + } + } From 16612e210e971f95a4a66d3c5ac317faeacfe47d Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 13:57:38 +0300 Subject: [PATCH 05/98] Explicit "Given" for stand tests. --- .../java/org/spine3/server/stand/Given.java | 67 +++++++++++++++++++ .../org/spine3/server/stand/StandShould.java | 23 +------ 2 files changed, 68 insertions(+), 22 deletions(-) create mode 100644 server/src/test/java/org/spine3/server/stand/Given.java diff --git a/server/src/test/java/org/spine3/server/stand/Given.java b/server/src/test/java/org/spine3/server/stand/Given.java new file mode 100644 index 00000000000..f1b54d0adfe --- /dev/null +++ b/server/src/test/java/org/spine3/server/stand/Given.java @@ -0,0 +1,67 @@ +/* + * Copyright 2016, TeamDev Ltd. All rights reserved. + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.spine3.server.stand; + +import org.spine3.base.Event; +import org.spine3.base.EventContext; +import org.spine3.protobuf.AnyPacker; +import org.spine3.server.BoundedContext; +import org.spine3.server.projection.Projection; +import org.spine3.server.projection.ProjectionRepository; +import org.spine3.test.projection.Project; +import org.spine3.test.projection.ProjectId; +import org.spine3.test.projection.event.ProjectCreated; + +/** + * @author Dmytro Dashenkov + */ +/*package*/ class Given { + + /*package*/static class StandTestProjectionRepository extends ProjectionRepository { + /*package*/ StandTestProjectionRepository(BoundedContext boundedContext) { + super(boundedContext); + } + } + + private static class StandTestProjection extends Projection { + /** + * Creates a new instance. + * + * Required to be public. + * + * @param id the ID for the new instance + * @throws IllegalArgumentException if the ID is not of one of the supported types + */ + public StandTestProjection(ProjectId id) { + super(id); + } + } + + /*package*/ static Event validEvent() { + return Event.newBuilder() + .setMessage(AnyPacker.pack(ProjectCreated.newBuilder() + .setProjectId(ProjectId.newBuilder().setId("12345AD0")) + .build()) + .toBuilder().setTypeUrl(ProjectCreated.getDescriptor().getFullName()).build()) + .setContext(EventContext.getDefaultInstance()) + .build(); + } +} diff --git a/server/src/test/java/org/spine3/server/stand/StandShould.java b/server/src/test/java/org/spine3/server/stand/StandShould.java index 98bc0609385..4cbe79e237d 100644 --- a/server/src/test/java/org/spine3/server/stand/StandShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandShould.java @@ -34,11 +34,11 @@ import org.spine3.server.projection.Projection; import org.spine3.server.projection.ProjectionRepository; import org.spine3.server.storage.EntityStorageRecord; +import org.spine3.server.stand.Given.StandTestProjectionRepository; import org.spine3.server.storage.StandStorage; import org.spine3.test.clientservice.customer.Customer; import org.spine3.test.clientservice.customer.CustomerId; import org.spine3.test.projection.Project; -import org.spine3.test.projection.ProjectId; import java.util.Objects; import java.util.Set; @@ -229,25 +229,4 @@ public void onEntityStateUpdate(Any newEntityState) { * - fail to initialize with improper build arguments. */ - - // ***** Inner classes used for tests. ***** - - private static class StandTestProjection extends Projection { - /** - * Creates a new instance. - * - * @param id the ID for the new instance - * @throws IllegalArgumentException if the ID is not of one of the supported types - */ - public StandTestProjection(ProjectId id) { - super(id); - } - } - - - private static class StandTestProjectionRepository extends ProjectionRepository { - protected StandTestProjectionRepository(BoundedContext boundedContext) { - super(boundedContext); - } - } } From 7666072fa933c61b33f8b1b8aedbd8840f079105 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 14:14:40 +0300 Subject: [PATCH 06/98] Add threading test. --- .../server/stand/StandFunnelShould.java | 44 ++++++++++++++++++- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index ef7ba6c2b08..899afd85415 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -24,11 +24,16 @@ import com.google.protobuf.Any; import org.junit.Assert; import org.junit.Test; +import org.mockito.ArgumentMatchers; import org.spine3.testdata.TestStandFactory; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doNothing; @@ -38,6 +43,7 @@ /** * @author Alex Tymchenko + * @author Dmytro Dashenkov */ public class StandFunnelShould { @@ -157,10 +163,44 @@ public void fail_to_initialize_from_empty_builder() { * - deliver the updates from several projection and aggregate repositories. */ + + @SuppressWarnings("MethodWithMultipleLoops") @Test - public void deliver_updates_from_projection_repo() { + public void deliver_updates_through_several_threads() throws InterruptedException { + final int threadsCount = 10; - } + final Map threadInvakationRegistry = new ConcurrentHashMap<>(threadsCount); + + final Stand stand = mock(Stand.class); + doNothing().when(stand).update(ArgumentMatchers.any(), any(Any.class)); + + final StandFunnel standFunnel = StandFunnel.newBuilder() + .setStand(stand) + .build(); + + final ExecutorService processes = Executors.newFixedThreadPool(threadsCount); + + + final Runnable task = new Runnable() { + @Override + public void run() { + final String threadName = Thread.currentThread().getName(); + Assert.assertFalse(threadInvakationRegistry.containsKey(threadName)); + + standFunnel.post(new Object(), Any.getDefaultInstance()); + + threadInvakationRegistry.put(threadName, new Object()); + } + }; + for (int i = 0; i < threadsCount; i++) { + processes.execute(task); + } + + processes.awaitTermination(10, TimeUnit.SECONDS); + + Assert.assertEquals(threadInvakationRegistry.size(), threadsCount); + + } } From 231f7cb0b9b61a0df161138ad7a190dc560ee446 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 14:16:54 +0300 Subject: [PATCH 07/98] Reduce executor await time. --- .../test/java/org/spine3/server/stand/StandFunnelShould.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index 899afd85415..f47fa5efb48 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -168,6 +168,7 @@ public void fail_to_initialize_from_empty_builder() { @Test public void deliver_updates_through_several_threads() throws InterruptedException { final int threadsCount = 10; + final int threadExecuteionMaxAwaitSeconds = 2; final Map threadInvakationRegistry = new ConcurrentHashMap<>(threadsCount); @@ -197,7 +198,7 @@ public void run() { processes.execute(task); } - processes.awaitTermination(10, TimeUnit.SECONDS); + processes.awaitTermination(threadExecuteionMaxAwaitSeconds, TimeUnit.SECONDS); Assert.assertEquals(threadInvakationRegistry.size(), threadsCount); From e38b6c147d19245c57c494239090a1ff4f4b25b5 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 14:54:11 +0300 Subject: [PATCH 08/98] Add deliver updates form projection repo test skeleton, --- .../java/org/spine3/server/stand/Given.java | 6 ++- .../server/stand/StandFunnelShould.java | 45 ++++++++++++++++--- 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/Given.java b/server/src/test/java/org/spine3/server/stand/Given.java index f1b54d0adfe..2f631d4995f 100644 --- a/server/src/test/java/org/spine3/server/stand/Given.java +++ b/server/src/test/java/org/spine3/server/stand/Given.java @@ -23,6 +23,7 @@ import org.spine3.base.Event; import org.spine3.base.EventContext; import org.spine3.protobuf.AnyPacker; +import org.spine3.protobuf.TypeUrl; import org.spine3.server.BoundedContext; import org.spine3.server.projection.Projection; import org.spine3.server.projection.ProjectionRepository; @@ -60,7 +61,10 @@ public StandTestProjection(ProjectId id) { .setMessage(AnyPacker.pack(ProjectCreated.newBuilder() .setProjectId(ProjectId.newBuilder().setId("12345AD0")) .build()) - .toBuilder().setTypeUrl(ProjectCreated.getDescriptor().getFullName()).build()) + .toBuilder() + .setTypeUrl(TypeUrl.SPINE_TYPE_URL_PREFIX + '/' + + ProjectCreated.getDescriptor().getFullName()) + .build()) .setContext(EventContext.getDefaultInstance()) .build(); } diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index f47fa5efb48..34e7ab2ff0f 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -25,6 +25,8 @@ import org.junit.Assert; import org.junit.Test; import org.mockito.ArgumentMatchers; +import org.spine3.server.BoundedContext; +import org.spine3.server.storage.memory.InMemoryStorageFactory; import org.spine3.testdata.TestStandFactory; import java.util.Map; @@ -40,6 +42,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; /** * @author Alex Tymchenko @@ -163,14 +166,44 @@ public void fail_to_initialize_from_empty_builder() { * - deliver the updates from several projection and aggregate repositories. */ + @Test + public void deliver_updates_from_projection_repository() throws Exception { + final BoundedContext boundedContext = mock(BoundedContext.class); + final Stand stand = mock(Stand.class); + when(boundedContext.getStandFunnel()).thenReturn(StandFunnel.newBuilder() + .setStand(stand) + .setExecutor(new Executor() { + @Override + public void execute(Runnable command) { + command.run(); + } + }) + .build()); + + // Init repository + final Given.StandTestProjectionRepository repository = new Given.StandTestProjectionRepository(boundedContext); + + stand.registerTypeSupplier(repository); + repository.initStorage(InMemoryStorageFactory.getInstance()); + repository.setOnline(); + + // Dispatch an update from projection repo + repository.dispatch(Given.validEvent()); + + // Was called ONCE + verify(boundedContext).getStandFunnel(); + verify(stand).update(ArgumentMatchers.any(), any(Any.class)); + } + @SuppressWarnings("MethodWithMultipleLoops") @Test public void deliver_updates_through_several_threads() throws InterruptedException { final int threadsCount = 10; - final int threadExecuteionMaxAwaitSeconds = 2; + @SuppressWarnings("LocalVariableNamingConvention") // Too long variable name + final int threadExecutionMaxAwaitSeconds = 2; - final Map threadInvakationRegistry = new ConcurrentHashMap<>(threadsCount); + final Map threadInvocationRegistry = new ConcurrentHashMap<>(threadsCount); final Stand stand = mock(Stand.class); doNothing().when(stand).update(ArgumentMatchers.any(), any(Any.class)); @@ -186,11 +219,11 @@ public void deliver_updates_through_several_threads() throws InterruptedExceptio @Override public void run() { final String threadName = Thread.currentThread().getName(); - Assert.assertFalse(threadInvakationRegistry.containsKey(threadName)); + Assert.assertFalse(threadInvocationRegistry.containsKey(threadName)); standFunnel.post(new Object(), Any.getDefaultInstance()); - threadInvakationRegistry.put(threadName, new Object()); + threadInvocationRegistry.put(threadName, new Object()); } }; @@ -198,9 +231,9 @@ public void run() { processes.execute(task); } - processes.awaitTermination(threadExecuteionMaxAwaitSeconds, TimeUnit.SECONDS); + processes.awaitTermination(threadExecutionMaxAwaitSeconds, TimeUnit.SECONDS); - Assert.assertEquals(threadInvakationRegistry.size(), threadsCount); + Assert.assertEquals(threadInvocationRegistry.size(), threadsCount); } From cc95a1317176cc89dee8c6a19f44b72b824588f9 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 14:57:41 +0300 Subject: [PATCH 09/98] Suppress not yet ready test. --- .../test/java/org/spine3/server/stand/StandFunnelShould.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index 34e7ab2ff0f..c924469b833 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -166,7 +166,7 @@ public void fail_to_initialize_from_empty_builder() { * - deliver the updates from several projection and aggregate repositories. */ - @Test + //@Test public void deliver_updates_from_projection_repository() throws Exception { final BoundedContext boundedContext = mock(BoundedContext.class); final Stand stand = mock(Stand.class); From 484110224af3ceb0c7d1d5149017a69c93e53c5d Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 16:09:19 +0300 Subject: [PATCH 10/98] Finish delivery from projection repo test. --- .../java/org/spine3/server/stand/Given.java | 36 +++++++++++++++++-- .../server/stand/StandFunnelShould.java | 27 +++++++------- 2 files changed, 46 insertions(+), 17 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/Given.java b/server/src/test/java/org/spine3/server/stand/Given.java index 2f631d4995f..c7da6d078b9 100644 --- a/server/src/test/java/org/spine3/server/stand/Given.java +++ b/server/src/test/java/org/spine3/server/stand/Given.java @@ -20,11 +20,16 @@ package org.spine3.server.stand; +import com.google.protobuf.Message; +import org.spine3.base.CommandContext; import org.spine3.base.Event; import org.spine3.base.EventContext; +import org.spine3.base.EventId; +import org.spine3.base.Identifiers; import org.spine3.protobuf.AnyPacker; import org.spine3.protobuf.TypeUrl; import org.spine3.server.BoundedContext; +import org.spine3.server.event.Subscribe; import org.spine3.server.projection.Projection; import org.spine3.server.projection.ProjectionRepository; import org.spine3.test.projection.Project; @@ -36,10 +41,20 @@ */ /*package*/ class Given { + private static final String PROJECT_UUID = Identifiers.newUuid(); + + private Given() { + } + /*package*/static class StandTestProjectionRepository extends ProjectionRepository { /*package*/ StandTestProjectionRepository(BoundedContext boundedContext) { super(boundedContext); } + + @Override + protected ProjectId getEntityId(Message event, EventContext context) { + return ProjectId.newBuilder().setId(PROJECT_UUID).build(); + } } private static class StandTestProjection extends Projection { @@ -54,6 +69,11 @@ private static class StandTestProjection extends Projection public StandTestProjection(ProjectId id) { super(id); } + + @Subscribe + public void handle(ProjectCreated event, EventContext context) { + // Do nothing + } } /*package*/ static Event validEvent() { @@ -62,10 +82,20 @@ public StandTestProjection(ProjectId id) { .setProjectId(ProjectId.newBuilder().setId("12345AD0")) .build()) .toBuilder() - .setTypeUrl(TypeUrl.SPINE_TYPE_URL_PREFIX + '/' + - ProjectCreated.getDescriptor().getFullName()) + .setTypeUrl(TypeUrl.SPINE_TYPE_URL_PREFIX + "/" + ProjectCreated.getDescriptor().getFullName()) .build()) - .setContext(EventContext.getDefaultInstance()) + .setContext(EventContext.newBuilder() + .setDoNotEnrich(true) + .setCommandContext(CommandContext.getDefaultInstance()) + .setEventId(EventId.newBuilder() + .setUuid(PROJECT_UUID) + .build())) .build(); } + + /*package*/ static ProjectionRepository repo(BoundedContext context) { + return new StandTestProjectionRepository(context); + } + + } diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index c924469b833..4c9816159f1 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -26,6 +26,7 @@ import org.junit.Test; import org.mockito.ArgumentMatchers; import org.spine3.server.BoundedContext; +import org.spine3.server.projection.ProjectionRepository; import org.spine3.server.storage.memory.InMemoryStorageFactory; import org.spine3.testdata.TestStandFactory; @@ -42,7 +43,6 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; /** * @author Alex Tymchenko @@ -166,22 +166,21 @@ public void fail_to_initialize_from_empty_builder() { * - deliver the updates from several projection and aggregate repositories. */ - //@Test + @Test public void deliver_updates_from_projection_repository() throws Exception { - final BoundedContext boundedContext = mock(BoundedContext.class); final Stand stand = mock(Stand.class); - when(boundedContext.getStandFunnel()).thenReturn(StandFunnel.newBuilder() - .setStand(stand) - .setExecutor(new Executor() { - @Override - public void execute(Runnable command) { - command.run(); - } - }) - .build()); - + final BoundedContext boundedContext = spy(BoundedContext.newBuilder() + .setStand(stand) + .setStorageFactory(InMemoryStorageFactory.getInstance()) + .setStandFunnelExecutor(new Executor() { // Straightforward executor + @Override + public void execute(Runnable command) { + command.run(); + } + }) + .build()); // Init repository - final Given.StandTestProjectionRepository repository = new Given.StandTestProjectionRepository(boundedContext); + final ProjectionRepository repository = Given.repo(boundedContext); stand.registerTypeSupplier(repository); repository.initStorage(InMemoryStorageFactory.getInstance()); From ad275ef86500cd8799c7e625787f814818ad4205 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 17:26:06 +0300 Subject: [PATCH 11/98] Finish delivery form an aggregate repo test. --- .../java/org/spine3/server/stand/Given.java | 82 ++++++++++++++++++- .../server/stand/StandFunnelShould.java | 44 ++++++---- 2 files changed, 108 insertions(+), 18 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/Given.java b/server/src/test/java/org/spine3/server/stand/Given.java index c7da6d078b9..595397ce31c 100644 --- a/server/src/test/java/org/spine3/server/stand/Given.java +++ b/server/src/test/java/org/spine3/server/stand/Given.java @@ -20,22 +20,34 @@ package org.spine3.server.stand; +import com.google.protobuf.Any; import com.google.protobuf.Message; +import org.spine3.base.Command; import org.spine3.base.CommandContext; import org.spine3.base.Event; import org.spine3.base.EventContext; import org.spine3.base.EventId; +import org.spine3.base.FailureThrowable; import org.spine3.base.Identifiers; import org.spine3.protobuf.AnyPacker; import org.spine3.protobuf.TypeUrl; import org.spine3.server.BoundedContext; +import org.spine3.server.aggregate.Aggregate; +import org.spine3.server.aggregate.AggregateRepository; +import org.spine3.server.aggregate.Apply; +import org.spine3.server.command.Assign; import org.spine3.server.event.Subscribe; import org.spine3.server.projection.Projection; import org.spine3.server.projection.ProjectionRepository; +import org.spine3.server.storage.memory.InMemoryStorageFactory; import org.spine3.test.projection.Project; import org.spine3.test.projection.ProjectId; +import org.spine3.test.projection.command.CreateProject; import org.spine3.test.projection.event.ProjectCreated; +import java.util.List; +import java.util.concurrent.Executor; + /** * @author Dmytro Dashenkov */ @@ -46,7 +58,7 @@ private Given() { } - /*package*/static class StandTestProjectionRepository extends ProjectionRepository { + /*package*/ static class StandTestProjectionRepository extends ProjectionRepository { /*package*/ StandTestProjectionRepository(BoundedContext boundedContext) { super(boundedContext); } @@ -57,6 +69,48 @@ protected ProjectId getEntityId(Message event, EventContext context) { } } + /*package*/ static class StandTestAggregateRepository extends AggregateRepository { + + /** + * Creates a new repository instance. + * + * @param boundedContext the bounded context to which this repository belongs + */ + /*package*/ StandTestAggregateRepository(BoundedContext boundedContext) { + super(boundedContext); + } + } + + /*package*/ static class TestFailure extends FailureThrowable { + + /*package*/ TestFailure() { + super(/*generatedMessage=*/null); + } + } + + private static class StandTestAggregate extends Aggregate { + + /** + * Creates a new aggregate instance. + * + * @param id the ID for the new aggregate + * @throws IllegalArgumentException if the ID is not of one of the supported types + */ + public StandTestAggregate(ProjectId id) { + super(id); + } + + @Assign + public List handle(CreateProject createProject, CommandContext context) { + return null; + } + + @Apply + public void handle(ProjectCreated event) { + // Do nothing + } + } + private static class StandTestProjection extends Projection { /** * Creates a new instance. @@ -76,7 +130,7 @@ public void handle(ProjectCreated event, EventContext context) { } } - /*package*/ static Event validEvent() { + /*package*/ static Event validEvent() { return Event.newBuilder() .setMessage(AnyPacker.pack(ProjectCreated.newBuilder() .setProjectId(ProjectId.newBuilder().setId("12345AD0")) @@ -93,9 +147,31 @@ public void handle(ProjectCreated event, EventContext context) { .build(); } - /*package*/ static ProjectionRepository repo(BoundedContext context) { + /*package*/ static Command validCommand() { + return Command.newBuilder() + .setMessage(AnyPacker.pack(CreateProject.getDefaultInstance())) + .setContext(CommandContext.getDefaultInstance()) + .build(); + } + + /*package*/ static ProjectionRepository projectionRepo(BoundedContext context) { return new StandTestProjectionRepository(context); } + /*package*/ static AggregateRepository aggregateRepo(BoundedContext context) { + return new StandTestAggregateRepository(context); + } + /*package*/ static BoundedContext boundedContext(Stand stand) { + return BoundedContext.newBuilder() + .setStand(stand) + .setStorageFactory(InMemoryStorageFactory.getInstance()) + .setStandFunnelExecutor(new Executor() { // Straightforward executor + @Override + public void execute(Runnable command) { + command.run(); + } + }) + .build(); + } } diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index 4c9816159f1..ad82932db72 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -26,6 +26,7 @@ import org.junit.Test; import org.mockito.ArgumentMatchers; import org.spine3.server.BoundedContext; +import org.spine3.server.aggregate.AggregateRepository; import org.spine3.server.projection.ProjectionRepository; import org.spine3.server.storage.memory.InMemoryStorageFactory; import org.spine3.testdata.TestStandFactory; @@ -161,28 +162,18 @@ public void fail_to_initialize_from_empty_builder() { // **** Integration scenarios ( -> StandFunnel -> Mock Stand) **** /** - * - Deliver updates from projection repo on update; - * - deliver updates from aggregate repo on update; + * - Deliver updates from projection projectionRepo on update; + * - deliver updates from aggregate projectionRepo on update; * - deliver the updates from several projection and aggregate repositories. */ @Test - public void deliver_updates_from_projection_repository() throws Exception { + public void deliver_updates_from_projection_repository() { final Stand stand = mock(Stand.class); - final BoundedContext boundedContext = spy(BoundedContext.newBuilder() - .setStand(stand) - .setStorageFactory(InMemoryStorageFactory.getInstance()) - .setStandFunnelExecutor(new Executor() { // Straightforward executor - @Override - public void execute(Runnable command) { - command.run(); - } - }) - .build()); + final BoundedContext boundedContext = spy(Given.boundedContext(stand)); // Init repository - final ProjectionRepository repository = Given.repo(boundedContext); + final ProjectionRepository repository = Given.projectionRepo(boundedContext); - stand.registerTypeSupplier(repository); repository.initStorage(InMemoryStorageFactory.getInstance()); repository.setOnline(); @@ -194,6 +185,29 @@ public void execute(Runnable command) { verify(stand).update(ArgumentMatchers.any(), any(Any.class)); } + @Test + public void deliver_updates_form_aggregate_repository() { + final Stand stand = mock(Stand.class); + + final BoundedContext boundedContext = spy(Given.boundedContext(stand)); + // Init repository + final AggregateRepository repository = Given.aggregateRepo(boundedContext); + + stand.registerTypeSupplier(repository); + repository.initStorage(InMemoryStorageFactory.getInstance()); + + // Dispatch an update from projection projectionRepo + try { + repository.dispatch(Given.validCommand()); + } catch (IllegalStateException e) { + // Handle null event dispatch after command handling. + Assert.assertTrue(e.getMessage().contains("No record found for command ID: EMPTY")); + } + + // Was called ONCE + verify(boundedContext).getStandFunnel(); + verify(stand).update(ArgumentMatchers.any(), any(Any.class)); + } @SuppressWarnings("MethodWithMultipleLoops") @Test From c280b88a2f7aa63bfc9d603d3ab5f93f7e263914 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 17:27:38 +0300 Subject: [PATCH 12/98] Delete redundant "TestFailure" declaration. --- server/src/test/java/org/spine3/server/stand/Given.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/Given.java b/server/src/test/java/org/spine3/server/stand/Given.java index 595397ce31c..18b75d76805 100644 --- a/server/src/test/java/org/spine3/server/stand/Given.java +++ b/server/src/test/java/org/spine3/server/stand/Given.java @@ -27,7 +27,6 @@ import org.spine3.base.Event; import org.spine3.base.EventContext; import org.spine3.base.EventId; -import org.spine3.base.FailureThrowable; import org.spine3.base.Identifiers; import org.spine3.protobuf.AnyPacker; import org.spine3.protobuf.TypeUrl; @@ -81,13 +80,6 @@ protected ProjectId getEntityId(Message event, EventContext context) { } } - /*package*/ static class TestFailure extends FailureThrowable { - - /*package*/ TestFailure() { - super(/*generatedMessage=*/null); - } - } - private static class StandTestAggregate extends Aggregate { /** From 04a211a29e0667546f483ad479740eecc1452f3a Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 17:40:54 +0300 Subject: [PATCH 13/98] Extract common test operations. --- .../server/stand/StandFunnelShould.java | 67 ++++++++++++------- 1 file changed, 42 insertions(+), 25 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index ad82932db72..3cac4e4acb7 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -39,6 +39,7 @@ import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; +import static com.google.common.base.Preconditions.checkNotNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; @@ -169,41 +170,53 @@ public void fail_to_initialize_from_empty_builder() { @Test public void deliver_updates_from_projection_repository() { - final Stand stand = mock(Stand.class); - final BoundedContext boundedContext = spy(Given.boundedContext(stand)); - // Init repository - final ProjectionRepository repository = Given.projectionRepo(boundedContext); - - repository.initStorage(InMemoryStorageFactory.getInstance()); - repository.setOnline(); + deliverUpdatesTest(new BoundedContextAction() { + @Override + public void perform(BoundedContext context) { + // Init repository + final ProjectionRepository repository = Given.projectionRepo(context); - // Dispatch an update from projection repo - repository.dispatch(Given.validEvent()); + repository.initStorage(InMemoryStorageFactory.getInstance()); + repository.setOnline(); - // Was called ONCE - verify(boundedContext).getStandFunnel(); - verify(stand).update(ArgumentMatchers.any(), any(Any.class)); + // Dispatch an update from projection repo + repository.dispatch(Given.validEvent()); + } + }); } @Test public void deliver_updates_form_aggregate_repository() { - final Stand stand = mock(Stand.class); + deliverUpdatesTest(new BoundedContextAction() { + @Override + public void perform(BoundedContext context) { + // Init repository + final AggregateRepository repository = Given.aggregateRepo(context); + + repository.initStorage(InMemoryStorageFactory.getInstance()); + try { + repository.dispatch(Given.validCommand()); + } catch (IllegalStateException e) { + // Handle null event dispatch after command handling. + Assert.assertTrue(e.getMessage().contains("No record found for command ID: EMPTY")); + } + + } + }); + } + + private static void deliverUpdatesTest(BoundedContextAction... dispatchActions) { + checkNotNull(dispatchActions); + + final Stand stand = mock(Stand.class); final BoundedContext boundedContext = spy(Given.boundedContext(stand)); - // Init repository - final AggregateRepository repository = Given.aggregateRepo(boundedContext); - - stand.registerTypeSupplier(repository); - repository.initStorage(InMemoryStorageFactory.getInstance()); - - // Dispatch an update from projection projectionRepo - try { - repository.dispatch(Given.validCommand()); - } catch (IllegalStateException e) { - // Handle null event dispatch after command handling. - Assert.assertTrue(e.getMessage().contains("No record found for command ID: EMPTY")); + + for (BoundedContextAction dispatchAction : dispatchActions) { + dispatchAction.perform(boundedContext); } + // Was called ONCE verify(boundedContext).getStandFunnel(); verify(stand).update(ArgumentMatchers.any(), any(Any.class)); @@ -250,4 +263,8 @@ public void run() { } + private interface BoundedContextAction { + void perform(BoundedContext context); + } + } From 0ef157de416bc945e5a04758bc3c9321ba5fe36a Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 18:21:34 +0300 Subject: [PATCH 14/98] Refactor and add delivery tests for several repositories successively and in parallel. --- .../java/org/spine3/server/stand/Given.java | 29 ++++-- .../server/stand/StandFunnelShould.java | 89 +++++++++++++------ 2 files changed, 82 insertions(+), 36 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/Given.java b/server/src/test/java/org/spine3/server/stand/Given.java index 18b75d76805..ccad28106a2 100644 --- a/server/src/test/java/org/spine3/server/stand/Given.java +++ b/server/src/test/java/org/spine3/server/stand/Given.java @@ -46,12 +46,16 @@ import java.util.List; import java.util.concurrent.Executor; +import java.util.concurrent.Executors; /** * @author Dmytro Dashenkov */ /*package*/ class Given { + /*package*/ static final int THREADS_COUNT_IN_POOL_EXECUTOR = 10; + /*package*/ static final int SEVERAL = THREADS_COUNT_IN_POOL_EXECUTOR; + private static final String PROJECT_UUID = Identifiers.newUuid(); private Given() { @@ -154,16 +158,23 @@ public void handle(ProjectCreated event, EventContext context) { return new StandTestAggregateRepository(context); } - /*package*/ static BoundedContext boundedContext(Stand stand) { + /*package*/ static BoundedContext boundedContext(Stand stand, int concurrentThreads) { + final Executor executor = concurrentThreads > 0 ? Executors.newFixedThreadPool(concurrentThreads) : + new Executor() { // Straightforward executor + @Override + public void execute(Runnable command) { + command.run(); + } + }; + + return boundedContextBuilder(stand) + .setStandFunnelExecutor(executor) + .build(); + } + + private static BoundedContext.Builder boundedContextBuilder(Stand stand) { return BoundedContext.newBuilder() .setStand(stand) - .setStorageFactory(InMemoryStorageFactory.getInstance()) - .setStandFunnelExecutor(new Executor() { // Straightforward executor - @Override - public void execute(Runnable command) { - command.run(); - } - }) - .build(); + .setStorageFactory(InMemoryStorageFactory.getInstance()); } } diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index 3cac4e4acb7..486697b153e 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -31,7 +31,9 @@ import org.spine3.server.storage.memory.InMemoryStorageFactory; import org.spine3.testdata.TestStandFactory; +import java.security.SecureRandom; import java.util.Map; +import java.util.Random; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; @@ -44,6 +46,7 @@ import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; /** @@ -170,24 +173,55 @@ public void fail_to_initialize_from_empty_builder() { @Test public void deliver_updates_from_projection_repository() { - deliverUpdatesTest(new BoundedContextAction() { - @Override - public void perform(BoundedContext context) { - // Init repository - final ProjectionRepository repository = Given.projectionRepo(context); + deliverUpdates(false, projectionRepositoryDispatch()); + } - repository.initStorage(InMemoryStorageFactory.getInstance()); - repository.setOnline(); + @Test + public void deliver_updates_form_aggregate_repository() { + deliverUpdates(false, aggregateRepositoryDispatch()); + } - // Dispatch an update from projection repo - repository.dispatch(Given.validEvent()); - } - }); + @Test + public void deliver_updates_from_several_repositories_in_single_thread() { + deliverUpdates(false, getSeveralRepositoryDispatchCalls()); } @Test - public void deliver_updates_form_aggregate_repository() { - deliverUpdatesTest(new BoundedContextAction() { + public void deliver_updates_from_several_repositories_in_multiple_threads() { + deliverUpdates(true, getSeveralRepositoryDispatchCalls()); + } + + private static BoundedContextAction[] getSeveralRepositoryDispatchCalls() { + final BoundedContextAction[] result = new BoundedContextAction[Given.SEVERAL]; + final Random random = new SecureRandom(); + + for (int i = 0; i < result.length; i++) { + result[i] = random.nextBoolean() ? aggregateRepositoryDispatch() : projectionRepositoryDispatch(); + } + + return result; + } + + private static void deliverUpdates(boolean isMultiThread, BoundedContextAction... dispatchActions) { + checkNotNull(dispatchActions); + + final Stand stand = mock(Stand.class); + final BoundedContext boundedContext = spy(Given.boundedContext(stand, + isMultiThread ? + Given.THREADS_COUNT_IN_POOL_EXECUTOR : 0)); + + for (BoundedContextAction dispatchAction : dispatchActions) { + dispatchAction.perform(boundedContext); + } + + + // Was called ONCE + verify(boundedContext, times(dispatchActions.length)).getStandFunnel(); + verify(stand, times(dispatchActions.length)).update(ArgumentMatchers.any(), any(Any.class)); + } + + private static BoundedContextAction aggregateRepositoryDispatch() { + return new BoundedContextAction() { @Override public void perform(BoundedContext context) { // Init repository @@ -201,31 +235,32 @@ public void perform(BoundedContext context) { // Handle null event dispatch after command handling. Assert.assertTrue(e.getMessage().contains("No record found for command ID: EMPTY")); } - } - }); + }; } - private static void deliverUpdatesTest(BoundedContextAction... dispatchActions) { - checkNotNull(dispatchActions); + private static BoundedContextAction projectionRepositoryDispatch() { + return new BoundedContextAction() { + @Override + public void perform(BoundedContext context) { + // Init repository + final ProjectionRepository repository = Given.projectionRepo(context); - final Stand stand = mock(Stand.class); - final BoundedContext boundedContext = spy(Given.boundedContext(stand)); + repository.initStorage(InMemoryStorageFactory.getInstance()); + repository.setOnline(); - for (BoundedContextAction dispatchAction : dispatchActions) { - dispatchAction.perform(boundedContext); - } + // Dispatch an update from projection repo + repository.dispatch(Given.validEvent()); + } + }; + } - // Was called ONCE - verify(boundedContext).getStandFunnel(); - verify(stand).update(ArgumentMatchers.any(), any(Any.class)); - } @SuppressWarnings("MethodWithMultipleLoops") @Test public void deliver_updates_through_several_threads() throws InterruptedException { - final int threadsCount = 10; + final int threadsCount = Given.THREADS_COUNT_IN_POOL_EXECUTOR; @SuppressWarnings("LocalVariableNamingConvention") // Too long variable name final int threadExecutionMaxAwaitSeconds = 2; From cfa0535f6842a47fa25a5fe7a979398bfe929eec Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Tue, 13 Sep 2016 14:10:41 +0300 Subject: [PATCH 15/98] Add creation test covering various Builder params. --- .../server/stand/StandFunnelShould.java | 36 ++++++++++++++++++- .../org/spine3/testdata/TestStandFactory.java | 2 +- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index 9e42f37e68e..5eb8fb42bdf 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -27,6 +27,8 @@ import org.spine3.testdata.TestStandFactory; import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.spy; @@ -40,7 +42,6 @@ public class StandFunnelShould { // **** Positive scenarios (unit) **** /** - * - Initialize properly with various Builder options; * - deliver mock updates to the stand (invoke proper methods with particular arguments) - test the delivery only. */ @@ -53,6 +54,39 @@ public void initialize_properly_with_stand_only() { Assert.assertNotNull(standFunnel); } + @Test + public void initialize_properly_with_various_builder_options() { + final Stand stand = TestStandFactory.create(); + final Executor executor = Executors.newSingleThreadExecutor(new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + return Thread.currentThread(); + } + }); + + final StandFunnel blockingFunnel = StandFunnel.newBuilder() + .setStand(stand) + .setExecutor(executor) + .build(); + Assert.assertNotNull(blockingFunnel); + + final StandFunnel funnelForBusyStand = StandFunnel.newBuilder() + .setStand(stand) + .setExecutor(Executors.newSingleThreadExecutor()) + .build(); + Assert.assertNotNull(funnelForBusyStand); + + + final StandFunnel emptyExecutorFunnel = StandFunnel.newBuilder() + .setStand(TestStandFactory.create()) + .setExecutor(new Executor() { + @Override + public void execute(Runnable neverCalled) { } + }) + .build(); + Assert.assertNotNull(emptyExecutorFunnel); + } + @Test public void use_executor_from_builder() { diff --git a/server/src/test/java/org/spine3/testdata/TestStandFactory.java b/server/src/test/java/org/spine3/testdata/TestStandFactory.java index d54df4fae6d..474a6226b30 100644 --- a/server/src/test/java/org/spine3/testdata/TestStandFactory.java +++ b/server/src/test/java/org/spine3/testdata/TestStandFactory.java @@ -22,13 +22,13 @@ package org.spine3.testdata; import org.spine3.server.stand.Stand; -import org.spine3.server.storage.memory.InMemoryStandStorage; /** * Creates stands for tests. * * @author Alex Tymchenko */ +@SuppressWarnings("UtilityClass") public class TestStandFactory { private TestStandFactory() {} From 000f360e5ef50413b3309c35b604704e889da921 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Tue, 13 Sep 2016 14:11:56 +0300 Subject: [PATCH 16/98] Bump gradle version, --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 58e7b927f5d..6d19c1542b6 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-3.0-bin.zip From b33eca7ced546c891309e2632adc651129da3c57 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Tue, 13 Sep 2016 14:37:55 +0300 Subject: [PATCH 17/98] Add some more tests (need to be processed). --- .../org/spine3/server/stand/StandFunnel.java | 2 ++ .../server/stand/StandFunnelShould.java | 35 +++++++++++++++++-- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/server/src/main/java/org/spine3/server/stand/StandFunnel.java b/server/src/main/java/org/spine3/server/stand/StandFunnel.java index 9e3cc659ce0..44997adbdf8 100644 --- a/server/src/main/java/org/spine3/server/stand/StandFunnel.java +++ b/server/src/main/java/org/spine3/server/stand/StandFunnel.java @@ -109,6 +109,7 @@ public Stand getStand() { *

The value must not be null. * *

If this method is not used, a default value will be used. + * // TODO:13-09-16:dmytro.dashenkov: Correct docs. No default value for Stand is used, null value leads to a fail instead. * * @param stand the instance of {@link Stand}. * @return {@code this} instance of {@code Builder} @@ -122,6 +123,7 @@ public Executor getExecutor() { return executor; } + // TODO:13-09-16:dmytro.dashenkov: Complete docs. Here is the place where a default value is used in case of not using the method. /** * Set the {@code Executor} instance for this {@code StandFunnel}. * diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index 5eb8fb42bdf..da5f2bffdfd 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -87,6 +87,21 @@ public void execute(Runnable neverCalled) { } Assert.assertNotNull(emptyExecutorFunnel); } + @Test + public void deliver_mock_updates_to_stand() { + final Stand stand = spy(TestStandFactory.create()); + + final StandFunnel funnel = StandFunnel.newBuilder() + .setStand(stand) + .build(); + + final Object id = new Object(); + final Any state = Any.getDefaultInstance(); + funnel.post(id, state); + + verify(stand).update(id, state); + } + @Test public void use_executor_from_builder() { @@ -114,9 +129,23 @@ public void execute(Runnable command) { // **** Negative scenarios (unit) **** - /** - * - Fail to initialise with improper stand. - */ + @SuppressWarnings("ResultOfMethodCallIgnored") + @Test(expected = NullPointerException.class) + public void fail_to_initialize_with_null_stand() { + @SuppressWarnings("ConstantConditions") + final StandFunnel.Builder builder = StandFunnel.newBuilder().setStand(null); + + builder.build(); + } + + @SuppressWarnings("ResultOfMethodCallIgnored") + @Test(expected = NullPointerException.class) + public void fail_to_initialize_with_importer_stand() { + // TODO:13-09-16:dmytro.dashenkov: Implement. +// @SuppressWarnings("ConstantConditions") +// final StandFunnel.Builder builder = StandFunnel.newBuilder().setStand(null); +// builder.build(); + } @SuppressWarnings("ResultOfMethodCallIgnored") @Test(expected = IllegalStateException.class) From 88d48a7a02495a36140bb39624190d3b95cc4fee Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Tue, 13 Sep 2016 15:16:06 +0300 Subject: [PATCH 18/98] Fix deliver mock updates test. --- .../server/stand/StandFunnelShould.java | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index da5f2bffdfd..ef7ba6c2b08 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -31,6 +31,8 @@ import java.util.concurrent.ThreadFactory; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -89,14 +91,16 @@ public void execute(Runnable neverCalled) { } @Test public void deliver_mock_updates_to_stand() { - final Stand stand = spy(TestStandFactory.create()); + final Object id = new Object(); + final Any state = Any.getDefaultInstance(); + + final Stand stand = mock(Stand.class); + doNothing().when(stand).update(id, state); final StandFunnel funnel = StandFunnel.newBuilder() .setStand(stand) .build(); - final Object id = new Object(); - final Any state = Any.getDefaultInstance(); funnel.post(id, state); verify(stand).update(id, state); @@ -131,22 +135,13 @@ public void execute(Runnable command) { @SuppressWarnings("ResultOfMethodCallIgnored") @Test(expected = NullPointerException.class) - public void fail_to_initialize_with_null_stand() { + public void fail_to_initialize_with_inproper_stand() { @SuppressWarnings("ConstantConditions") final StandFunnel.Builder builder = StandFunnel.newBuilder().setStand(null); builder.build(); } - @SuppressWarnings("ResultOfMethodCallIgnored") - @Test(expected = NullPointerException.class) - public void fail_to_initialize_with_importer_stand() { - // TODO:13-09-16:dmytro.dashenkov: Implement. -// @SuppressWarnings("ConstantConditions") -// final StandFunnel.Builder builder = StandFunnel.newBuilder().setStand(null); -// builder.build(); - } - @SuppressWarnings("ResultOfMethodCallIgnored") @Test(expected = IllegalStateException.class) public void fail_to_initialize_from_empty_builder() { @@ -162,5 +157,10 @@ public void fail_to_initialize_from_empty_builder() { * - deliver the updates from several projection and aggregate repositories. */ + @Test + public void deliver_updates_from_projection_repo() { + + } + } From 1efce72641cea63b3513941dd0d21f6ac9b42f1a Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 13:57:38 +0300 Subject: [PATCH 19/98] Explicit "Given" for stand tests. --- .../java/org/spine3/server/stand/Given.java | 67 +++++++++++++++++++ .../org/spine3/server/stand/StandShould.java | 21 +----- 2 files changed, 68 insertions(+), 20 deletions(-) create mode 100644 server/src/test/java/org/spine3/server/stand/Given.java diff --git a/server/src/test/java/org/spine3/server/stand/Given.java b/server/src/test/java/org/spine3/server/stand/Given.java new file mode 100644 index 00000000000..f1b54d0adfe --- /dev/null +++ b/server/src/test/java/org/spine3/server/stand/Given.java @@ -0,0 +1,67 @@ +/* + * Copyright 2016, TeamDev Ltd. All rights reserved. + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.spine3.server.stand; + +import org.spine3.base.Event; +import org.spine3.base.EventContext; +import org.spine3.protobuf.AnyPacker; +import org.spine3.server.BoundedContext; +import org.spine3.server.projection.Projection; +import org.spine3.server.projection.ProjectionRepository; +import org.spine3.test.projection.Project; +import org.spine3.test.projection.ProjectId; +import org.spine3.test.projection.event.ProjectCreated; + +/** + * @author Dmytro Dashenkov + */ +/*package*/ class Given { + + /*package*/static class StandTestProjectionRepository extends ProjectionRepository { + /*package*/ StandTestProjectionRepository(BoundedContext boundedContext) { + super(boundedContext); + } + } + + private static class StandTestProjection extends Projection { + /** + * Creates a new instance. + * + * Required to be public. + * + * @param id the ID for the new instance + * @throws IllegalArgumentException if the ID is not of one of the supported types + */ + public StandTestProjection(ProjectId id) { + super(id); + } + } + + /*package*/ static Event validEvent() { + return Event.newBuilder() + .setMessage(AnyPacker.pack(ProjectCreated.newBuilder() + .setProjectId(ProjectId.newBuilder().setId("12345AD0")) + .build()) + .toBuilder().setTypeUrl(ProjectCreated.getDescriptor().getFullName()).build()) + .setContext(EventContext.getDefaultInstance()) + .build(); + } +} diff --git a/server/src/test/java/org/spine3/server/stand/StandShould.java b/server/src/test/java/org/spine3/server/stand/StandShould.java index 7880fe510a9..5884a55cb1f 100644 --- a/server/src/test/java/org/spine3/server/stand/StandShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandShould.java @@ -44,6 +44,7 @@ import org.spine3.server.projection.Projection; import org.spine3.server.projection.ProjectionRepository; import org.spine3.server.storage.EntityStorageRecord; +import org.spine3.server.stand.Given.StandTestProjectionRepository; import org.spine3.server.storage.StandStorage; import org.spine3.test.clientservice.customer.Customer; import org.spine3.test.clientservice.customer.CustomerId; @@ -347,26 +348,6 @@ public void onEntityStateUpdate(Any newEntityState) { // ***** Inner classes used for tests. ***** - private static class StandTestProjection extends Projection { - /** - * Creates a new instance. - * - * @param id the ID for the new instance - * @throws IllegalArgumentException if the ID is not of one of the supported types - */ - public StandTestProjection(ProjectId id) { - super(id); - } - } - - - private static class StandTestProjectionRepository extends ProjectionRepository { - protected StandTestProjectionRepository(BoundedContext boundedContext) { - super(boundedContext); - } - } - - /** * A {@link StreamObserver} storing the state of {@link Query} execution. */ From cddf64c4ea3f100732590bc349d9a3bd7abcd53a Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 14:14:40 +0300 Subject: [PATCH 20/98] Add threading test. --- .../server/stand/StandFunnelShould.java | 44 ++++++++++++++++++- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index ef7ba6c2b08..899afd85415 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -24,11 +24,16 @@ import com.google.protobuf.Any; import org.junit.Assert; import org.junit.Test; +import org.mockito.ArgumentMatchers; import org.spine3.testdata.TestStandFactory; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doNothing; @@ -38,6 +43,7 @@ /** * @author Alex Tymchenko + * @author Dmytro Dashenkov */ public class StandFunnelShould { @@ -157,10 +163,44 @@ public void fail_to_initialize_from_empty_builder() { * - deliver the updates from several projection and aggregate repositories. */ + + @SuppressWarnings("MethodWithMultipleLoops") @Test - public void deliver_updates_from_projection_repo() { + public void deliver_updates_through_several_threads() throws InterruptedException { + final int threadsCount = 10; - } + final Map threadInvakationRegistry = new ConcurrentHashMap<>(threadsCount); + + final Stand stand = mock(Stand.class); + doNothing().when(stand).update(ArgumentMatchers.any(), any(Any.class)); + + final StandFunnel standFunnel = StandFunnel.newBuilder() + .setStand(stand) + .build(); + + final ExecutorService processes = Executors.newFixedThreadPool(threadsCount); + + + final Runnable task = new Runnable() { + @Override + public void run() { + final String threadName = Thread.currentThread().getName(); + Assert.assertFalse(threadInvakationRegistry.containsKey(threadName)); + + standFunnel.post(new Object(), Any.getDefaultInstance()); + + threadInvakationRegistry.put(threadName, new Object()); + } + }; + for (int i = 0; i < threadsCount; i++) { + processes.execute(task); + } + + processes.awaitTermination(10, TimeUnit.SECONDS); + + Assert.assertEquals(threadInvakationRegistry.size(), threadsCount); + + } } From 935f7a922f3ef9630172c752434df1e50565e30c Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 14:16:54 +0300 Subject: [PATCH 21/98] Reduce executor await time. --- .../test/java/org/spine3/server/stand/StandFunnelShould.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index 899afd85415..f47fa5efb48 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -168,6 +168,7 @@ public void fail_to_initialize_from_empty_builder() { @Test public void deliver_updates_through_several_threads() throws InterruptedException { final int threadsCount = 10; + final int threadExecuteionMaxAwaitSeconds = 2; final Map threadInvakationRegistry = new ConcurrentHashMap<>(threadsCount); @@ -197,7 +198,7 @@ public void run() { processes.execute(task); } - processes.awaitTermination(10, TimeUnit.SECONDS); + processes.awaitTermination(threadExecuteionMaxAwaitSeconds, TimeUnit.SECONDS); Assert.assertEquals(threadInvakationRegistry.size(), threadsCount); From 3f90a6f1a10cd33287c6c0eea3eac389bf26c2c7 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 14:54:11 +0300 Subject: [PATCH 22/98] Add deliver updates form projection repo test skeleton, --- .../java/org/spine3/server/stand/Given.java | 6 ++- .../server/stand/StandFunnelShould.java | 45 ++++++++++++++++--- 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/Given.java b/server/src/test/java/org/spine3/server/stand/Given.java index f1b54d0adfe..2f631d4995f 100644 --- a/server/src/test/java/org/spine3/server/stand/Given.java +++ b/server/src/test/java/org/spine3/server/stand/Given.java @@ -23,6 +23,7 @@ import org.spine3.base.Event; import org.spine3.base.EventContext; import org.spine3.protobuf.AnyPacker; +import org.spine3.protobuf.TypeUrl; import org.spine3.server.BoundedContext; import org.spine3.server.projection.Projection; import org.spine3.server.projection.ProjectionRepository; @@ -60,7 +61,10 @@ public StandTestProjection(ProjectId id) { .setMessage(AnyPacker.pack(ProjectCreated.newBuilder() .setProjectId(ProjectId.newBuilder().setId("12345AD0")) .build()) - .toBuilder().setTypeUrl(ProjectCreated.getDescriptor().getFullName()).build()) + .toBuilder() + .setTypeUrl(TypeUrl.SPINE_TYPE_URL_PREFIX + '/' + + ProjectCreated.getDescriptor().getFullName()) + .build()) .setContext(EventContext.getDefaultInstance()) .build(); } diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index f47fa5efb48..34e7ab2ff0f 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -25,6 +25,8 @@ import org.junit.Assert; import org.junit.Test; import org.mockito.ArgumentMatchers; +import org.spine3.server.BoundedContext; +import org.spine3.server.storage.memory.InMemoryStorageFactory; import org.spine3.testdata.TestStandFactory; import java.util.Map; @@ -40,6 +42,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; /** * @author Alex Tymchenko @@ -163,14 +166,44 @@ public void fail_to_initialize_from_empty_builder() { * - deliver the updates from several projection and aggregate repositories. */ + @Test + public void deliver_updates_from_projection_repository() throws Exception { + final BoundedContext boundedContext = mock(BoundedContext.class); + final Stand stand = mock(Stand.class); + when(boundedContext.getStandFunnel()).thenReturn(StandFunnel.newBuilder() + .setStand(stand) + .setExecutor(new Executor() { + @Override + public void execute(Runnable command) { + command.run(); + } + }) + .build()); + + // Init repository + final Given.StandTestProjectionRepository repository = new Given.StandTestProjectionRepository(boundedContext); + + stand.registerTypeSupplier(repository); + repository.initStorage(InMemoryStorageFactory.getInstance()); + repository.setOnline(); + + // Dispatch an update from projection repo + repository.dispatch(Given.validEvent()); + + // Was called ONCE + verify(boundedContext).getStandFunnel(); + verify(stand).update(ArgumentMatchers.any(), any(Any.class)); + } + @SuppressWarnings("MethodWithMultipleLoops") @Test public void deliver_updates_through_several_threads() throws InterruptedException { final int threadsCount = 10; - final int threadExecuteionMaxAwaitSeconds = 2; + @SuppressWarnings("LocalVariableNamingConvention") // Too long variable name + final int threadExecutionMaxAwaitSeconds = 2; - final Map threadInvakationRegistry = new ConcurrentHashMap<>(threadsCount); + final Map threadInvocationRegistry = new ConcurrentHashMap<>(threadsCount); final Stand stand = mock(Stand.class); doNothing().when(stand).update(ArgumentMatchers.any(), any(Any.class)); @@ -186,11 +219,11 @@ public void deliver_updates_through_several_threads() throws InterruptedExceptio @Override public void run() { final String threadName = Thread.currentThread().getName(); - Assert.assertFalse(threadInvakationRegistry.containsKey(threadName)); + Assert.assertFalse(threadInvocationRegistry.containsKey(threadName)); standFunnel.post(new Object(), Any.getDefaultInstance()); - threadInvakationRegistry.put(threadName, new Object()); + threadInvocationRegistry.put(threadName, new Object()); } }; @@ -198,9 +231,9 @@ public void run() { processes.execute(task); } - processes.awaitTermination(threadExecuteionMaxAwaitSeconds, TimeUnit.SECONDS); + processes.awaitTermination(threadExecutionMaxAwaitSeconds, TimeUnit.SECONDS); - Assert.assertEquals(threadInvakationRegistry.size(), threadsCount); + Assert.assertEquals(threadInvocationRegistry.size(), threadsCount); } From 68c908bf6e4b32f19caa3050fa03c0a95f37d5cd Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 14:57:41 +0300 Subject: [PATCH 23/98] Suppress not yet ready test. --- .../test/java/org/spine3/server/stand/StandFunnelShould.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index 34e7ab2ff0f..c924469b833 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -166,7 +166,7 @@ public void fail_to_initialize_from_empty_builder() { * - deliver the updates from several projection and aggregate repositories. */ - @Test + //@Test public void deliver_updates_from_projection_repository() throws Exception { final BoundedContext boundedContext = mock(BoundedContext.class); final Stand stand = mock(Stand.class); From 3b6601c8b9faca2e6b5bb8119648b0c6950f0a29 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 16:09:19 +0300 Subject: [PATCH 24/98] Finish delivery from projection repo test. --- .../java/org/spine3/server/stand/Given.java | 36 +++++++++++++++++-- .../server/stand/StandFunnelShould.java | 27 +++++++------- 2 files changed, 46 insertions(+), 17 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/Given.java b/server/src/test/java/org/spine3/server/stand/Given.java index 2f631d4995f..c7da6d078b9 100644 --- a/server/src/test/java/org/spine3/server/stand/Given.java +++ b/server/src/test/java/org/spine3/server/stand/Given.java @@ -20,11 +20,16 @@ package org.spine3.server.stand; +import com.google.protobuf.Message; +import org.spine3.base.CommandContext; import org.spine3.base.Event; import org.spine3.base.EventContext; +import org.spine3.base.EventId; +import org.spine3.base.Identifiers; import org.spine3.protobuf.AnyPacker; import org.spine3.protobuf.TypeUrl; import org.spine3.server.BoundedContext; +import org.spine3.server.event.Subscribe; import org.spine3.server.projection.Projection; import org.spine3.server.projection.ProjectionRepository; import org.spine3.test.projection.Project; @@ -36,10 +41,20 @@ */ /*package*/ class Given { + private static final String PROJECT_UUID = Identifiers.newUuid(); + + private Given() { + } + /*package*/static class StandTestProjectionRepository extends ProjectionRepository { /*package*/ StandTestProjectionRepository(BoundedContext boundedContext) { super(boundedContext); } + + @Override + protected ProjectId getEntityId(Message event, EventContext context) { + return ProjectId.newBuilder().setId(PROJECT_UUID).build(); + } } private static class StandTestProjection extends Projection { @@ -54,6 +69,11 @@ private static class StandTestProjection extends Projection public StandTestProjection(ProjectId id) { super(id); } + + @Subscribe + public void handle(ProjectCreated event, EventContext context) { + // Do nothing + } } /*package*/ static Event validEvent() { @@ -62,10 +82,20 @@ public StandTestProjection(ProjectId id) { .setProjectId(ProjectId.newBuilder().setId("12345AD0")) .build()) .toBuilder() - .setTypeUrl(TypeUrl.SPINE_TYPE_URL_PREFIX + '/' + - ProjectCreated.getDescriptor().getFullName()) + .setTypeUrl(TypeUrl.SPINE_TYPE_URL_PREFIX + "/" + ProjectCreated.getDescriptor().getFullName()) .build()) - .setContext(EventContext.getDefaultInstance()) + .setContext(EventContext.newBuilder() + .setDoNotEnrich(true) + .setCommandContext(CommandContext.getDefaultInstance()) + .setEventId(EventId.newBuilder() + .setUuid(PROJECT_UUID) + .build())) .build(); } + + /*package*/ static ProjectionRepository repo(BoundedContext context) { + return new StandTestProjectionRepository(context); + } + + } diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index c924469b833..4c9816159f1 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -26,6 +26,7 @@ import org.junit.Test; import org.mockito.ArgumentMatchers; import org.spine3.server.BoundedContext; +import org.spine3.server.projection.ProjectionRepository; import org.spine3.server.storage.memory.InMemoryStorageFactory; import org.spine3.testdata.TestStandFactory; @@ -42,7 +43,6 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; /** * @author Alex Tymchenko @@ -166,22 +166,21 @@ public void fail_to_initialize_from_empty_builder() { * - deliver the updates from several projection and aggregate repositories. */ - //@Test + @Test public void deliver_updates_from_projection_repository() throws Exception { - final BoundedContext boundedContext = mock(BoundedContext.class); final Stand stand = mock(Stand.class); - when(boundedContext.getStandFunnel()).thenReturn(StandFunnel.newBuilder() - .setStand(stand) - .setExecutor(new Executor() { - @Override - public void execute(Runnable command) { - command.run(); - } - }) - .build()); - + final BoundedContext boundedContext = spy(BoundedContext.newBuilder() + .setStand(stand) + .setStorageFactory(InMemoryStorageFactory.getInstance()) + .setStandFunnelExecutor(new Executor() { // Straightforward executor + @Override + public void execute(Runnable command) { + command.run(); + } + }) + .build()); // Init repository - final Given.StandTestProjectionRepository repository = new Given.StandTestProjectionRepository(boundedContext); + final ProjectionRepository repository = Given.repo(boundedContext); stand.registerTypeSupplier(repository); repository.initStorage(InMemoryStorageFactory.getInstance()); From cba94e5f1198ed6836b51b2419e91adb531394e1 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 17:26:06 +0300 Subject: [PATCH 25/98] Finish delivery form an aggregate repo test. --- .../java/org/spine3/server/stand/Given.java | 82 ++++++++++++++++++- .../server/stand/StandFunnelShould.java | 44 ++++++---- 2 files changed, 108 insertions(+), 18 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/Given.java b/server/src/test/java/org/spine3/server/stand/Given.java index c7da6d078b9..595397ce31c 100644 --- a/server/src/test/java/org/spine3/server/stand/Given.java +++ b/server/src/test/java/org/spine3/server/stand/Given.java @@ -20,22 +20,34 @@ package org.spine3.server.stand; +import com.google.protobuf.Any; import com.google.protobuf.Message; +import org.spine3.base.Command; import org.spine3.base.CommandContext; import org.spine3.base.Event; import org.spine3.base.EventContext; import org.spine3.base.EventId; +import org.spine3.base.FailureThrowable; import org.spine3.base.Identifiers; import org.spine3.protobuf.AnyPacker; import org.spine3.protobuf.TypeUrl; import org.spine3.server.BoundedContext; +import org.spine3.server.aggregate.Aggregate; +import org.spine3.server.aggregate.AggregateRepository; +import org.spine3.server.aggregate.Apply; +import org.spine3.server.command.Assign; import org.spine3.server.event.Subscribe; import org.spine3.server.projection.Projection; import org.spine3.server.projection.ProjectionRepository; +import org.spine3.server.storage.memory.InMemoryStorageFactory; import org.spine3.test.projection.Project; import org.spine3.test.projection.ProjectId; +import org.spine3.test.projection.command.CreateProject; import org.spine3.test.projection.event.ProjectCreated; +import java.util.List; +import java.util.concurrent.Executor; + /** * @author Dmytro Dashenkov */ @@ -46,7 +58,7 @@ private Given() { } - /*package*/static class StandTestProjectionRepository extends ProjectionRepository { + /*package*/ static class StandTestProjectionRepository extends ProjectionRepository { /*package*/ StandTestProjectionRepository(BoundedContext boundedContext) { super(boundedContext); } @@ -57,6 +69,48 @@ protected ProjectId getEntityId(Message event, EventContext context) { } } + /*package*/ static class StandTestAggregateRepository extends AggregateRepository { + + /** + * Creates a new repository instance. + * + * @param boundedContext the bounded context to which this repository belongs + */ + /*package*/ StandTestAggregateRepository(BoundedContext boundedContext) { + super(boundedContext); + } + } + + /*package*/ static class TestFailure extends FailureThrowable { + + /*package*/ TestFailure() { + super(/*generatedMessage=*/null); + } + } + + private static class StandTestAggregate extends Aggregate { + + /** + * Creates a new aggregate instance. + * + * @param id the ID for the new aggregate + * @throws IllegalArgumentException if the ID is not of one of the supported types + */ + public StandTestAggregate(ProjectId id) { + super(id); + } + + @Assign + public List handle(CreateProject createProject, CommandContext context) { + return null; + } + + @Apply + public void handle(ProjectCreated event) { + // Do nothing + } + } + private static class StandTestProjection extends Projection { /** * Creates a new instance. @@ -76,7 +130,7 @@ public void handle(ProjectCreated event, EventContext context) { } } - /*package*/ static Event validEvent() { + /*package*/ static Event validEvent() { return Event.newBuilder() .setMessage(AnyPacker.pack(ProjectCreated.newBuilder() .setProjectId(ProjectId.newBuilder().setId("12345AD0")) @@ -93,9 +147,31 @@ public void handle(ProjectCreated event, EventContext context) { .build(); } - /*package*/ static ProjectionRepository repo(BoundedContext context) { + /*package*/ static Command validCommand() { + return Command.newBuilder() + .setMessage(AnyPacker.pack(CreateProject.getDefaultInstance())) + .setContext(CommandContext.getDefaultInstance()) + .build(); + } + + /*package*/ static ProjectionRepository projectionRepo(BoundedContext context) { return new StandTestProjectionRepository(context); } + /*package*/ static AggregateRepository aggregateRepo(BoundedContext context) { + return new StandTestAggregateRepository(context); + } + /*package*/ static BoundedContext boundedContext(Stand stand) { + return BoundedContext.newBuilder() + .setStand(stand) + .setStorageFactory(InMemoryStorageFactory.getInstance()) + .setStandFunnelExecutor(new Executor() { // Straightforward executor + @Override + public void execute(Runnable command) { + command.run(); + } + }) + .build(); + } } diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index 4c9816159f1..ad82932db72 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -26,6 +26,7 @@ import org.junit.Test; import org.mockito.ArgumentMatchers; import org.spine3.server.BoundedContext; +import org.spine3.server.aggregate.AggregateRepository; import org.spine3.server.projection.ProjectionRepository; import org.spine3.server.storage.memory.InMemoryStorageFactory; import org.spine3.testdata.TestStandFactory; @@ -161,28 +162,18 @@ public void fail_to_initialize_from_empty_builder() { // **** Integration scenarios ( -> StandFunnel -> Mock Stand) **** /** - * - Deliver updates from projection repo on update; - * - deliver updates from aggregate repo on update; + * - Deliver updates from projection projectionRepo on update; + * - deliver updates from aggregate projectionRepo on update; * - deliver the updates from several projection and aggregate repositories. */ @Test - public void deliver_updates_from_projection_repository() throws Exception { + public void deliver_updates_from_projection_repository() { final Stand stand = mock(Stand.class); - final BoundedContext boundedContext = spy(BoundedContext.newBuilder() - .setStand(stand) - .setStorageFactory(InMemoryStorageFactory.getInstance()) - .setStandFunnelExecutor(new Executor() { // Straightforward executor - @Override - public void execute(Runnable command) { - command.run(); - } - }) - .build()); + final BoundedContext boundedContext = spy(Given.boundedContext(stand)); // Init repository - final ProjectionRepository repository = Given.repo(boundedContext); + final ProjectionRepository repository = Given.projectionRepo(boundedContext); - stand.registerTypeSupplier(repository); repository.initStorage(InMemoryStorageFactory.getInstance()); repository.setOnline(); @@ -194,6 +185,29 @@ public void execute(Runnable command) { verify(stand).update(ArgumentMatchers.any(), any(Any.class)); } + @Test + public void deliver_updates_form_aggregate_repository() { + final Stand stand = mock(Stand.class); + + final BoundedContext boundedContext = spy(Given.boundedContext(stand)); + // Init repository + final AggregateRepository repository = Given.aggregateRepo(boundedContext); + + stand.registerTypeSupplier(repository); + repository.initStorage(InMemoryStorageFactory.getInstance()); + + // Dispatch an update from projection projectionRepo + try { + repository.dispatch(Given.validCommand()); + } catch (IllegalStateException e) { + // Handle null event dispatch after command handling. + Assert.assertTrue(e.getMessage().contains("No record found for command ID: EMPTY")); + } + + // Was called ONCE + verify(boundedContext).getStandFunnel(); + verify(stand).update(ArgumentMatchers.any(), any(Any.class)); + } @SuppressWarnings("MethodWithMultipleLoops") @Test From 7c49973b3bdb84b15a60cd04730d625e1ed247a9 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 17:27:38 +0300 Subject: [PATCH 26/98] Delete redundant "TestFailure" declaration. --- server/src/test/java/org/spine3/server/stand/Given.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/Given.java b/server/src/test/java/org/spine3/server/stand/Given.java index 595397ce31c..18b75d76805 100644 --- a/server/src/test/java/org/spine3/server/stand/Given.java +++ b/server/src/test/java/org/spine3/server/stand/Given.java @@ -27,7 +27,6 @@ import org.spine3.base.Event; import org.spine3.base.EventContext; import org.spine3.base.EventId; -import org.spine3.base.FailureThrowable; import org.spine3.base.Identifiers; import org.spine3.protobuf.AnyPacker; import org.spine3.protobuf.TypeUrl; @@ -81,13 +80,6 @@ protected ProjectId getEntityId(Message event, EventContext context) { } } - /*package*/ static class TestFailure extends FailureThrowable { - - /*package*/ TestFailure() { - super(/*generatedMessage=*/null); - } - } - private static class StandTestAggregate extends Aggregate { /** From 85269778da7fdc66e95bf57c56a8a0f9fc960284 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 17:40:54 +0300 Subject: [PATCH 27/98] Extract common test operations. --- .../server/stand/StandFunnelShould.java | 67 ++++++++++++------- 1 file changed, 42 insertions(+), 25 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index ad82932db72..3cac4e4acb7 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -39,6 +39,7 @@ import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; +import static com.google.common.base.Preconditions.checkNotNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; @@ -169,41 +170,53 @@ public void fail_to_initialize_from_empty_builder() { @Test public void deliver_updates_from_projection_repository() { - final Stand stand = mock(Stand.class); - final BoundedContext boundedContext = spy(Given.boundedContext(stand)); - // Init repository - final ProjectionRepository repository = Given.projectionRepo(boundedContext); - - repository.initStorage(InMemoryStorageFactory.getInstance()); - repository.setOnline(); + deliverUpdatesTest(new BoundedContextAction() { + @Override + public void perform(BoundedContext context) { + // Init repository + final ProjectionRepository repository = Given.projectionRepo(context); - // Dispatch an update from projection repo - repository.dispatch(Given.validEvent()); + repository.initStorage(InMemoryStorageFactory.getInstance()); + repository.setOnline(); - // Was called ONCE - verify(boundedContext).getStandFunnel(); - verify(stand).update(ArgumentMatchers.any(), any(Any.class)); + // Dispatch an update from projection repo + repository.dispatch(Given.validEvent()); + } + }); } @Test public void deliver_updates_form_aggregate_repository() { - final Stand stand = mock(Stand.class); + deliverUpdatesTest(new BoundedContextAction() { + @Override + public void perform(BoundedContext context) { + // Init repository + final AggregateRepository repository = Given.aggregateRepo(context); + + repository.initStorage(InMemoryStorageFactory.getInstance()); + try { + repository.dispatch(Given.validCommand()); + } catch (IllegalStateException e) { + // Handle null event dispatch after command handling. + Assert.assertTrue(e.getMessage().contains("No record found for command ID: EMPTY")); + } + + } + }); + } + + private static void deliverUpdatesTest(BoundedContextAction... dispatchActions) { + checkNotNull(dispatchActions); + + final Stand stand = mock(Stand.class); final BoundedContext boundedContext = spy(Given.boundedContext(stand)); - // Init repository - final AggregateRepository repository = Given.aggregateRepo(boundedContext); - - stand.registerTypeSupplier(repository); - repository.initStorage(InMemoryStorageFactory.getInstance()); - - // Dispatch an update from projection projectionRepo - try { - repository.dispatch(Given.validCommand()); - } catch (IllegalStateException e) { - // Handle null event dispatch after command handling. - Assert.assertTrue(e.getMessage().contains("No record found for command ID: EMPTY")); + + for (BoundedContextAction dispatchAction : dispatchActions) { + dispatchAction.perform(boundedContext); } + // Was called ONCE verify(boundedContext).getStandFunnel(); verify(stand).update(ArgumentMatchers.any(), any(Any.class)); @@ -250,4 +263,8 @@ public void run() { } + private interface BoundedContextAction { + void perform(BoundedContext context); + } + } From f553109be061eede2059550067d104595442c579 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 18:21:34 +0300 Subject: [PATCH 28/98] Refactor and add delivery tests for several repositories successively and in parallel. --- .../java/org/spine3/server/stand/Given.java | 29 ++++-- .../server/stand/StandFunnelShould.java | 89 +++++++++++++------ 2 files changed, 82 insertions(+), 36 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/Given.java b/server/src/test/java/org/spine3/server/stand/Given.java index 18b75d76805..ccad28106a2 100644 --- a/server/src/test/java/org/spine3/server/stand/Given.java +++ b/server/src/test/java/org/spine3/server/stand/Given.java @@ -46,12 +46,16 @@ import java.util.List; import java.util.concurrent.Executor; +import java.util.concurrent.Executors; /** * @author Dmytro Dashenkov */ /*package*/ class Given { + /*package*/ static final int THREADS_COUNT_IN_POOL_EXECUTOR = 10; + /*package*/ static final int SEVERAL = THREADS_COUNT_IN_POOL_EXECUTOR; + private static final String PROJECT_UUID = Identifiers.newUuid(); private Given() { @@ -154,16 +158,23 @@ public void handle(ProjectCreated event, EventContext context) { return new StandTestAggregateRepository(context); } - /*package*/ static BoundedContext boundedContext(Stand stand) { + /*package*/ static BoundedContext boundedContext(Stand stand, int concurrentThreads) { + final Executor executor = concurrentThreads > 0 ? Executors.newFixedThreadPool(concurrentThreads) : + new Executor() { // Straightforward executor + @Override + public void execute(Runnable command) { + command.run(); + } + }; + + return boundedContextBuilder(stand) + .setStandFunnelExecutor(executor) + .build(); + } + + private static BoundedContext.Builder boundedContextBuilder(Stand stand) { return BoundedContext.newBuilder() .setStand(stand) - .setStorageFactory(InMemoryStorageFactory.getInstance()) - .setStandFunnelExecutor(new Executor() { // Straightforward executor - @Override - public void execute(Runnable command) { - command.run(); - } - }) - .build(); + .setStorageFactory(InMemoryStorageFactory.getInstance()); } } diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index 3cac4e4acb7..486697b153e 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -31,7 +31,9 @@ import org.spine3.server.storage.memory.InMemoryStorageFactory; import org.spine3.testdata.TestStandFactory; +import java.security.SecureRandom; import java.util.Map; +import java.util.Random; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; @@ -44,6 +46,7 @@ import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; /** @@ -170,24 +173,55 @@ public void fail_to_initialize_from_empty_builder() { @Test public void deliver_updates_from_projection_repository() { - deliverUpdatesTest(new BoundedContextAction() { - @Override - public void perform(BoundedContext context) { - // Init repository - final ProjectionRepository repository = Given.projectionRepo(context); + deliverUpdates(false, projectionRepositoryDispatch()); + } - repository.initStorage(InMemoryStorageFactory.getInstance()); - repository.setOnline(); + @Test + public void deliver_updates_form_aggregate_repository() { + deliverUpdates(false, aggregateRepositoryDispatch()); + } - // Dispatch an update from projection repo - repository.dispatch(Given.validEvent()); - } - }); + @Test + public void deliver_updates_from_several_repositories_in_single_thread() { + deliverUpdates(false, getSeveralRepositoryDispatchCalls()); } @Test - public void deliver_updates_form_aggregate_repository() { - deliverUpdatesTest(new BoundedContextAction() { + public void deliver_updates_from_several_repositories_in_multiple_threads() { + deliverUpdates(true, getSeveralRepositoryDispatchCalls()); + } + + private static BoundedContextAction[] getSeveralRepositoryDispatchCalls() { + final BoundedContextAction[] result = new BoundedContextAction[Given.SEVERAL]; + final Random random = new SecureRandom(); + + for (int i = 0; i < result.length; i++) { + result[i] = random.nextBoolean() ? aggregateRepositoryDispatch() : projectionRepositoryDispatch(); + } + + return result; + } + + private static void deliverUpdates(boolean isMultiThread, BoundedContextAction... dispatchActions) { + checkNotNull(dispatchActions); + + final Stand stand = mock(Stand.class); + final BoundedContext boundedContext = spy(Given.boundedContext(stand, + isMultiThread ? + Given.THREADS_COUNT_IN_POOL_EXECUTOR : 0)); + + for (BoundedContextAction dispatchAction : dispatchActions) { + dispatchAction.perform(boundedContext); + } + + + // Was called ONCE + verify(boundedContext, times(dispatchActions.length)).getStandFunnel(); + verify(stand, times(dispatchActions.length)).update(ArgumentMatchers.any(), any(Any.class)); + } + + private static BoundedContextAction aggregateRepositoryDispatch() { + return new BoundedContextAction() { @Override public void perform(BoundedContext context) { // Init repository @@ -201,31 +235,32 @@ public void perform(BoundedContext context) { // Handle null event dispatch after command handling. Assert.assertTrue(e.getMessage().contains("No record found for command ID: EMPTY")); } - } - }); + }; } - private static void deliverUpdatesTest(BoundedContextAction... dispatchActions) { - checkNotNull(dispatchActions); + private static BoundedContextAction projectionRepositoryDispatch() { + return new BoundedContextAction() { + @Override + public void perform(BoundedContext context) { + // Init repository + final ProjectionRepository repository = Given.projectionRepo(context); - final Stand stand = mock(Stand.class); - final BoundedContext boundedContext = spy(Given.boundedContext(stand)); + repository.initStorage(InMemoryStorageFactory.getInstance()); + repository.setOnline(); - for (BoundedContextAction dispatchAction : dispatchActions) { - dispatchAction.perform(boundedContext); - } + // Dispatch an update from projection repo + repository.dispatch(Given.validEvent()); + } + }; + } - // Was called ONCE - verify(boundedContext).getStandFunnel(); - verify(stand).update(ArgumentMatchers.any(), any(Any.class)); - } @SuppressWarnings("MethodWithMultipleLoops") @Test public void deliver_updates_through_several_threads() throws InterruptedException { - final int threadsCount = 10; + final int threadsCount = Given.THREADS_COUNT_IN_POOL_EXECUTOR; @SuppressWarnings("LocalVariableNamingConvention") // Too long variable name final int threadExecutionMaxAwaitSeconds = 2; From 7a7f6a9cd985fb880b60176300fa2ffedd12580e Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 18:47:07 +0300 Subject: [PATCH 29/98] Fix failing build by adding "await" for a concurrent test. --- .../java/org/spine3/server/stand/Given.java | 1 + .../spine3/server/stand/StandFunnelShould.java | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/Given.java b/server/src/test/java/org/spine3/server/stand/Given.java index ccad28106a2..9e6d31d9f83 100644 --- a/server/src/test/java/org/spine3/server/stand/Given.java +++ b/server/src/test/java/org/spine3/server/stand/Given.java @@ -57,6 +57,7 @@ /*package*/ static final int SEVERAL = THREADS_COUNT_IN_POOL_EXECUTOR; private static final String PROJECT_UUID = Identifiers.newUuid(); + public static final int AWAIT_SECONDS = 2; private Given() { } diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index 486697b153e..0e8f48438fc 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -214,12 +214,23 @@ private static void deliverUpdates(boolean isMultiThread, BoundedContextAction.. dispatchAction.perform(boundedContext); } - - // Was called ONCE + // Was called as much times as there are dispatch actions. verify(boundedContext, times(dispatchActions.length)).getStandFunnel(); + + if (isMultiThread) { + await(Given.AWAIT_SECONDS); + } + verify(stand, times(dispatchActions.length)).update(ArgumentMatchers.any(), any(Any.class)); } + private static void await(int seconds) { + try { + Thread.sleep(seconds); + } catch (InterruptedException ignore) { + } + } + private static BoundedContextAction aggregateRepositoryDispatch() { return new BoundedContextAction() { @Override @@ -256,13 +267,12 @@ public void perform(BoundedContext context) { } - @SuppressWarnings("MethodWithMultipleLoops") @Test public void deliver_updates_through_several_threads() throws InterruptedException { final int threadsCount = Given.THREADS_COUNT_IN_POOL_EXECUTOR; @SuppressWarnings("LocalVariableNamingConvention") // Too long variable name - final int threadExecutionMaxAwaitSeconds = 2; + final int threadExecutionMaxAwaitSeconds = Given.AWAIT_SECONDS; final Map threadInvocationRegistry = new ConcurrentHashMap<>(threadsCount); From 459a42ba8933e30f43b3b8ec079c478e5924b37d Mon Sep 17 00:00:00 2001 From: Alex Tymchenko Date: Wed, 14 Sep 2016 20:12:22 +0300 Subject: [PATCH 30/98] Return a query result as an instance of List; test bulk reading of aggregate states by their IDs. --- .../java/org/spine3/server/stand/Stand.java | 8 +- .../org/spine3/server/stand/StandShould.java | 130 ++++++++++++++---- 2 files changed, 106 insertions(+), 32 deletions(-) diff --git a/server/src/main/java/org/spine3/server/stand/Stand.java b/server/src/main/java/org/spine3/server/stand/Stand.java index 6f15d357b64..c9b9ca3a8a2 100644 --- a/server/src/main/java/org/spine3/server/stand/Stand.java +++ b/server/src/main/java/org/spine3/server/stand/Stand.java @@ -258,7 +258,7 @@ public void execute(Query query, StreamObserver responseObserver) private ImmutableCollection internalExecute(Query query) { - final ImmutableSet.Builder resultBuilder = ImmutableSet.builder(); + final ImmutableList.Builder resultBuilder = ImmutableList.builder(); final Target target = query.getTarget(); @@ -280,7 +280,7 @@ private ImmutableCollection internalExecute(Query query) { feedStateRecordsToBuilder(resultBuilder, stateRecords); } - final ImmutableSet result = resultBuilder.build(); + final ImmutableList result = resultBuilder.build(); return result; } @@ -355,7 +355,7 @@ private static ImmutableCollection fetchFromEntityRepository(T return result; } - private static void feedEntitiesToBuilder(ImmutableSet.Builder resultBuilder, ImmutableCollection all) { + private static void feedEntitiesToBuilder(ImmutableList.Builder resultBuilder, ImmutableCollection all) { for (Entity record : all) { final Message state = record.getState(); final Any packedState = AnyPacker.pack(state); @@ -363,7 +363,7 @@ private static void feedEntitiesToBuilder(ImmutableSet.Builder resultBuilde } } - private static void feedStateRecordsToBuilder(ImmutableSet.Builder resultBuilder, ImmutableCollection all) { + private static void feedStateRecordsToBuilder(ImmutableList.Builder resultBuilder, ImmutableCollection all) { for (EntityStorageRecord record : all) { final Any state = record.getState(); resultBuilder.add(state); diff --git a/server/src/test/java/org/spine3/server/stand/StandShould.java b/server/src/test/java/org/spine3/server/stand/StandShould.java index 7880fe510a9..7de36fe77c5 100644 --- a/server/src/test/java/org/spine3/server/stand/StandShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandShould.java @@ -25,7 +25,6 @@ import com.google.common.collect.ImmutableSet; import com.google.protobuf.Any; import com.google.protobuf.Descriptors; -import com.google.protobuf.Message; import io.grpc.stub.StreamObserver; import org.junit.Test; import org.mockito.ArgumentMatcher; @@ -50,11 +49,15 @@ import org.spine3.test.projection.Project; import org.spine3.test.projection.ProjectId; +import java.util.Collection; import java.util.List; +import java.util.Map; import java.util.Objects; +import java.util.Random; import java.util.Set; import java.util.concurrent.Executor; +import static com.google.common.collect.Maps.newHashMap; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; @@ -72,8 +75,9 @@ /** * @author Alex Tymchenko */ -@SuppressWarnings("OverlyCoupledClass") //It's OK for a test. +@SuppressWarnings({"OverlyCoupledClass", "InstanceMethodNamingConvention"}) //It's OK for a test. public class StandShould { + private static final int TOTAL_CUSTOMERS_FOR_BATCH_READING = 10; // **** Positive scenarios **** @@ -230,50 +234,120 @@ public void return_empty_list_for_aggregate_read_all_on_empty_stand_storage() { @Test public void return_single_result_for_aggregate_state_read_by_id() { + doCheckReadingCustomersById(1); + } + + @Test + public void return_multiple_results_for_aggregate_state_batch_read_by_ids() { + doCheckReadingCustomersById(TOTAL_CUSTOMERS_FOR_BATCH_READING); + } + + private static void doCheckReadingCustomersById(int numberOfCustomers) { // Define the types and values used as a test data. + final Map sampleCustomers = newHashMap(); final TypeUrl customerType = TypeUrl.of(Customer.class); - final Customer customer = Customer.getDefaultInstance(); - final Any customerState = AnyPacker.pack(customer); - final CustomerId customerId = CustomerId.newBuilder() - .setNumber(42) - .build(); - final AggregateStateId stateId = AggregateStateId.of(customerId, customerType); - + fillSampleCustomers(sampleCustomers, numberOfCustomers); // Prepare the stand and its mock storage to act. final StandStorage standStorageMock = mock(StandStorage.class); - final EntityStorageRecord entityStorageRecord = EntityStorageRecord.newBuilder() - .setState(customerState) - .build(); - when(standStorageMock.read(eq(stateId))).thenReturn(entityStorageRecord); - final Stand stand = prepareStandWithAggregateRepo(standStorageMock); + setupExpectedBulkReadBehaviour(sampleCustomers, customerType, standStorageMock); - // Trigger the update. - stand.update(customerId, customerState); + final Stand stand = prepareStandWithAggregateRepo(standStorageMock); + triggerMultipleUpdates(sampleCustomers, stand); // Now we are ready to query. - final EntityIdFilter idFilter = EntityIdFilter.newBuilder() - .addIds(EntityId.newBuilder() - .setId(AnyPacker.pack(customerId))) - .build(); + final EntityIdFilter idFilter = idFilterFor(sampleCustomers.keySet()); + final Target customerTarget = Target.newBuilder() .setFilters(EntityFilters.newBuilder() .setIdFilter(idFilter)) .setType(customerType.getTypeName()) .build(); - final Query readSingleCustomer = Query.newBuilder() - .setTarget(customerTarget) - .build(); + final Query readMultipleCustomers = Query.newBuilder() + .setTarget(customerTarget) + .build(); final MemoizeQueryResponseObserver responseObserver = new MemoizeQueryResponseObserver(); - stand.execute(readSingleCustomer, responseObserver); + stand.execute(readMultipleCustomers, responseObserver); final List messageList = checkAndGetMessageList(responseObserver); - assertEquals(1, messageList.size()); - final Any singleRecord = messageList.get(0); - final Message unpackedSingleResult = AnyPacker.unpack(singleRecord); - assertEquals(customer, unpackedSingleResult); + assertEquals(sampleCustomers.size(), messageList.size()); + final Collection allCustomers = sampleCustomers.values(); + for (Any singleRecord : messageList) { + final Customer unpackedSingleResult = AnyPacker.unpack(singleRecord); + assertTrue(allCustomers.contains(unpackedSingleResult)); + } + + } + + @SuppressWarnings("ConstantConditions") + private static void setupExpectedBulkReadBehaviour(Map sampleCustomers, TypeUrl customerType, StandStorage standStorageMock) { + final ImmutableList.Builder stateIdsBuilder = ImmutableList.builder(); + final ImmutableList.Builder recordsBuilder = ImmutableList.builder(); + for (CustomerId customerId : sampleCustomers.keySet()) { + final AggregateStateId stateId = AggregateStateId.of(customerId, customerType); + final Customer customer = Customer.getDefaultInstance(); + final Any customerState = AnyPacker.pack(customer); + final EntityStorageRecord entityStorageRecord = EntityStorageRecord.newBuilder() + .setState(customerState) + .build(); + stateIdsBuilder.add(stateId); + recordsBuilder.add(entityStorageRecord); + + when(standStorageMock.read(eq(stateId))).thenReturn(entityStorageRecord); + } + + final ImmutableList stateIds = stateIdsBuilder.build(); + final ImmutableList records = recordsBuilder.build(); + + final Iterable matchingIds = argThat(aggregateIdsIterableMatcher(stateIds)); + when(standStorageMock.readBulk(matchingIds)).thenReturn(records); + } + + private static ArgumentMatcher> aggregateIdsIterableMatcher(final ImmutableList stateIds) { + return new ArgumentMatcher>() { + @Override + public boolean matches(Iterable argument) { + boolean everyElementPresent = true; + for (AggregateStateId aggregateStateId : argument) { + everyElementPresent = everyElementPresent && stateIds.contains(aggregateStateId); + } + return everyElementPresent; + } + }; + } + + private static EntityIdFilter idFilterFor(Collection customerIds) { + final EntityIdFilter.Builder idFilterBuilder = EntityIdFilter.newBuilder(); + for (CustomerId id : customerIds) { + idFilterBuilder + .addIds(EntityId.newBuilder() + .setId(AnyPacker.pack(id))); + } + return idFilterBuilder.build(); + } + + private static void triggerMultipleUpdates(Map sampleCustomers, Stand stand) { + // Trigger the aggregate state updates. + for (CustomerId id : sampleCustomers.keySet()) { + final Customer sampleCustomer = sampleCustomers.get(id); + final Any customerState = AnyPacker.pack(sampleCustomer); + stand.update(id, customerState); + } + } + + private static void fillSampleCustomers(Map sampleCustomers, int numberOfCustomers) { + for (int customerIndex = 0; customerIndex < numberOfCustomers; customerIndex++) { + final Customer customer = Customer.getDefaultInstance(); + + @SuppressWarnings("UnsecureRandomNumberGeneration") + final Random randomizer = new Random(); + final CustomerId customerId = CustomerId.newBuilder() + .setNumber(randomizer.nextInt()) + .build(); + sampleCustomers.put(customerId, customer); + } } private static List checkAndGetMessageList(MemoizeQueryResponseObserver responseObserver) { From 47ab39e785b541747103bb3106ed3c317b1f2024 Mon Sep 17 00:00:00 2001 From: Alex Tymchenko Date: Wed, 14 Sep 2016 20:40:47 +0300 Subject: [PATCH 31/98] Test aggregate state query processing in case of: * various empty filter states; * valid filters but empty aggregate storage state. --- .../java/org/spine3/server/stand/Stand.java | 4 +- .../org/spine3/server/stand/StandShould.java | 133 ++++++++++++++++-- 2 files changed, 122 insertions(+), 15 deletions(-) diff --git a/server/src/main/java/org/spine3/server/stand/Stand.java b/server/src/main/java/org/spine3/server/stand/Stand.java index c9b9ca3a8a2..ecdc46bffc9 100644 --- a/server/src/main/java/org/spine3/server/stand/Stand.java +++ b/server/src/main/java/org/spine3/server/stand/Stand.java @@ -293,7 +293,9 @@ private ImmutableCollection fetchFromStandStorage(Target ta } else { final EntityFilters filters = target.getFilters(); - if (filters != null && filters.getIdFilter() != null) { + + // TODO[alex.tymchenko]: do we need to check for null at all? How about, say, Python gRPC client? + if (filters != null && filters.getIdFilter() != null && !filters.getIdFilter().getIdsList().isEmpty()) { final EntityIdFilter idFilter = filters.getIdFilter(); final Collection stateIds = Collections2.transform(idFilter.getIdsList(), aggregateStateIdTransformer(typeUrl)); diff --git a/server/src/test/java/org/spine3/server/stand/StandShould.java b/server/src/test/java/org/spine3/server/stand/StandShould.java index 7de36fe77c5..97efa3352e6 100644 --- a/server/src/test/java/org/spine3/server/stand/StandShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandShould.java @@ -23,11 +23,13 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Maps; import com.google.protobuf.Any; import com.google.protobuf.Descriptors; import io.grpc.stub.StreamObserver; import org.junit.Test; import org.mockito.ArgumentMatcher; +import org.mockito.ArgumentMatchers; import org.mockito.InOrder; import org.spine3.base.Responses; import org.spine3.client.EntityFilters; @@ -75,7 +77,8 @@ /** * @author Alex Tymchenko */ -@SuppressWarnings({"OverlyCoupledClass", "InstanceMethodNamingConvention"}) //It's OK for a test. +//It's OK for a test. +@SuppressWarnings({"OverlyCoupledClass", "InstanceMethodNamingConvention", "ClassWithTooManyMethods"}) public class StandShould { private static final int TOTAL_CUSTOMERS_FOR_BATCH_READING = 10; @@ -208,28 +211,76 @@ public void operate_with_storage_provided_through_builder() { @Test public void return_empty_list_for_aggregate_read_all_on_empty_stand_storage() { - final StandStorage standStorageMock = mock(StandStorage.class); - // Return an empty collection on {@link StandStorage#readAllByType(TypeUrl)} call. - final ImmutableList emptyResultList = ImmutableList.builder().build(); - when(standStorageMock.readAllByType(any(TypeUrl.class))).thenReturn(emptyResultList); + final TypeUrl customerType = TypeUrl.of(Customer.class); + final Target customerTarget = Target.newBuilder() + .setIncludeAll(true) + .setType(customerType.getTypeName()) + .build(); - final Stand stand = prepareStandWithAggregateRepo(standStorageMock); + checkEmptyResultForTargetOnEmptyStorage(customerTarget); + } + + @Test + public void return_empty_list_for_aggregate_read_by_ids_on_empty_stand_storage() { final TypeUrl customerType = TypeUrl.of(Customer.class); + + final EntityId firstId = wrapCustomerId(1); + final EntityId anotherId = wrapCustomerId(2); + final EntityIdFilter idFilter = EntityIdFilter.newBuilder() + .addIds(firstId) + .addIds(anotherId) + .build(); + final EntityFilters filters = EntityFilters.newBuilder() + .setIdFilter(idFilter) + .build(); final Target customerTarget = Target.newBuilder() - .setIncludeAll(true) + .setIncludeAll(false) .setType(customerType.getTypeName()) + .setFilters(filters) .build(); - final Query readAllCustomers = Query.newBuilder() - .setTarget(customerTarget) + + checkEmptyResultForTargetOnEmptyStorage(customerTarget); + } + + @Test + public void return_empty_list_for_aggregate_reads_with_filters_not_set() { + + final TypeUrl customerType = TypeUrl.of(Customer.class); + final Target customerTarget = Target.newBuilder() + .setIncludeAll(false) + .setType(customerType.getTypeName()) .build(); + checkEmptyResultOnNonEmptyStorageForQueryTarget(customerTarget); + } - final MemoizeQueryResponseObserver responseObserver = new MemoizeQueryResponseObserver(); - stand.execute(readAllCustomers, responseObserver); + @Test + public void return_empty_list_for_aggregate_reads_with_id_filter_not_set() { - final List messageList = checkAndGetMessageList(responseObserver); - assertTrue("Query returned a non-empty response message list though the target was empty", messageList - .isEmpty()); + final TypeUrl customerType = TypeUrl.of(Customer.class); + final EntityFilters filters = EntityFilters.newBuilder() + .build(); + final Target customerTarget = Target.newBuilder() + .setIncludeAll(false) + .setType(customerType.getTypeName()) + .setFilters(filters) + .build(); + checkEmptyResultOnNonEmptyStorageForQueryTarget(customerTarget); + } + + @Test + public void return_empty_list_for_aggregate_reads_with_empty_list_of_ids() { + + final TypeUrl customerType = TypeUrl.of(Customer.class); + final EntityFilters filters = EntityFilters.newBuilder() + .setIdFilter(EntityIdFilter.getDefaultInstance()) + .build(); + final Target customerTarget = Target.newBuilder() + .setIncludeAll(false) + .setType(customerType.getTypeName()) + .setFilters(filters) + .build(); + checkEmptyResultOnNonEmptyStorageForQueryTarget(customerTarget); } @Test @@ -242,6 +293,24 @@ public void return_multiple_results_for_aggregate_state_batch_read_by_ids() { doCheckReadingCustomersById(TOTAL_CUSTOMERS_FOR_BATCH_READING); } + private static void checkEmptyResultForTargetOnEmptyStorage(Target customerTarget) { + final StandStorage standStorageMock = mock(StandStorage.class); + // Return an empty collection on {@link StandStorage#readAllByType(TypeUrl)} call. + final ImmutableList emptyResultList = ImmutableList.builder().build(); + when(standStorageMock.readAllByType(any(TypeUrl.class))).thenReturn(emptyResultList); + + final Stand stand = prepareStandWithAggregateRepo(standStorageMock); + + final Query readAllCustomers = Query.newBuilder() + .setTarget(customerTarget) + .build(); + + final MemoizeQueryResponseObserver responseObserver = new MemoizeQueryResponseObserver(); + stand.execute(readAllCustomers, responseObserver); + + final List messageList = checkAndGetMessageList(responseObserver); + assertTrue("Query returned a non-empty response message list though the target was empty", messageList.isEmpty()); + } private static void doCheckReadingCustomersById(int numberOfCustomers) { // Define the types and values used as a test data. @@ -281,6 +350,32 @@ private static void doCheckReadingCustomersById(int numberOfCustomers) { } + private static void checkEmptyResultOnNonEmptyStorageForQueryTarget(Target customerTarget) { + final StandStorage standStorageMock = mock(StandStorage.class); + + // Return non-empty results on any storage read call. + final EntityStorageRecord someRecord = EntityStorageRecord.getDefaultInstance(); + final ImmutableList nonEmptyList = ImmutableList.builder().add(someRecord) + .build(); + when(standStorageMock.readAllByType(any(TypeUrl.class))).thenReturn(nonEmptyList); + when(standStorageMock.read(any(AggregateStateId.class))).thenReturn(someRecord); + when(standStorageMock.readAll()).thenReturn(Maps.newHashMap()); + when(standStorageMock.readBulk(ArgumentMatchers.anyIterable())).thenReturn(nonEmptyList); + + final Stand stand = prepareStandWithAggregateRepo(standStorageMock); + + final Query queryWithNoFilters = Query.newBuilder() + .setTarget(customerTarget) + .build(); + + final MemoizeQueryResponseObserver responseObserver = new MemoizeQueryResponseObserver(); + stand.execute(queryWithNoFilters, responseObserver); + + final List messageList = checkAndGetMessageList(responseObserver); + assertTrue("Query returned a non-empty response message list though the filter was not set", messageList.isEmpty()); + } + + @SuppressWarnings("ConstantConditions") private static void setupExpectedBulkReadBehaviour(Map sampleCustomers, TypeUrl customerType, StandStorage standStorageMock) { final ImmutableList.Builder stateIdsBuilder = ImmutableList.builder(); @@ -305,6 +400,16 @@ private static void setupExpectedBulkReadBehaviour(Map sam when(standStorageMock.readBulk(matchingIds)).thenReturn(records); } + private static EntityId wrapCustomerId(int number) { + final CustomerId customerId = CustomerId.newBuilder() + .setNumber(number) + .build(); + final Any packedId = AnyPacker.pack(customerId); + return EntityId.newBuilder() + .setId(packedId) + .build(); + } + private static ArgumentMatcher> aggregateIdsIterableMatcher(final ImmutableList stateIds) { return new ArgumentMatcher>() { @Override From f2c99a1b1a69981e9317bc77d759b21b14be243e Mon Sep 17 00:00:00 2001 From: Alex Tymchenko Date: Wed, 14 Sep 2016 20:50:38 +0300 Subject: [PATCH 32/98] Stand tests: use Mockito#times() instead of Mockito#calls(), since the order is not important. --- .../org/spine3/server/stand/StandShould.java | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/StandShould.java b/server/src/test/java/org/spine3/server/stand/StandShould.java index 97efa3352e6..81778486fe0 100644 --- a/server/src/test/java/org/spine3/server/stand/StandShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandShould.java @@ -30,7 +30,6 @@ import org.junit.Test; import org.mockito.ArgumentMatcher; import org.mockito.ArgumentMatchers; -import org.mockito.InOrder; import org.spine3.base.Responses; import org.spine3.client.EntityFilters; import org.spine3.client.EntityId; @@ -67,10 +66,10 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.calls; -import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.spine3.testdata.TestBoundedContextFactory.newBoundedContext; @@ -150,7 +149,6 @@ public void register_aggregate_repositories() { @Test public void use_provided_executor_upon_update_of_watched_type() { final Executor executor = mock(Executor.class); - final InOrder executorInOrder = inOrder(executor); final Stand stand = Stand.newBuilder() .setCallbackExecutor(executor) .build(); @@ -161,21 +159,18 @@ public void use_provided_executor_upon_update_of_watched_type() { final TypeUrl projectProjectionType = TypeUrl.of(Project.class); stand.watch(projectProjectionType, emptyUpdateCallback()); - executorInOrder.verify(executor, never()) - .execute(any(Runnable.class)); + verify(executor, never()).execute(any(Runnable.class)); final Any someUpdate = AnyPacker.pack(Project.getDefaultInstance()); final Object someId = new Object(); stand.update(someId, someUpdate); - executorInOrder.verify(executor, calls(1)) - .execute(any(Runnable.class)); + verify(executor, times(1)).execute(any(Runnable.class)); } @Test public void operate_with_storage_provided_through_builder() { final StandStorage standStorageMock = mock(StandStorage.class); - final InOrder standStorageInOrder = inOrder(standStorageMock); final Stand stand = Stand.newBuilder() .setStorage(standStorageMock) .build(); @@ -195,8 +190,7 @@ public void operate_with_storage_provided_through_builder() { final Any packedState = AnyPacker.pack(customerState); final TypeUrl customerType = TypeUrl.of(Customer.class); - standStorageInOrder.verify(standStorageMock, never()) - .write(any(AggregateStateId.class), any(EntityStorageRecord.class)); + verify(standStorageMock, never()).write(any(AggregateStateId.class), any(EntityStorageRecord.class)); stand.update(customerId, packedState); @@ -204,8 +198,7 @@ public void operate_with_storage_provided_through_builder() { final EntityStorageRecord expectedRecord = EntityStorageRecord.newBuilder() .setState(packedState) .build(); - standStorageInOrder.verify(standStorageMock, calls(1)) - .write(eq(expectedAggregateStateId), recordStateMatcher(expectedRecord)); + verify(standStorageMock, times(1)).write(eq(expectedAggregateStateId), recordStateMatcher(expectedRecord)); } @Test From ecf11611e4ac089187c40af7f6714ced1c012be3 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 21:22:03 +0300 Subject: [PATCH 33/98] Fix issues form PR. --- .../java/org/spine3/server/stand/Given.java | 10 +-- .../server/stand/StandFunnelShould.java | 89 ++++++++----------- 2 files changed, 41 insertions(+), 58 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/Given.java b/server/src/test/java/org/spine3/server/stand/Given.java index 9e6d31d9f83..4977b9e0a3e 100644 --- a/server/src/test/java/org/spine3/server/stand/Given.java +++ b/server/src/test/java/org/spine3/server/stand/Given.java @@ -20,6 +20,7 @@ package org.spine3.server.stand; +import com.google.common.util.concurrent.MoreExecutors; import com.google.protobuf.Any; import com.google.protobuf.Message; import org.spine3.base.Command; @@ -139,7 +140,7 @@ public void handle(ProjectCreated event, EventContext context) { .setDoNotEnrich(true) .setCommandContext(CommandContext.getDefaultInstance()) .setEventId(EventId.newBuilder() - .setUuid(PROJECT_UUID) + .setUuid(Identifiers.newUuid()) .build())) .build(); } @@ -161,12 +162,7 @@ public void handle(ProjectCreated event, EventContext context) { /*package*/ static BoundedContext boundedContext(Stand stand, int concurrentThreads) { final Executor executor = concurrentThreads > 0 ? Executors.newFixedThreadPool(concurrentThreads) : - new Executor() { // Straightforward executor - @Override - public void execute(Runnable command) { - command.run(); - } - }; + MoreExecutors.directExecutor(); return boundedContextBuilder(stand) .setStandFunnelExecutor(executor) diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index 0e8f48438fc..b405de4777f 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -22,6 +22,7 @@ package org.spine3.server.stand; import com.google.protobuf.Any; +import io.netty.util.internal.ConcurrentSet; import org.junit.Assert; import org.junit.Test; import org.mockito.ArgumentMatchers; @@ -32,13 +33,11 @@ import org.spine3.testdata.TestStandFactory; import java.security.SecureRandom; -import java.util.Map; import java.util.Random; -import java.util.concurrent.ConcurrentHashMap; +import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import static com.google.common.base.Preconditions.checkNotNull; @@ -57,10 +56,6 @@ public class StandFunnelShould { // **** Positive scenarios (unit) **** - /** - * - deliver mock updates to the stand (invoke proper methods with particular arguments) - test the delivery only. - */ - @Test public void initialize_properly_with_stand_only() { final Stand stand = TestStandFactory.create(); @@ -71,36 +66,25 @@ public void initialize_properly_with_stand_only() { } @Test - public void initialize_properly_with_various_builder_options() { + public void initialize_properly_with_all_builder_options() { final Stand stand = TestStandFactory.create(); - final Executor executor = Executors.newSingleThreadExecutor(new ThreadFactory() { - @Override - public Thread newThread(Runnable r) { - return Thread.currentThread(); - } - }); + final Executor executor = Executors.newSingleThreadExecutor(); - final StandFunnel blockingFunnel = StandFunnel.newBuilder() + final StandFunnel funnel = StandFunnel.newBuilder() .setStand(stand) .setExecutor(executor) .build(); - Assert.assertNotNull(blockingFunnel); + Assert.assertNotNull(funnel); + } + + @Test + public void initialize_properly_with_no_executor() { + final Stand stand = TestStandFactory.create(); final StandFunnel funnelForBusyStand = StandFunnel.newBuilder() .setStand(stand) - .setExecutor(Executors.newSingleThreadExecutor()) .build(); Assert.assertNotNull(funnelForBusyStand); - - - final StandFunnel emptyExecutorFunnel = StandFunnel.newBuilder() - .setStand(TestStandFactory.create()) - .setExecutor(new Executor() { - @Override - public void execute(Runnable neverCalled) { } - }) - .build(); - Assert.assertNotNull(emptyExecutorFunnel); } @Test @@ -149,8 +133,8 @@ public void execute(Runnable command) { @SuppressWarnings("ResultOfMethodCallIgnored") @Test(expected = NullPointerException.class) - public void fail_to_initialize_with_inproper_stand() { - @SuppressWarnings("ConstantConditions") + public void fail_to_initialize_with_improper_stand() { + @SuppressWarnings("ConstantConditions") // null is marked as improper with this warning final StandFunnel.Builder builder = StandFunnel.newBuilder().setStand(null); builder.build(); @@ -165,30 +149,24 @@ public void fail_to_initialize_from_empty_builder() { // **** Integration scenarios ( -> StandFunnel -> Mock Stand) **** - /** - * - Deliver updates from projection projectionRepo on update; - * - deliver updates from aggregate projectionRepo on update; - * - deliver the updates from several projection and aggregate repositories. - */ - @Test public void deliver_updates_from_projection_repository() { - deliverUpdates(false, projectionRepositoryDispatch()); + checkUpdatesDelivery(false, projectionRepositoryDispatch()); } @Test public void deliver_updates_form_aggregate_repository() { - deliverUpdates(false, aggregateRepositoryDispatch()); + checkUpdatesDelivery(false, aggregateRepositoryDispatch()); } @Test public void deliver_updates_from_several_repositories_in_single_thread() { - deliverUpdates(false, getSeveralRepositoryDispatchCalls()); + checkUpdatesDelivery(false, getSeveralRepositoryDispatchCalls()); } @Test public void deliver_updates_from_several_repositories_in_multiple_threads() { - deliverUpdates(true, getSeveralRepositoryDispatchCalls()); + checkUpdatesDelivery(true, getSeveralRepositoryDispatchCalls()); } private static BoundedContextAction[] getSeveralRepositoryDispatchCalls() { @@ -202,22 +180,22 @@ private static BoundedContextAction[] getSeveralRepositoryDispatchCalls() { return result; } - private static void deliverUpdates(boolean isMultiThread, BoundedContextAction... dispatchActions) { + private static void checkUpdatesDelivery(boolean isConcurrent, BoundedContextAction... dispatchActions) { checkNotNull(dispatchActions); final Stand stand = mock(Stand.class); final BoundedContext boundedContext = spy(Given.boundedContext(stand, - isMultiThread ? + isConcurrent ? Given.THREADS_COUNT_IN_POOL_EXECUTOR : 0)); for (BoundedContextAction dispatchAction : dispatchActions) { dispatchAction.perform(boundedContext); } - // Was called as much times as there are dispatch actions. + // Was called as many times as there are dispatch actions. verify(boundedContext, times(dispatchActions.length)).getStandFunnel(); - if (isMultiThread) { + if (isConcurrent) { await(Given.AWAIT_SECONDS); } @@ -241,10 +219,19 @@ public void perform(BoundedContext context) { repository.initStorage(InMemoryStorageFactory.getInstance()); try { + // Mock aggregate and mock stand are not able to handle events returned after command handling. + // This causes IllegalStateException to be thrown. + // Note that this is not the end of a test case, so we can't just "expect=IllegalStateException" repository.dispatch(Given.validCommand()); } catch (IllegalStateException e) { - // Handle null event dispatch after command handling. - Assert.assertTrue(e.getMessage().contains("No record found for command ID: EMPTY")); + // Handle null event dispatching after the command is handled. + + // Check if this error is caused by returning nuu or empty list after command processing. + // Proceed crash if it's not + if (!e.getMessage() + .contains("No record found for command ID: EMPTY")) { + throw e; + } } } }; @@ -274,7 +261,7 @@ public void deliver_updates_through_several_threads() throws InterruptedExceptio @SuppressWarnings("LocalVariableNamingConvention") // Too long variable name final int threadExecutionMaxAwaitSeconds = Given.AWAIT_SECONDS; - final Map threadInvocationRegistry = new ConcurrentHashMap<>(threadsCount); + final Set threadInvocationRegistry = new ConcurrentSet<>(); final Stand stand = mock(Stand.class); doNothing().when(stand).update(ArgumentMatchers.any(), any(Any.class)); @@ -283,26 +270,26 @@ public void deliver_updates_through_several_threads() throws InterruptedExceptio .setStand(stand) .build(); - final ExecutorService processes = Executors.newFixedThreadPool(threadsCount); + final ExecutorService executor = Executors.newFixedThreadPool(threadsCount); final Runnable task = new Runnable() { @Override public void run() { final String threadName = Thread.currentThread().getName(); - Assert.assertFalse(threadInvocationRegistry.containsKey(threadName)); + Assert.assertFalse(threadInvocationRegistry.contains(threadName)); standFunnel.post(new Object(), Any.getDefaultInstance()); - threadInvocationRegistry.put(threadName, new Object()); + threadInvocationRegistry.add(threadName); } }; for (int i = 0; i < threadsCount; i++) { - processes.execute(task); + executor.execute(task); } - processes.awaitTermination(threadExecutionMaxAwaitSeconds, TimeUnit.SECONDS); + executor.awaitTermination(threadExecutionMaxAwaitSeconds, TimeUnit.SECONDS); Assert.assertEquals(threadInvocationRegistry.size(), threadsCount); From 6fa6f1440170c7a7a0d5a642daacf38b07f0a562 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 21:36:39 +0300 Subject: [PATCH 34/98] Increase await time for concurrent check execution. --- server/src/test/java/org/spine3/server/stand/Given.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/test/java/org/spine3/server/stand/Given.java b/server/src/test/java/org/spine3/server/stand/Given.java index 4977b9e0a3e..82fc42e059d 100644 --- a/server/src/test/java/org/spine3/server/stand/Given.java +++ b/server/src/test/java/org/spine3/server/stand/Given.java @@ -58,7 +58,7 @@ /*package*/ static final int SEVERAL = THREADS_COUNT_IN_POOL_EXECUTOR; private static final String PROJECT_UUID = Identifiers.newUuid(); - public static final int AWAIT_SECONDS = 2; + public static final int AWAIT_SECONDS = 4; private Given() { } From 5226db609bdb4cfca6e0cb277587ff08db2e6c56 Mon Sep 17 00:00:00 2001 From: Alex Tymchenko Date: Wed, 14 Sep 2016 21:44:17 +0300 Subject: [PATCH 35/98] Add tests for projection repositories used in stand: reading by IDs. --- .../org/spine3/server/stand/StandShould.java | 165 +++++++++++++++++- 1 file changed, 160 insertions(+), 5 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/StandShould.java b/server/src/test/java/org/spine3/server/stand/StandShould.java index 81778486fe0..75ca1a56694 100644 --- a/server/src/test/java/org/spine3/server/stand/StandShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandShould.java @@ -21,11 +21,15 @@ */ package org.spine3.server.stand; +import com.google.common.base.Function; +import com.google.common.collect.Collections2; +import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; import com.google.protobuf.Any; import com.google.protobuf.Descriptors; +import com.google.protobuf.Message; import io.grpc.stub.StreamObserver; import org.junit.Test; import org.mockito.ArgumentMatcher; @@ -50,14 +54,17 @@ import org.spine3.test.projection.Project; import org.spine3.test.projection.ProjectId; +import javax.annotation.Nullable; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Random; import java.util.Set; +import java.util.UUID; import java.util.concurrent.Executor; +import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.Maps.newHashMap; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -80,6 +87,7 @@ @SuppressWarnings({"OverlyCoupledClass", "InstanceMethodNamingConvention", "ClassWithTooManyMethods"}) public class StandShould { private static final int TOTAL_CUSTOMERS_FOR_BATCH_READING = 10; + private static final int TOTAL_PROJECTS_FOR_BATCH_READING = 10; // **** Positive scenarios **** @@ -286,6 +294,17 @@ public void return_multiple_results_for_aggregate_state_batch_read_by_ids() { doCheckReadingCustomersById(TOTAL_CUSTOMERS_FOR_BATCH_READING); } + + @Test + public void return_single_result_for_projection_read_by_id() { + doCheckReadingProjectsById(1); + } + + @Test + public void return_multiple_results_for_projection_batch_read_by_ids() { + doCheckReadingProjectsById(TOTAL_PROJECTS_FOR_BATCH_READING); + } + private static void checkEmptyResultForTargetOnEmptyStorage(Target customerTarget) { final StandStorage standStorageMock = mock(StandStorage.class); // Return an empty collection on {@link StandStorage#readAllByType(TypeUrl)} call. @@ -305,6 +324,42 @@ private static void checkEmptyResultForTargetOnEmptyStorage(Target customerTarge assertTrue("Query returned a non-empty response message list though the target was empty", messageList.isEmpty()); } + private static void doCheckReadingProjectsById(int numberOfProjects) { + // Define the types and values used as a test data. + final Map sampleProjects = newHashMap(); + final TypeUrl projectType = TypeUrl.of(Project.class); + fillSampleProjects(sampleProjects, numberOfProjects); + + final StandTestProjectionRepository projectionRepository = mock(StandTestProjectionRepository.class); + when(projectionRepository.getEntityStateType()).thenReturn(projectType); + setupExpectedFindAllBehaviour(sampleProjects, projectionRepository); + + final Stand stand = prepareStandWithProjectionRepo(projectionRepository); + + // Now we are ready to query. + final EntityIdFilter idFilter = idFilterForProjection(sampleProjects.keySet()); + + final Target projectTarget = Target.newBuilder() + .setFilters(EntityFilters.newBuilder() + .setIdFilter(idFilter)) + .setType(projectType.getTypeName()) + .build(); + final Query readMultipleProjects = Query.newBuilder() + .setTarget(projectTarget) + .build(); + + final MemoizeQueryResponseObserver responseObserver = new MemoizeQueryResponseObserver(); + stand.execute(readMultipleProjects, responseObserver); + + final List messageList = checkAndGetMessageList(responseObserver); + assertEquals(sampleProjects.size(), messageList.size()); + final Collection allCustomers = sampleProjects.values(); + for (Any singleRecord : messageList) { + final Project unpackedSingleResult = AnyPacker.unpack(singleRecord); + assertTrue(allCustomers.contains(unpackedSingleResult)); + } + } + private static void doCheckReadingCustomersById(int numberOfCustomers) { // Define the types and values used as a test data. final Map sampleCustomers = newHashMap(); @@ -319,7 +374,7 @@ private static void doCheckReadingCustomersById(int numberOfCustomers) { triggerMultipleUpdates(sampleCustomers, stand); // Now we are ready to query. - final EntityIdFilter idFilter = idFilterFor(sampleCustomers.keySet()); + final EntityIdFilter idFilter = idFilterForAggregate(sampleCustomers.keySet()); final Target customerTarget = Target.newBuilder() .setFilters(EntityFilters.newBuilder() @@ -340,7 +395,6 @@ private static void doCheckReadingCustomersById(int numberOfCustomers) { final Customer unpackedSingleResult = AnyPacker.unpack(singleRecord); assertTrue(allCustomers.contains(unpackedSingleResult)); } - } private static void checkEmptyResultOnNonEmptyStorageForQueryTarget(Target customerTarget) { @@ -370,7 +424,8 @@ private static void checkEmptyResultOnNonEmptyStorageForQueryTarget(Target custo @SuppressWarnings("ConstantConditions") - private static void setupExpectedBulkReadBehaviour(Map sampleCustomers, TypeUrl customerType, StandStorage standStorageMock) { + private static void setupExpectedBulkReadBehaviour(Map sampleCustomers, TypeUrl customerType, + StandStorage standStorageMock) { final ImmutableList.Builder stateIdsBuilder = ImmutableList.builder(); final ImmutableList.Builder recordsBuilder = ImmutableList.builder(); for (CustomerId customerId : sampleCustomers.keySet()) { @@ -393,6 +448,64 @@ private static void setupExpectedBulkReadBehaviour(Map sam when(standStorageMock.readBulk(matchingIds)).thenReturn(records); } + + @SuppressWarnings("ConstantConditions") + private static void setupExpectedFindAllBehaviour(Map sampleProjects, + StandTestProjectionRepository projectionRepository) { + + final Set projectIds = sampleProjects.keySet(); + final ImmutableCollection allResults = toProjectionCollection(projectIds); + + for (ProjectId projectId : projectIds) { + when(projectionRepository.find(eq(projectId))).thenReturn(new StandTestProjection(projectId)); + } + + final Iterable matchingIds = argThat(projectionIdsIterableMatcher(projectIds)); + when(projectionRepository.findBulk(matchingIds)).thenReturn(allResults); + + when(projectionRepository.findAll()).thenReturn(allResults); + + final EntityFilters matchingFilter = argThat(entityFilterMatcher(projectIds)); + when(projectionRepository.findAll(matchingFilter)).thenReturn(allResults); + } + + @SuppressWarnings("OverlyComplexAnonymousInnerClass") + private static ArgumentMatcher entityFilterMatcher(final Set projectIds) { + // This argument matcher does NOT mimic the exact repository behavior. + // Instead, it only matches the EntityFilters instance in case it has EntityIdFilter with ALL the expected IDs. + return new ArgumentMatcher() { + @Override + public boolean matches(EntityFilters argument) { + boolean everyElementPresent = true; + for (EntityId entityId : argument.getIdFilter() + .getIdsList()) { + final Any idAsAny = entityId.getId(); + final Message rawId = AnyPacker.unpack(idAsAny); + if (rawId instanceof ProjectId) { + final ProjectId convertedProjectId = (ProjectId) rawId; + everyElementPresent = everyElementPresent && projectIds.contains(convertedProjectId); + } else { + everyElementPresent = false; + } + } + return everyElementPresent; + } + }; + } + + private static ImmutableCollection toProjectionCollection(Collection values) { + final Collection transformed = Collections2.transform(values, new Function() { + @Nullable + @Override + public StandTestProjection apply(@Nullable ProjectId input) { + checkNotNull(input); + return new StandTestProjection(input); + } + }); + final ImmutableList result = ImmutableList.copyOf(transformed); + return result; + } + private static EntityId wrapCustomerId(int number) { final CustomerId customerId = CustomerId.newBuilder() .setNumber(number) @@ -403,7 +516,20 @@ private static EntityId wrapCustomerId(int number) { .build(); } - private static ArgumentMatcher> aggregateIdsIterableMatcher(final ImmutableList stateIds) { + private static ArgumentMatcher> projectionIdsIterableMatcher(final Set projectIds) { + return new ArgumentMatcher>() { + @Override + public boolean matches(Iterable argument) { + boolean everyElementPresent = true; + for (ProjectId projectId : argument) { + everyElementPresent = everyElementPresent && projectIds.contains(projectId); + } + return everyElementPresent; + } + }; + } + + private static ArgumentMatcher> aggregateIdsIterableMatcher(final List stateIds) { return new ArgumentMatcher>() { @Override public boolean matches(Iterable argument) { @@ -416,7 +542,7 @@ public boolean matches(Iterable argument) { }; } - private static EntityIdFilter idFilterFor(Collection customerIds) { + private static EntityIdFilter idFilterForAggregate(Collection customerIds) { final EntityIdFilter.Builder idFilterBuilder = EntityIdFilter.newBuilder(); for (CustomerId id : customerIds) { idFilterBuilder @@ -426,6 +552,16 @@ private static EntityIdFilter idFilterFor(Collection customerIds) { return idFilterBuilder.build(); } + private static EntityIdFilter idFilterForProjection(Collection projectIds) { + final EntityIdFilter.Builder idFilterBuilder = EntityIdFilter.newBuilder(); + for (ProjectId id : projectIds) { + idFilterBuilder + .addIds(EntityId.newBuilder() + .setId(AnyPacker.pack(id))); + } + return idFilterBuilder.build(); + } + private static void triggerMultipleUpdates(Map sampleCustomers, Stand stand) { // Trigger the aggregate state updates. for (CustomerId id : sampleCustomers.keySet()) { @@ -448,6 +584,17 @@ private static void fillSampleCustomers(Map sampleCustomer } } + private static void fillSampleProjects(Map sampleProjects, int numberOfProjects) { + for (int projectIndex = 0; projectIndex < numberOfProjects; projectIndex++) { + final Project project = Project.getDefaultInstance(); + final ProjectId projectId = ProjectId.newBuilder() + .setId(UUID.randomUUID() + .toString()) + .build(); + sampleProjects.put(projectId, project); + } + } + private static List checkAndGetMessageList(MemoizeQueryResponseObserver responseObserver) { assertTrue("Query has not completed successfully", responseObserver.isCompleted); assertNull("Throwable has been caught upon query execution", responseObserver.throwable); @@ -474,6 +621,14 @@ private static Stand prepareStandWithAggregateRepo(StandStorage standStorageMock return stand; } + private static Stand prepareStandWithProjectionRepo(ProjectionRepository projectionRepository) { + final Stand stand = Stand.newBuilder() + .build(); + assertNotNull(stand); + stand.registerTypeSupplier(projectionRepository); + return stand; + } + private static EntityStorageRecord recordStateMatcher(final EntityStorageRecord expectedRecord) { return argThat(new ArgumentMatcher() { @Override From 1eb84455e3275ed7a05ab408888f3d69a32393c5 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 21:51:17 +0300 Subject: [PATCH 36/98] Increase await time for concurrent check execution again. --- server/src/test/java/org/spine3/server/stand/Given.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/test/java/org/spine3/server/stand/Given.java b/server/src/test/java/org/spine3/server/stand/Given.java index 82fc42e059d..e0872d39b9f 100644 --- a/server/src/test/java/org/spine3/server/stand/Given.java +++ b/server/src/test/java/org/spine3/server/stand/Given.java @@ -58,7 +58,7 @@ /*package*/ static final int SEVERAL = THREADS_COUNT_IN_POOL_EXECUTOR; private static final String PROJECT_UUID = Identifiers.newUuid(); - public static final int AWAIT_SECONDS = 4; + /*package*/ static final int AWAIT_SECONDS = 10; private Given() { } From d34a80b67f93a80b849c5f924f089658f8460bbe Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 21:52:48 +0300 Subject: [PATCH 37/98] Reduce randomness of generated test data. --- server/src/test/java/org/spine3/server/stand/Given.java | 2 +- .../test/java/org/spine3/server/stand/StandFunnelShould.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/Given.java b/server/src/test/java/org/spine3/server/stand/Given.java index e0872d39b9f..d194aecc21f 100644 --- a/server/src/test/java/org/spine3/server/stand/Given.java +++ b/server/src/test/java/org/spine3/server/stand/Given.java @@ -58,7 +58,7 @@ /*package*/ static final int SEVERAL = THREADS_COUNT_IN_POOL_EXECUTOR; private static final String PROJECT_UUID = Identifiers.newUuid(); - /*package*/ static final int AWAIT_SECONDS = 10; + /*package*/ static final int AWAIT_SECONDS = 6; private Given() { } diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index b405de4777f..41be840a692 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -174,7 +174,7 @@ private static BoundedContextAction[] getSeveralRepositoryDispatchCalls() { final Random random = new SecureRandom(); for (int i = 0; i < result.length; i++) { - result[i] = random.nextBoolean() ? aggregateRepositoryDispatch() : projectionRepositoryDispatch(); + result[i] = (i % 2 == 0) ? aggregateRepositoryDispatch() : projectionRepositoryDispatch(); } return result; From ecfede812a9657acad0ac873636545f50f94ce28 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 23:29:36 +0300 Subject: [PATCH 38/98] Remove redundant imports. --- .../src/test/java/org/spine3/server/stand/StandShould.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/StandShould.java b/server/src/test/java/org/spine3/server/stand/StandShould.java index 5884a55cb1f..b2f4c35af8c 100644 --- a/server/src/test/java/org/spine3/server/stand/StandShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandShould.java @@ -41,15 +41,12 @@ import org.spine3.protobuf.TypeUrl; import org.spine3.server.BoundedContext; import org.spine3.server.Given; -import org.spine3.server.projection.Projection; -import org.spine3.server.projection.ProjectionRepository; -import org.spine3.server.storage.EntityStorageRecord; import org.spine3.server.stand.Given.StandTestProjectionRepository; +import org.spine3.server.storage.EntityStorageRecord; import org.spine3.server.storage.StandStorage; import org.spine3.test.clientservice.customer.Customer; import org.spine3.test.clientservice.customer.CustomerId; import org.spine3.test.projection.Project; -import org.spine3.test.projection.ProjectId; import java.util.List; import java.util.Objects; From dfd823db4d749779860e25fe307ef2cd9a2e8229 Mon Sep 17 00:00:00 2001 From: Alex Tymchenko Date: Thu, 15 Sep 2016 15:01:36 +0300 Subject: [PATCH 39/98] Test handling an unknown type in a Query. --- .../org/spine3/server/stand/StandShould.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/server/src/test/java/org/spine3/server/stand/StandShould.java b/server/src/test/java/org/spine3/server/stand/StandShould.java index 75ca1a56694..35170e85d6b 100644 --- a/server/src/test/java/org/spine3/server/stand/StandShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandShould.java @@ -221,6 +221,32 @@ public void return_empty_list_for_aggregate_read_all_on_empty_stand_storage() { checkEmptyResultForTargetOnEmptyStorage(customerTarget); } + @Test + public void return_empty_list_to_unknown_type_reading() { + + final Stand stand = Stand.newBuilder() + .build(); + + checkTypesEmpty(stand); + + // Customer type was NOT registered. + final TypeUrl customerType = TypeUrl.of(Customer.class); + final Target customerTarget = Target.newBuilder() + .setIncludeAll(true) + .setType(customerType.getTypeName()) + .build(); + final Query readAllCustomers = Query.newBuilder() + .setTarget(customerTarget) + .build(); + + final MemoizeQueryResponseObserver responseObserver = new MemoizeQueryResponseObserver(); + stand.execute(readAllCustomers, responseObserver); + + final List messageList = checkAndGetMessageList(responseObserver); + + assertTrue("Query returned a non-empty response message list for an unknown type", messageList.isEmpty()); + } + @Test public void return_empty_list_for_aggregate_read_by_ids_on_empty_stand_storage() { From ff73389a27a26942e5375a35fd086e434fabaf31 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Tue, 13 Sep 2016 14:10:41 +0300 Subject: [PATCH 40/98] Add creation test covering various Builder params. --- .../server/stand/StandFunnelShould.java | 36 ++++++++++++++++++- .../org/spine3/testdata/TestStandFactory.java | 2 +- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index 9e42f37e68e..5eb8fb42bdf 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -27,6 +27,8 @@ import org.spine3.testdata.TestStandFactory; import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.spy; @@ -40,7 +42,6 @@ public class StandFunnelShould { // **** Positive scenarios (unit) **** /** - * - Initialize properly with various Builder options; * - deliver mock updates to the stand (invoke proper methods with particular arguments) - test the delivery only. */ @@ -53,6 +54,39 @@ public void initialize_properly_with_stand_only() { Assert.assertNotNull(standFunnel); } + @Test + public void initialize_properly_with_various_builder_options() { + final Stand stand = TestStandFactory.create(); + final Executor executor = Executors.newSingleThreadExecutor(new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + return Thread.currentThread(); + } + }); + + final StandFunnel blockingFunnel = StandFunnel.newBuilder() + .setStand(stand) + .setExecutor(executor) + .build(); + Assert.assertNotNull(blockingFunnel); + + final StandFunnel funnelForBusyStand = StandFunnel.newBuilder() + .setStand(stand) + .setExecutor(Executors.newSingleThreadExecutor()) + .build(); + Assert.assertNotNull(funnelForBusyStand); + + + final StandFunnel emptyExecutorFunnel = StandFunnel.newBuilder() + .setStand(TestStandFactory.create()) + .setExecutor(new Executor() { + @Override + public void execute(Runnable neverCalled) { } + }) + .build(); + Assert.assertNotNull(emptyExecutorFunnel); + } + @Test public void use_executor_from_builder() { diff --git a/server/src/test/java/org/spine3/testdata/TestStandFactory.java b/server/src/test/java/org/spine3/testdata/TestStandFactory.java index d54df4fae6d..474a6226b30 100644 --- a/server/src/test/java/org/spine3/testdata/TestStandFactory.java +++ b/server/src/test/java/org/spine3/testdata/TestStandFactory.java @@ -22,13 +22,13 @@ package org.spine3.testdata; import org.spine3.server.stand.Stand; -import org.spine3.server.storage.memory.InMemoryStandStorage; /** * Creates stands for tests. * * @author Alex Tymchenko */ +@SuppressWarnings("UtilityClass") public class TestStandFactory { private TestStandFactory() {} From bfeec821673f8d04a3550da4ea81bfa58f751f0e Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Tue, 13 Sep 2016 14:11:56 +0300 Subject: [PATCH 41/98] Bump gradle version, --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 58e7b927f5d..6d19c1542b6 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-3.0-bin.zip From d141f24d8ebbe28bc21c0e6c070d633909879a24 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Tue, 13 Sep 2016 14:37:55 +0300 Subject: [PATCH 42/98] Add some more tests (need to be processed). --- .../org/spine3/server/stand/StandFunnel.java | 2 ++ .../server/stand/StandFunnelShould.java | 35 +++++++++++++++++-- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/server/src/main/java/org/spine3/server/stand/StandFunnel.java b/server/src/main/java/org/spine3/server/stand/StandFunnel.java index 9e3cc659ce0..44997adbdf8 100644 --- a/server/src/main/java/org/spine3/server/stand/StandFunnel.java +++ b/server/src/main/java/org/spine3/server/stand/StandFunnel.java @@ -109,6 +109,7 @@ public Stand getStand() { *

The value must not be null. * *

If this method is not used, a default value will be used. + * // TODO:13-09-16:dmytro.dashenkov: Correct docs. No default value for Stand is used, null value leads to a fail instead. * * @param stand the instance of {@link Stand}. * @return {@code this} instance of {@code Builder} @@ -122,6 +123,7 @@ public Executor getExecutor() { return executor; } + // TODO:13-09-16:dmytro.dashenkov: Complete docs. Here is the place where a default value is used in case of not using the method. /** * Set the {@code Executor} instance for this {@code StandFunnel}. * diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index 5eb8fb42bdf..da5f2bffdfd 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -87,6 +87,21 @@ public void execute(Runnable neverCalled) { } Assert.assertNotNull(emptyExecutorFunnel); } + @Test + public void deliver_mock_updates_to_stand() { + final Stand stand = spy(TestStandFactory.create()); + + final StandFunnel funnel = StandFunnel.newBuilder() + .setStand(stand) + .build(); + + final Object id = new Object(); + final Any state = Any.getDefaultInstance(); + funnel.post(id, state); + + verify(stand).update(id, state); + } + @Test public void use_executor_from_builder() { @@ -114,9 +129,23 @@ public void execute(Runnable command) { // **** Negative scenarios (unit) **** - /** - * - Fail to initialise with improper stand. - */ + @SuppressWarnings("ResultOfMethodCallIgnored") + @Test(expected = NullPointerException.class) + public void fail_to_initialize_with_null_stand() { + @SuppressWarnings("ConstantConditions") + final StandFunnel.Builder builder = StandFunnel.newBuilder().setStand(null); + + builder.build(); + } + + @SuppressWarnings("ResultOfMethodCallIgnored") + @Test(expected = NullPointerException.class) + public void fail_to_initialize_with_importer_stand() { + // TODO:13-09-16:dmytro.dashenkov: Implement. +// @SuppressWarnings("ConstantConditions") +// final StandFunnel.Builder builder = StandFunnel.newBuilder().setStand(null); +// builder.build(); + } @SuppressWarnings("ResultOfMethodCallIgnored") @Test(expected = IllegalStateException.class) From 927a50c9fa5b01363e52006c97b75dfb964f69f7 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Tue, 13 Sep 2016 15:16:06 +0300 Subject: [PATCH 43/98] Fix deliver mock updates test. --- .../server/stand/StandFunnelShould.java | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index da5f2bffdfd..ef7ba6c2b08 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -31,6 +31,8 @@ import java.util.concurrent.ThreadFactory; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -89,14 +91,16 @@ public void execute(Runnable neverCalled) { } @Test public void deliver_mock_updates_to_stand() { - final Stand stand = spy(TestStandFactory.create()); + final Object id = new Object(); + final Any state = Any.getDefaultInstance(); + + final Stand stand = mock(Stand.class); + doNothing().when(stand).update(id, state); final StandFunnel funnel = StandFunnel.newBuilder() .setStand(stand) .build(); - final Object id = new Object(); - final Any state = Any.getDefaultInstance(); funnel.post(id, state); verify(stand).update(id, state); @@ -131,22 +135,13 @@ public void execute(Runnable command) { @SuppressWarnings("ResultOfMethodCallIgnored") @Test(expected = NullPointerException.class) - public void fail_to_initialize_with_null_stand() { + public void fail_to_initialize_with_inproper_stand() { @SuppressWarnings("ConstantConditions") final StandFunnel.Builder builder = StandFunnel.newBuilder().setStand(null); builder.build(); } - @SuppressWarnings("ResultOfMethodCallIgnored") - @Test(expected = NullPointerException.class) - public void fail_to_initialize_with_importer_stand() { - // TODO:13-09-16:dmytro.dashenkov: Implement. -// @SuppressWarnings("ConstantConditions") -// final StandFunnel.Builder builder = StandFunnel.newBuilder().setStand(null); -// builder.build(); - } - @SuppressWarnings("ResultOfMethodCallIgnored") @Test(expected = IllegalStateException.class) public void fail_to_initialize_from_empty_builder() { @@ -162,5 +157,10 @@ public void fail_to_initialize_from_empty_builder() { * - deliver the updates from several projection and aggregate repositories. */ + @Test + public void deliver_updates_from_projection_repo() { + + } + } From 4e1f868461aa15209a409b99932733b833926475 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 13:57:38 +0300 Subject: [PATCH 44/98] Explicit "Given" for stand tests. --- .../java/org/spine3/server/stand/Given.java | 67 +++++++++++++++++++ .../org/spine3/server/stand/StandShould.java | 21 +----- 2 files changed, 68 insertions(+), 20 deletions(-) create mode 100644 server/src/test/java/org/spine3/server/stand/Given.java diff --git a/server/src/test/java/org/spine3/server/stand/Given.java b/server/src/test/java/org/spine3/server/stand/Given.java new file mode 100644 index 00000000000..f1b54d0adfe --- /dev/null +++ b/server/src/test/java/org/spine3/server/stand/Given.java @@ -0,0 +1,67 @@ +/* + * Copyright 2016, TeamDev Ltd. All rights reserved. + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.spine3.server.stand; + +import org.spine3.base.Event; +import org.spine3.base.EventContext; +import org.spine3.protobuf.AnyPacker; +import org.spine3.server.BoundedContext; +import org.spine3.server.projection.Projection; +import org.spine3.server.projection.ProjectionRepository; +import org.spine3.test.projection.Project; +import org.spine3.test.projection.ProjectId; +import org.spine3.test.projection.event.ProjectCreated; + +/** + * @author Dmytro Dashenkov + */ +/*package*/ class Given { + + /*package*/static class StandTestProjectionRepository extends ProjectionRepository { + /*package*/ StandTestProjectionRepository(BoundedContext boundedContext) { + super(boundedContext); + } + } + + private static class StandTestProjection extends Projection { + /** + * Creates a new instance. + * + * Required to be public. + * + * @param id the ID for the new instance + * @throws IllegalArgumentException if the ID is not of one of the supported types + */ + public StandTestProjection(ProjectId id) { + super(id); + } + } + + /*package*/ static Event validEvent() { + return Event.newBuilder() + .setMessage(AnyPacker.pack(ProjectCreated.newBuilder() + .setProjectId(ProjectId.newBuilder().setId("12345AD0")) + .build()) + .toBuilder().setTypeUrl(ProjectCreated.getDescriptor().getFullName()).build()) + .setContext(EventContext.getDefaultInstance()) + .build(); + } +} diff --git a/server/src/test/java/org/spine3/server/stand/StandShould.java b/server/src/test/java/org/spine3/server/stand/StandShould.java index 35170e85d6b..574998ef00d 100644 --- a/server/src/test/java/org/spine3/server/stand/StandShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandShould.java @@ -48,6 +48,7 @@ import org.spine3.server.projection.Projection; import org.spine3.server.projection.ProjectionRepository; import org.spine3.server.storage.EntityStorageRecord; +import org.spine3.server.stand.Given.StandTestProjectionRepository; import org.spine3.server.storage.StandStorage; import org.spine3.test.clientservice.customer.Customer; import org.spine3.test.clientservice.customer.CustomerId; @@ -700,26 +701,6 @@ public void onEntityStateUpdate(Any newEntityState) { // ***** Inner classes used for tests. ***** - private static class StandTestProjection extends Projection { - /** - * Creates a new instance. - * - * @param id the ID for the new instance - * @throws IllegalArgumentException if the ID is not of one of the supported types - */ - public StandTestProjection(ProjectId id) { - super(id); - } - } - - - private static class StandTestProjectionRepository extends ProjectionRepository { - protected StandTestProjectionRepository(BoundedContext boundedContext) { - super(boundedContext); - } - } - - /** * A {@link StreamObserver} storing the state of {@link Query} execution. */ From 7e814e9098a6c06b862b8f6980832eefccdb5914 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 14:14:40 +0300 Subject: [PATCH 45/98] Add threading test. --- .../server/stand/StandFunnelShould.java | 44 ++++++++++++++++++- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index ef7ba6c2b08..899afd85415 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -24,11 +24,16 @@ import com.google.protobuf.Any; import org.junit.Assert; import org.junit.Test; +import org.mockito.ArgumentMatchers; import org.spine3.testdata.TestStandFactory; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doNothing; @@ -38,6 +43,7 @@ /** * @author Alex Tymchenko + * @author Dmytro Dashenkov */ public class StandFunnelShould { @@ -157,10 +163,44 @@ public void fail_to_initialize_from_empty_builder() { * - deliver the updates from several projection and aggregate repositories. */ + + @SuppressWarnings("MethodWithMultipleLoops") @Test - public void deliver_updates_from_projection_repo() { + public void deliver_updates_through_several_threads() throws InterruptedException { + final int threadsCount = 10; - } + final Map threadInvakationRegistry = new ConcurrentHashMap<>(threadsCount); + + final Stand stand = mock(Stand.class); + doNothing().when(stand).update(ArgumentMatchers.any(), any(Any.class)); + + final StandFunnel standFunnel = StandFunnel.newBuilder() + .setStand(stand) + .build(); + + final ExecutorService processes = Executors.newFixedThreadPool(threadsCount); + + + final Runnable task = new Runnable() { + @Override + public void run() { + final String threadName = Thread.currentThread().getName(); + Assert.assertFalse(threadInvakationRegistry.containsKey(threadName)); + + standFunnel.post(new Object(), Any.getDefaultInstance()); + + threadInvakationRegistry.put(threadName, new Object()); + } + }; + for (int i = 0; i < threadsCount; i++) { + processes.execute(task); + } + + processes.awaitTermination(10, TimeUnit.SECONDS); + + Assert.assertEquals(threadInvakationRegistry.size(), threadsCount); + + } } From 13393bf57841f8bb19ee66fcc74b6272be94bac5 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 14:16:54 +0300 Subject: [PATCH 46/98] Reduce executor await time. --- .../test/java/org/spine3/server/stand/StandFunnelShould.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index 899afd85415..f47fa5efb48 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -168,6 +168,7 @@ public void fail_to_initialize_from_empty_builder() { @Test public void deliver_updates_through_several_threads() throws InterruptedException { final int threadsCount = 10; + final int threadExecuteionMaxAwaitSeconds = 2; final Map threadInvakationRegistry = new ConcurrentHashMap<>(threadsCount); @@ -197,7 +198,7 @@ public void run() { processes.execute(task); } - processes.awaitTermination(10, TimeUnit.SECONDS); + processes.awaitTermination(threadExecuteionMaxAwaitSeconds, TimeUnit.SECONDS); Assert.assertEquals(threadInvakationRegistry.size(), threadsCount); From 8d43860c8b670d55a5548e09939811dd98d5509b Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 14:54:11 +0300 Subject: [PATCH 47/98] Add deliver updates form projection repo test skeleton, --- .../java/org/spine3/server/stand/Given.java | 6 ++- .../server/stand/StandFunnelShould.java | 45 ++++++++++++++++--- 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/Given.java b/server/src/test/java/org/spine3/server/stand/Given.java index f1b54d0adfe..2f631d4995f 100644 --- a/server/src/test/java/org/spine3/server/stand/Given.java +++ b/server/src/test/java/org/spine3/server/stand/Given.java @@ -23,6 +23,7 @@ import org.spine3.base.Event; import org.spine3.base.EventContext; import org.spine3.protobuf.AnyPacker; +import org.spine3.protobuf.TypeUrl; import org.spine3.server.BoundedContext; import org.spine3.server.projection.Projection; import org.spine3.server.projection.ProjectionRepository; @@ -60,7 +61,10 @@ public StandTestProjection(ProjectId id) { .setMessage(AnyPacker.pack(ProjectCreated.newBuilder() .setProjectId(ProjectId.newBuilder().setId("12345AD0")) .build()) - .toBuilder().setTypeUrl(ProjectCreated.getDescriptor().getFullName()).build()) + .toBuilder() + .setTypeUrl(TypeUrl.SPINE_TYPE_URL_PREFIX + '/' + + ProjectCreated.getDescriptor().getFullName()) + .build()) .setContext(EventContext.getDefaultInstance()) .build(); } diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index f47fa5efb48..34e7ab2ff0f 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -25,6 +25,8 @@ import org.junit.Assert; import org.junit.Test; import org.mockito.ArgumentMatchers; +import org.spine3.server.BoundedContext; +import org.spine3.server.storage.memory.InMemoryStorageFactory; import org.spine3.testdata.TestStandFactory; import java.util.Map; @@ -40,6 +42,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; /** * @author Alex Tymchenko @@ -163,14 +166,44 @@ public void fail_to_initialize_from_empty_builder() { * - deliver the updates from several projection and aggregate repositories. */ + @Test + public void deliver_updates_from_projection_repository() throws Exception { + final BoundedContext boundedContext = mock(BoundedContext.class); + final Stand stand = mock(Stand.class); + when(boundedContext.getStandFunnel()).thenReturn(StandFunnel.newBuilder() + .setStand(stand) + .setExecutor(new Executor() { + @Override + public void execute(Runnable command) { + command.run(); + } + }) + .build()); + + // Init repository + final Given.StandTestProjectionRepository repository = new Given.StandTestProjectionRepository(boundedContext); + + stand.registerTypeSupplier(repository); + repository.initStorage(InMemoryStorageFactory.getInstance()); + repository.setOnline(); + + // Dispatch an update from projection repo + repository.dispatch(Given.validEvent()); + + // Was called ONCE + verify(boundedContext).getStandFunnel(); + verify(stand).update(ArgumentMatchers.any(), any(Any.class)); + } + @SuppressWarnings("MethodWithMultipleLoops") @Test public void deliver_updates_through_several_threads() throws InterruptedException { final int threadsCount = 10; - final int threadExecuteionMaxAwaitSeconds = 2; + @SuppressWarnings("LocalVariableNamingConvention") // Too long variable name + final int threadExecutionMaxAwaitSeconds = 2; - final Map threadInvakationRegistry = new ConcurrentHashMap<>(threadsCount); + final Map threadInvocationRegistry = new ConcurrentHashMap<>(threadsCount); final Stand stand = mock(Stand.class); doNothing().when(stand).update(ArgumentMatchers.any(), any(Any.class)); @@ -186,11 +219,11 @@ public void deliver_updates_through_several_threads() throws InterruptedExceptio @Override public void run() { final String threadName = Thread.currentThread().getName(); - Assert.assertFalse(threadInvakationRegistry.containsKey(threadName)); + Assert.assertFalse(threadInvocationRegistry.containsKey(threadName)); standFunnel.post(new Object(), Any.getDefaultInstance()); - threadInvakationRegistry.put(threadName, new Object()); + threadInvocationRegistry.put(threadName, new Object()); } }; @@ -198,9 +231,9 @@ public void run() { processes.execute(task); } - processes.awaitTermination(threadExecuteionMaxAwaitSeconds, TimeUnit.SECONDS); + processes.awaitTermination(threadExecutionMaxAwaitSeconds, TimeUnit.SECONDS); - Assert.assertEquals(threadInvakationRegistry.size(), threadsCount); + Assert.assertEquals(threadInvocationRegistry.size(), threadsCount); } From c95265c980c6abf755f2bf8a144a7795859c25f2 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 14:57:41 +0300 Subject: [PATCH 48/98] Suppress not yet ready test. --- .../test/java/org/spine3/server/stand/StandFunnelShould.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index 34e7ab2ff0f..c924469b833 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -166,7 +166,7 @@ public void fail_to_initialize_from_empty_builder() { * - deliver the updates from several projection and aggregate repositories. */ - @Test + //@Test public void deliver_updates_from_projection_repository() throws Exception { final BoundedContext boundedContext = mock(BoundedContext.class); final Stand stand = mock(Stand.class); From 1fa2b525d2f3dfdaf64ce4ca856f4990ea49c8a8 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 16:09:19 +0300 Subject: [PATCH 49/98] Finish delivery from projection repo test. --- .../java/org/spine3/server/stand/Given.java | 36 +++++++++++++++++-- .../server/stand/StandFunnelShould.java | 27 +++++++------- 2 files changed, 46 insertions(+), 17 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/Given.java b/server/src/test/java/org/spine3/server/stand/Given.java index 2f631d4995f..c7da6d078b9 100644 --- a/server/src/test/java/org/spine3/server/stand/Given.java +++ b/server/src/test/java/org/spine3/server/stand/Given.java @@ -20,11 +20,16 @@ package org.spine3.server.stand; +import com.google.protobuf.Message; +import org.spine3.base.CommandContext; import org.spine3.base.Event; import org.spine3.base.EventContext; +import org.spine3.base.EventId; +import org.spine3.base.Identifiers; import org.spine3.protobuf.AnyPacker; import org.spine3.protobuf.TypeUrl; import org.spine3.server.BoundedContext; +import org.spine3.server.event.Subscribe; import org.spine3.server.projection.Projection; import org.spine3.server.projection.ProjectionRepository; import org.spine3.test.projection.Project; @@ -36,10 +41,20 @@ */ /*package*/ class Given { + private static final String PROJECT_UUID = Identifiers.newUuid(); + + private Given() { + } + /*package*/static class StandTestProjectionRepository extends ProjectionRepository { /*package*/ StandTestProjectionRepository(BoundedContext boundedContext) { super(boundedContext); } + + @Override + protected ProjectId getEntityId(Message event, EventContext context) { + return ProjectId.newBuilder().setId(PROJECT_UUID).build(); + } } private static class StandTestProjection extends Projection { @@ -54,6 +69,11 @@ private static class StandTestProjection extends Projection public StandTestProjection(ProjectId id) { super(id); } + + @Subscribe + public void handle(ProjectCreated event, EventContext context) { + // Do nothing + } } /*package*/ static Event validEvent() { @@ -62,10 +82,20 @@ public StandTestProjection(ProjectId id) { .setProjectId(ProjectId.newBuilder().setId("12345AD0")) .build()) .toBuilder() - .setTypeUrl(TypeUrl.SPINE_TYPE_URL_PREFIX + '/' + - ProjectCreated.getDescriptor().getFullName()) + .setTypeUrl(TypeUrl.SPINE_TYPE_URL_PREFIX + "/" + ProjectCreated.getDescriptor().getFullName()) .build()) - .setContext(EventContext.getDefaultInstance()) + .setContext(EventContext.newBuilder() + .setDoNotEnrich(true) + .setCommandContext(CommandContext.getDefaultInstance()) + .setEventId(EventId.newBuilder() + .setUuid(PROJECT_UUID) + .build())) .build(); } + + /*package*/ static ProjectionRepository repo(BoundedContext context) { + return new StandTestProjectionRepository(context); + } + + } diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index c924469b833..4c9816159f1 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -26,6 +26,7 @@ import org.junit.Test; import org.mockito.ArgumentMatchers; import org.spine3.server.BoundedContext; +import org.spine3.server.projection.ProjectionRepository; import org.spine3.server.storage.memory.InMemoryStorageFactory; import org.spine3.testdata.TestStandFactory; @@ -42,7 +43,6 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; /** * @author Alex Tymchenko @@ -166,22 +166,21 @@ public void fail_to_initialize_from_empty_builder() { * - deliver the updates from several projection and aggregate repositories. */ - //@Test + @Test public void deliver_updates_from_projection_repository() throws Exception { - final BoundedContext boundedContext = mock(BoundedContext.class); final Stand stand = mock(Stand.class); - when(boundedContext.getStandFunnel()).thenReturn(StandFunnel.newBuilder() - .setStand(stand) - .setExecutor(new Executor() { - @Override - public void execute(Runnable command) { - command.run(); - } - }) - .build()); - + final BoundedContext boundedContext = spy(BoundedContext.newBuilder() + .setStand(stand) + .setStorageFactory(InMemoryStorageFactory.getInstance()) + .setStandFunnelExecutor(new Executor() { // Straightforward executor + @Override + public void execute(Runnable command) { + command.run(); + } + }) + .build()); // Init repository - final Given.StandTestProjectionRepository repository = new Given.StandTestProjectionRepository(boundedContext); + final ProjectionRepository repository = Given.repo(boundedContext); stand.registerTypeSupplier(repository); repository.initStorage(InMemoryStorageFactory.getInstance()); From 2660489d71f55ca208306692ec4b8fefc7e12bd5 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 17:26:06 +0300 Subject: [PATCH 50/98] Finish delivery form an aggregate repo test. --- .../java/org/spine3/server/stand/Given.java | 82 ++++++++++++++++++- .../server/stand/StandFunnelShould.java | 44 ++++++---- 2 files changed, 108 insertions(+), 18 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/Given.java b/server/src/test/java/org/spine3/server/stand/Given.java index c7da6d078b9..595397ce31c 100644 --- a/server/src/test/java/org/spine3/server/stand/Given.java +++ b/server/src/test/java/org/spine3/server/stand/Given.java @@ -20,22 +20,34 @@ package org.spine3.server.stand; +import com.google.protobuf.Any; import com.google.protobuf.Message; +import org.spine3.base.Command; import org.spine3.base.CommandContext; import org.spine3.base.Event; import org.spine3.base.EventContext; import org.spine3.base.EventId; +import org.spine3.base.FailureThrowable; import org.spine3.base.Identifiers; import org.spine3.protobuf.AnyPacker; import org.spine3.protobuf.TypeUrl; import org.spine3.server.BoundedContext; +import org.spine3.server.aggregate.Aggregate; +import org.spine3.server.aggregate.AggregateRepository; +import org.spine3.server.aggregate.Apply; +import org.spine3.server.command.Assign; import org.spine3.server.event.Subscribe; import org.spine3.server.projection.Projection; import org.spine3.server.projection.ProjectionRepository; +import org.spine3.server.storage.memory.InMemoryStorageFactory; import org.spine3.test.projection.Project; import org.spine3.test.projection.ProjectId; +import org.spine3.test.projection.command.CreateProject; import org.spine3.test.projection.event.ProjectCreated; +import java.util.List; +import java.util.concurrent.Executor; + /** * @author Dmytro Dashenkov */ @@ -46,7 +58,7 @@ private Given() { } - /*package*/static class StandTestProjectionRepository extends ProjectionRepository { + /*package*/ static class StandTestProjectionRepository extends ProjectionRepository { /*package*/ StandTestProjectionRepository(BoundedContext boundedContext) { super(boundedContext); } @@ -57,6 +69,48 @@ protected ProjectId getEntityId(Message event, EventContext context) { } } + /*package*/ static class StandTestAggregateRepository extends AggregateRepository { + + /** + * Creates a new repository instance. + * + * @param boundedContext the bounded context to which this repository belongs + */ + /*package*/ StandTestAggregateRepository(BoundedContext boundedContext) { + super(boundedContext); + } + } + + /*package*/ static class TestFailure extends FailureThrowable { + + /*package*/ TestFailure() { + super(/*generatedMessage=*/null); + } + } + + private static class StandTestAggregate extends Aggregate { + + /** + * Creates a new aggregate instance. + * + * @param id the ID for the new aggregate + * @throws IllegalArgumentException if the ID is not of one of the supported types + */ + public StandTestAggregate(ProjectId id) { + super(id); + } + + @Assign + public List handle(CreateProject createProject, CommandContext context) { + return null; + } + + @Apply + public void handle(ProjectCreated event) { + // Do nothing + } + } + private static class StandTestProjection extends Projection { /** * Creates a new instance. @@ -76,7 +130,7 @@ public void handle(ProjectCreated event, EventContext context) { } } - /*package*/ static Event validEvent() { + /*package*/ static Event validEvent() { return Event.newBuilder() .setMessage(AnyPacker.pack(ProjectCreated.newBuilder() .setProjectId(ProjectId.newBuilder().setId("12345AD0")) @@ -93,9 +147,31 @@ public void handle(ProjectCreated event, EventContext context) { .build(); } - /*package*/ static ProjectionRepository repo(BoundedContext context) { + /*package*/ static Command validCommand() { + return Command.newBuilder() + .setMessage(AnyPacker.pack(CreateProject.getDefaultInstance())) + .setContext(CommandContext.getDefaultInstance()) + .build(); + } + + /*package*/ static ProjectionRepository projectionRepo(BoundedContext context) { return new StandTestProjectionRepository(context); } + /*package*/ static AggregateRepository aggregateRepo(BoundedContext context) { + return new StandTestAggregateRepository(context); + } + /*package*/ static BoundedContext boundedContext(Stand stand) { + return BoundedContext.newBuilder() + .setStand(stand) + .setStorageFactory(InMemoryStorageFactory.getInstance()) + .setStandFunnelExecutor(new Executor() { // Straightforward executor + @Override + public void execute(Runnable command) { + command.run(); + } + }) + .build(); + } } diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index 4c9816159f1..ad82932db72 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -26,6 +26,7 @@ import org.junit.Test; import org.mockito.ArgumentMatchers; import org.spine3.server.BoundedContext; +import org.spine3.server.aggregate.AggregateRepository; import org.spine3.server.projection.ProjectionRepository; import org.spine3.server.storage.memory.InMemoryStorageFactory; import org.spine3.testdata.TestStandFactory; @@ -161,28 +162,18 @@ public void fail_to_initialize_from_empty_builder() { // **** Integration scenarios ( -> StandFunnel -> Mock Stand) **** /** - * - Deliver updates from projection repo on update; - * - deliver updates from aggregate repo on update; + * - Deliver updates from projection projectionRepo on update; + * - deliver updates from aggregate projectionRepo on update; * - deliver the updates from several projection and aggregate repositories. */ @Test - public void deliver_updates_from_projection_repository() throws Exception { + public void deliver_updates_from_projection_repository() { final Stand stand = mock(Stand.class); - final BoundedContext boundedContext = spy(BoundedContext.newBuilder() - .setStand(stand) - .setStorageFactory(InMemoryStorageFactory.getInstance()) - .setStandFunnelExecutor(new Executor() { // Straightforward executor - @Override - public void execute(Runnable command) { - command.run(); - } - }) - .build()); + final BoundedContext boundedContext = spy(Given.boundedContext(stand)); // Init repository - final ProjectionRepository repository = Given.repo(boundedContext); + final ProjectionRepository repository = Given.projectionRepo(boundedContext); - stand.registerTypeSupplier(repository); repository.initStorage(InMemoryStorageFactory.getInstance()); repository.setOnline(); @@ -194,6 +185,29 @@ public void execute(Runnable command) { verify(stand).update(ArgumentMatchers.any(), any(Any.class)); } + @Test + public void deliver_updates_form_aggregate_repository() { + final Stand stand = mock(Stand.class); + + final BoundedContext boundedContext = spy(Given.boundedContext(stand)); + // Init repository + final AggregateRepository repository = Given.aggregateRepo(boundedContext); + + stand.registerTypeSupplier(repository); + repository.initStorage(InMemoryStorageFactory.getInstance()); + + // Dispatch an update from projection projectionRepo + try { + repository.dispatch(Given.validCommand()); + } catch (IllegalStateException e) { + // Handle null event dispatch after command handling. + Assert.assertTrue(e.getMessage().contains("No record found for command ID: EMPTY")); + } + + // Was called ONCE + verify(boundedContext).getStandFunnel(); + verify(stand).update(ArgumentMatchers.any(), any(Any.class)); + } @SuppressWarnings("MethodWithMultipleLoops") @Test From 84fed4b52adb83ad4c542c389818d3b1a698e0e2 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 17:27:38 +0300 Subject: [PATCH 51/98] Delete redundant "TestFailure" declaration. --- server/src/test/java/org/spine3/server/stand/Given.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/Given.java b/server/src/test/java/org/spine3/server/stand/Given.java index 595397ce31c..18b75d76805 100644 --- a/server/src/test/java/org/spine3/server/stand/Given.java +++ b/server/src/test/java/org/spine3/server/stand/Given.java @@ -27,7 +27,6 @@ import org.spine3.base.Event; import org.spine3.base.EventContext; import org.spine3.base.EventId; -import org.spine3.base.FailureThrowable; import org.spine3.base.Identifiers; import org.spine3.protobuf.AnyPacker; import org.spine3.protobuf.TypeUrl; @@ -81,13 +80,6 @@ protected ProjectId getEntityId(Message event, EventContext context) { } } - /*package*/ static class TestFailure extends FailureThrowable { - - /*package*/ TestFailure() { - super(/*generatedMessage=*/null); - } - } - private static class StandTestAggregate extends Aggregate { /** From 8c93ffc351baccdedd1376491d849f82db93c1a7 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 17:40:54 +0300 Subject: [PATCH 52/98] Extract common test operations. --- .../server/stand/StandFunnelShould.java | 67 ++++++++++++------- 1 file changed, 42 insertions(+), 25 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index ad82932db72..3cac4e4acb7 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -39,6 +39,7 @@ import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; +import static com.google.common.base.Preconditions.checkNotNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; @@ -169,41 +170,53 @@ public void fail_to_initialize_from_empty_builder() { @Test public void deliver_updates_from_projection_repository() { - final Stand stand = mock(Stand.class); - final BoundedContext boundedContext = spy(Given.boundedContext(stand)); - // Init repository - final ProjectionRepository repository = Given.projectionRepo(boundedContext); - - repository.initStorage(InMemoryStorageFactory.getInstance()); - repository.setOnline(); + deliverUpdatesTest(new BoundedContextAction() { + @Override + public void perform(BoundedContext context) { + // Init repository + final ProjectionRepository repository = Given.projectionRepo(context); - // Dispatch an update from projection repo - repository.dispatch(Given.validEvent()); + repository.initStorage(InMemoryStorageFactory.getInstance()); + repository.setOnline(); - // Was called ONCE - verify(boundedContext).getStandFunnel(); - verify(stand).update(ArgumentMatchers.any(), any(Any.class)); + // Dispatch an update from projection repo + repository.dispatch(Given.validEvent()); + } + }); } @Test public void deliver_updates_form_aggregate_repository() { - final Stand stand = mock(Stand.class); + deliverUpdatesTest(new BoundedContextAction() { + @Override + public void perform(BoundedContext context) { + // Init repository + final AggregateRepository repository = Given.aggregateRepo(context); + + repository.initStorage(InMemoryStorageFactory.getInstance()); + try { + repository.dispatch(Given.validCommand()); + } catch (IllegalStateException e) { + // Handle null event dispatch after command handling. + Assert.assertTrue(e.getMessage().contains("No record found for command ID: EMPTY")); + } + + } + }); + } + + private static void deliverUpdatesTest(BoundedContextAction... dispatchActions) { + checkNotNull(dispatchActions); + + final Stand stand = mock(Stand.class); final BoundedContext boundedContext = spy(Given.boundedContext(stand)); - // Init repository - final AggregateRepository repository = Given.aggregateRepo(boundedContext); - - stand.registerTypeSupplier(repository); - repository.initStorage(InMemoryStorageFactory.getInstance()); - - // Dispatch an update from projection projectionRepo - try { - repository.dispatch(Given.validCommand()); - } catch (IllegalStateException e) { - // Handle null event dispatch after command handling. - Assert.assertTrue(e.getMessage().contains("No record found for command ID: EMPTY")); + + for (BoundedContextAction dispatchAction : dispatchActions) { + dispatchAction.perform(boundedContext); } + // Was called ONCE verify(boundedContext).getStandFunnel(); verify(stand).update(ArgumentMatchers.any(), any(Any.class)); @@ -250,4 +263,8 @@ public void run() { } + private interface BoundedContextAction { + void perform(BoundedContext context); + } + } From 15c815fc40fcf17dcdef28e922c9706c4570160e Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 18:21:34 +0300 Subject: [PATCH 53/98] Refactor and add delivery tests for several repositories successively and in parallel. --- .../java/org/spine3/server/stand/Given.java | 29 ++++-- .../server/stand/StandFunnelShould.java | 89 +++++++++++++------ 2 files changed, 82 insertions(+), 36 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/Given.java b/server/src/test/java/org/spine3/server/stand/Given.java index 18b75d76805..ccad28106a2 100644 --- a/server/src/test/java/org/spine3/server/stand/Given.java +++ b/server/src/test/java/org/spine3/server/stand/Given.java @@ -46,12 +46,16 @@ import java.util.List; import java.util.concurrent.Executor; +import java.util.concurrent.Executors; /** * @author Dmytro Dashenkov */ /*package*/ class Given { + /*package*/ static final int THREADS_COUNT_IN_POOL_EXECUTOR = 10; + /*package*/ static final int SEVERAL = THREADS_COUNT_IN_POOL_EXECUTOR; + private static final String PROJECT_UUID = Identifiers.newUuid(); private Given() { @@ -154,16 +158,23 @@ public void handle(ProjectCreated event, EventContext context) { return new StandTestAggregateRepository(context); } - /*package*/ static BoundedContext boundedContext(Stand stand) { + /*package*/ static BoundedContext boundedContext(Stand stand, int concurrentThreads) { + final Executor executor = concurrentThreads > 0 ? Executors.newFixedThreadPool(concurrentThreads) : + new Executor() { // Straightforward executor + @Override + public void execute(Runnable command) { + command.run(); + } + }; + + return boundedContextBuilder(stand) + .setStandFunnelExecutor(executor) + .build(); + } + + private static BoundedContext.Builder boundedContextBuilder(Stand stand) { return BoundedContext.newBuilder() .setStand(stand) - .setStorageFactory(InMemoryStorageFactory.getInstance()) - .setStandFunnelExecutor(new Executor() { // Straightforward executor - @Override - public void execute(Runnable command) { - command.run(); - } - }) - .build(); + .setStorageFactory(InMemoryStorageFactory.getInstance()); } } diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index 3cac4e4acb7..486697b153e 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -31,7 +31,9 @@ import org.spine3.server.storage.memory.InMemoryStorageFactory; import org.spine3.testdata.TestStandFactory; +import java.security.SecureRandom; import java.util.Map; +import java.util.Random; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; @@ -44,6 +46,7 @@ import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; /** @@ -170,24 +173,55 @@ public void fail_to_initialize_from_empty_builder() { @Test public void deliver_updates_from_projection_repository() { - deliverUpdatesTest(new BoundedContextAction() { - @Override - public void perform(BoundedContext context) { - // Init repository - final ProjectionRepository repository = Given.projectionRepo(context); + deliverUpdates(false, projectionRepositoryDispatch()); + } - repository.initStorage(InMemoryStorageFactory.getInstance()); - repository.setOnline(); + @Test + public void deliver_updates_form_aggregate_repository() { + deliverUpdates(false, aggregateRepositoryDispatch()); + } - // Dispatch an update from projection repo - repository.dispatch(Given.validEvent()); - } - }); + @Test + public void deliver_updates_from_several_repositories_in_single_thread() { + deliverUpdates(false, getSeveralRepositoryDispatchCalls()); } @Test - public void deliver_updates_form_aggregate_repository() { - deliverUpdatesTest(new BoundedContextAction() { + public void deliver_updates_from_several_repositories_in_multiple_threads() { + deliverUpdates(true, getSeveralRepositoryDispatchCalls()); + } + + private static BoundedContextAction[] getSeveralRepositoryDispatchCalls() { + final BoundedContextAction[] result = new BoundedContextAction[Given.SEVERAL]; + final Random random = new SecureRandom(); + + for (int i = 0; i < result.length; i++) { + result[i] = random.nextBoolean() ? aggregateRepositoryDispatch() : projectionRepositoryDispatch(); + } + + return result; + } + + private static void deliverUpdates(boolean isMultiThread, BoundedContextAction... dispatchActions) { + checkNotNull(dispatchActions); + + final Stand stand = mock(Stand.class); + final BoundedContext boundedContext = spy(Given.boundedContext(stand, + isMultiThread ? + Given.THREADS_COUNT_IN_POOL_EXECUTOR : 0)); + + for (BoundedContextAction dispatchAction : dispatchActions) { + dispatchAction.perform(boundedContext); + } + + + // Was called ONCE + verify(boundedContext, times(dispatchActions.length)).getStandFunnel(); + verify(stand, times(dispatchActions.length)).update(ArgumentMatchers.any(), any(Any.class)); + } + + private static BoundedContextAction aggregateRepositoryDispatch() { + return new BoundedContextAction() { @Override public void perform(BoundedContext context) { // Init repository @@ -201,31 +235,32 @@ public void perform(BoundedContext context) { // Handle null event dispatch after command handling. Assert.assertTrue(e.getMessage().contains("No record found for command ID: EMPTY")); } - } - }); + }; } - private static void deliverUpdatesTest(BoundedContextAction... dispatchActions) { - checkNotNull(dispatchActions); + private static BoundedContextAction projectionRepositoryDispatch() { + return new BoundedContextAction() { + @Override + public void perform(BoundedContext context) { + // Init repository + final ProjectionRepository repository = Given.projectionRepo(context); - final Stand stand = mock(Stand.class); - final BoundedContext boundedContext = spy(Given.boundedContext(stand)); + repository.initStorage(InMemoryStorageFactory.getInstance()); + repository.setOnline(); - for (BoundedContextAction dispatchAction : dispatchActions) { - dispatchAction.perform(boundedContext); - } + // Dispatch an update from projection repo + repository.dispatch(Given.validEvent()); + } + }; + } - // Was called ONCE - verify(boundedContext).getStandFunnel(); - verify(stand).update(ArgumentMatchers.any(), any(Any.class)); - } @SuppressWarnings("MethodWithMultipleLoops") @Test public void deliver_updates_through_several_threads() throws InterruptedException { - final int threadsCount = 10; + final int threadsCount = Given.THREADS_COUNT_IN_POOL_EXECUTOR; @SuppressWarnings("LocalVariableNamingConvention") // Too long variable name final int threadExecutionMaxAwaitSeconds = 2; From e1256f459553e1e99e48c31366f8b665dd3432c9 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Tue, 13 Sep 2016 14:10:41 +0300 Subject: [PATCH 54/98] Add creation test covering various Builder params. --- .../test/java/org/spine3/server/stand/StandFunnelShould.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index 486697b153e..5b39a7d1122 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -166,8 +166,8 @@ public void fail_to_initialize_from_empty_builder() { // **** Integration scenarios ( -> StandFunnel -> Mock Stand) **** /** - * - Deliver updates from projection projectionRepo on update; - * - deliver updates from aggregate projectionRepo on update; + * - Deliver updates from projection repo on update; + * - deliver updates from aggregate repo on update; * - deliver the updates from several projection and aggregate repositories. */ From f2697c216bf93ffab4b3eb7b182f5324d953230a Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Tue, 13 Sep 2016 14:37:55 +0300 Subject: [PATCH 55/98] Add some more tests (need to be processed). --- .../org/spine3/server/stand/StandFunnelShould.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index 5b39a7d1122..04054ea196e 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -105,16 +105,14 @@ public void execute(Runnable neverCalled) { } @Test public void deliver_mock_updates_to_stand() { - final Object id = new Object(); - final Any state = Any.getDefaultInstance(); - - final Stand stand = mock(Stand.class); - doNothing().when(stand).update(id, state); + final Stand stand = spy(TestStandFactory.create()); final StandFunnel funnel = StandFunnel.newBuilder() .setStand(stand) .build(); + final Object id = new Object(); + final Any state = Any.getDefaultInstance(); funnel.post(id, state); verify(stand).update(id, state); @@ -149,13 +147,14 @@ public void execute(Runnable command) { @SuppressWarnings("ResultOfMethodCallIgnored") @Test(expected = NullPointerException.class) - public void fail_to_initialize_with_inproper_stand() { + public void fail_to_initialize_with_null_stand() { @SuppressWarnings("ConstantConditions") final StandFunnel.Builder builder = StandFunnel.newBuilder().setStand(null); builder.build(); } + @SuppressWarnings("ResultOfMethodCallIgnored") @Test(expected = IllegalStateException.class) public void fail_to_initialize_from_empty_builder() { From 21fd7ae9c748f53ab2834b951b64f39e1fc36121 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Tue, 13 Sep 2016 15:16:06 +0300 Subject: [PATCH 56/98] Fix deliver mock updates test. --- .../test/java/org/spine3/server/stand/StandFunnelShould.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index 04054ea196e..59eeb7211d9 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -111,8 +111,6 @@ public void deliver_mock_updates_to_stand() { .setStand(stand) .build(); - final Object id = new Object(); - final Any state = Any.getDefaultInstance(); funnel.post(id, state); verify(stand).update(id, state); @@ -154,7 +152,6 @@ public void fail_to_initialize_with_null_stand() { builder.build(); } - @SuppressWarnings("ResultOfMethodCallIgnored") @Test(expected = IllegalStateException.class) public void fail_to_initialize_from_empty_builder() { From 83da39fa7af6195ca0405b101b115451770626e8 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 13:57:38 +0300 Subject: [PATCH 57/98] Explicit "Given" for stand tests. --- server/src/test/java/org/spine3/server/stand/Given.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/test/java/org/spine3/server/stand/Given.java b/server/src/test/java/org/spine3/server/stand/Given.java index ccad28106a2..c9bcb9499ac 100644 --- a/server/src/test/java/org/spine3/server/stand/Given.java +++ b/server/src/test/java/org/spine3/server/stand/Given.java @@ -126,7 +126,7 @@ public void handle(ProjectCreated event, EventContext context) { } } - /*package*/ static Event validEvent() { + /*package*/ static Event validEvent() { return Event.newBuilder() .setMessage(AnyPacker.pack(ProjectCreated.newBuilder() .setProjectId(ProjectId.newBuilder().setId("12345AD0")) From 10f2add7994c88eba884adc7b8f708216c7bfd54 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 14:14:40 +0300 Subject: [PATCH 58/98] Add threading test. --- .../test/java/org/spine3/server/stand/StandFunnelShould.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index 59eeb7211d9..338d67bf343 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -31,6 +31,8 @@ import org.spine3.server.storage.memory.InMemoryStorageFactory; import org.spine3.testdata.TestStandFactory; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.security.SecureRandom; import java.util.Map; import java.util.Random; @@ -167,6 +169,8 @@ public void fail_to_initialize_from_empty_builder() { * - deliver the updates from several projection and aggregate repositories. */ + + @SuppressWarnings("MethodWithMultipleLoops") @Test public void deliver_updates_from_projection_repository() { deliverUpdates(false, projectionRepositoryDispatch()); From c22281568e8f90076567f59e49ad9d60c389afa7 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 14:54:11 +0300 Subject: [PATCH 59/98] Add deliver updates form projection repo test skeleton, --- .../src/test/java/org/spine3/server/stand/StandFunnelShould.java | 1 + 1 file changed, 1 insertion(+) diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index 338d67bf343..ca3ba5e60ed 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -50,6 +50,7 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; /** * @author Alex Tymchenko From 7f8665b85dddaf2d06a79718c2abb696e7cce061 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 18:21:34 +0300 Subject: [PATCH 60/98] Refactor and add delivery tests for several repositories successively and in parallel. --- .../test/java/org/spine3/server/stand/StandFunnelShould.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index ca3ba5e60ed..b6197714be4 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -31,7 +31,9 @@ import org.spine3.server.storage.memory.InMemoryStorageFactory; import org.spine3.testdata.TestStandFactory; +import java.security.SecureRandom; import java.util.Map; +import java.util.Random; import java.util.concurrent.ConcurrentHashMap; import java.security.SecureRandom; import java.util.Map; From b3b7a1fbf36d86775eef8dc4a568423ce047a46b Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 18:47:07 +0300 Subject: [PATCH 61/98] Fix failing build by adding "await" for a concurrent test. --- .../java/org/spine3/server/stand/Given.java | 1 + .../spine3/server/stand/StandFunnelShould.java | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/Given.java b/server/src/test/java/org/spine3/server/stand/Given.java index c9bcb9499ac..6ab3e9b5145 100644 --- a/server/src/test/java/org/spine3/server/stand/Given.java +++ b/server/src/test/java/org/spine3/server/stand/Given.java @@ -57,6 +57,7 @@ /*package*/ static final int SEVERAL = THREADS_COUNT_IN_POOL_EXECUTOR; private static final String PROJECT_UUID = Identifiers.newUuid(); + public static final int AWAIT_SECONDS = 2; private Given() { } diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index b6197714be4..19eb9f85e96 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -217,12 +217,23 @@ private static void deliverUpdates(boolean isMultiThread, BoundedContextAction.. dispatchAction.perform(boundedContext); } - - // Was called ONCE + // Was called as much times as there are dispatch actions. verify(boundedContext, times(dispatchActions.length)).getStandFunnel(); + + if (isMultiThread) { + await(Given.AWAIT_SECONDS); + } + verify(stand, times(dispatchActions.length)).update(ArgumentMatchers.any(), any(Any.class)); } + private static void await(int seconds) { + try { + Thread.sleep(seconds); + } catch (InterruptedException ignore) { + } + } + private static BoundedContextAction aggregateRepositoryDispatch() { return new BoundedContextAction() { @Override @@ -259,13 +270,12 @@ public void perform(BoundedContext context) { } - @SuppressWarnings("MethodWithMultipleLoops") @Test public void deliver_updates_through_several_threads() throws InterruptedException { final int threadsCount = Given.THREADS_COUNT_IN_POOL_EXECUTOR; @SuppressWarnings("LocalVariableNamingConvention") // Too long variable name - final int threadExecutionMaxAwaitSeconds = 2; + final int threadExecutionMaxAwaitSeconds = Given.AWAIT_SECONDS; final Map threadInvocationRegistry = new ConcurrentHashMap<>(threadsCount); From a5528d57450dfd84ce94f8df09ac3122b454e20c Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 21:22:03 +0300 Subject: [PATCH 62/98] Fix issues form PR. --- .../src/test/java/org/spine3/server/stand/Given.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/Given.java b/server/src/test/java/org/spine3/server/stand/Given.java index 6ab3e9b5145..7f26931bdd8 100644 --- a/server/src/test/java/org/spine3/server/stand/Given.java +++ b/server/src/test/java/org/spine3/server/stand/Given.java @@ -20,6 +20,7 @@ package org.spine3.server.stand; +import com.google.common.util.concurrent.MoreExecutors; import com.google.protobuf.Any; import com.google.protobuf.Message; import org.spine3.base.Command; @@ -139,7 +140,7 @@ public void handle(ProjectCreated event, EventContext context) { .setDoNotEnrich(true) .setCommandContext(CommandContext.getDefaultInstance()) .setEventId(EventId.newBuilder() - .setUuid(PROJECT_UUID) + .setUuid(Identifiers.newUuid()) .build())) .build(); } @@ -161,12 +162,7 @@ public void handle(ProjectCreated event, EventContext context) { /*package*/ static BoundedContext boundedContext(Stand stand, int concurrentThreads) { final Executor executor = concurrentThreads > 0 ? Executors.newFixedThreadPool(concurrentThreads) : - new Executor() { // Straightforward executor - @Override - public void execute(Runnable command) { - command.run(); - } - }; + MoreExecutors.directExecutor(); return boundedContextBuilder(stand) .setStandFunnelExecutor(executor) From 7a794f7428a104ff9531200dd2053df0555c7821 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 21:36:39 +0300 Subject: [PATCH 63/98] Increase await time for concurrent check execution. --- server/src/test/java/org/spine3/server/stand/Given.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/test/java/org/spine3/server/stand/Given.java b/server/src/test/java/org/spine3/server/stand/Given.java index 7f26931bdd8..d1bbd98a8d5 100644 --- a/server/src/test/java/org/spine3/server/stand/Given.java +++ b/server/src/test/java/org/spine3/server/stand/Given.java @@ -58,7 +58,7 @@ /*package*/ static final int SEVERAL = THREADS_COUNT_IN_POOL_EXECUTOR; private static final String PROJECT_UUID = Identifiers.newUuid(); - public static final int AWAIT_SECONDS = 2; + public static final int AWAIT_SECONDS = 4; private Given() { } From 8669683a169bf9eea3a59ef3a677dc3ef4276786 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 21:51:17 +0300 Subject: [PATCH 64/98] Increase await time for concurrent check execution again. --- server/src/test/java/org/spine3/server/stand/Given.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/test/java/org/spine3/server/stand/Given.java b/server/src/test/java/org/spine3/server/stand/Given.java index d1bbd98a8d5..d5481c2d6bf 100644 --- a/server/src/test/java/org/spine3/server/stand/Given.java +++ b/server/src/test/java/org/spine3/server/stand/Given.java @@ -58,7 +58,7 @@ /*package*/ static final int SEVERAL = THREADS_COUNT_IN_POOL_EXECUTOR; private static final String PROJECT_UUID = Identifiers.newUuid(); - public static final int AWAIT_SECONDS = 4; + /*package*/ static final int AWAIT_SECONDS = 10; private Given() { } From a383f882c6d43b7076aa51075084156b7c0e3cff Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 21:52:48 +0300 Subject: [PATCH 65/98] Reduce randomness of generated test data. --- server/src/test/java/org/spine3/server/stand/Given.java | 2 +- .../test/java/org/spine3/server/stand/StandFunnelShould.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/Given.java b/server/src/test/java/org/spine3/server/stand/Given.java index d5481c2d6bf..fa15cd2f6df 100644 --- a/server/src/test/java/org/spine3/server/stand/Given.java +++ b/server/src/test/java/org/spine3/server/stand/Given.java @@ -58,7 +58,7 @@ /*package*/ static final int SEVERAL = THREADS_COUNT_IN_POOL_EXECUTOR; private static final String PROJECT_UUID = Identifiers.newUuid(); - /*package*/ static final int AWAIT_SECONDS = 10; + /*package*/ static final int AWAIT_SECONDS = 6; private Given() { } diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index 19eb9f85e96..b91d293681d 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -199,7 +199,7 @@ private static BoundedContextAction[] getSeveralRepositoryDispatchCalls() { final Random random = new SecureRandom(); for (int i = 0; i < result.length; i++) { - result[i] = random.nextBoolean() ? aggregateRepositoryDispatch() : projectionRepositoryDispatch(); + result[i] = (i % 2 == 0) ? aggregateRepositoryDispatch() : projectionRepositoryDispatch(); } return result; From 57f08a9aa053b04a40b2e37c33de1161a8a8e784 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 23:29:36 +0300 Subject: [PATCH 66/98] Remove redundant imports. --- .../src/test/java/org/spine3/server/stand/StandShould.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/StandShould.java b/server/src/test/java/org/spine3/server/stand/StandShould.java index 574998ef00d..dd464363e70 100644 --- a/server/src/test/java/org/spine3/server/stand/StandShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandShould.java @@ -45,15 +45,12 @@ import org.spine3.protobuf.TypeUrl; import org.spine3.server.BoundedContext; import org.spine3.server.Given; -import org.spine3.server.projection.Projection; -import org.spine3.server.projection.ProjectionRepository; -import org.spine3.server.storage.EntityStorageRecord; import org.spine3.server.stand.Given.StandTestProjectionRepository; +import org.spine3.server.storage.EntityStorageRecord; import org.spine3.server.storage.StandStorage; import org.spine3.test.clientservice.customer.Customer; import org.spine3.test.clientservice.customer.CustomerId; import org.spine3.test.projection.Project; -import org.spine3.test.projection.ProjectId; import javax.annotation.Nullable; import java.util.Collection; From cf7b94fe976d86a317803e65a1bdc5e66a628d64 Mon Sep 17 00:00:00 2001 From: Alex Tymchenko Date: Thu, 15 Sep 2016 16:20:16 +0300 Subject: [PATCH 67/98] Add a straightforward Stand#watch() test. --- .../org/spine3/server/stand/StandShould.java | 41 +++++++++++++++++-- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/StandShould.java b/server/src/test/java/org/spine3/server/stand/StandShould.java index 35170e85d6b..9228d51f346 100644 --- a/server/src/test/java/org/spine3/server/stand/StandShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandShould.java @@ -331,6 +331,26 @@ public void return_multiple_results_for_projection_batch_read_by_ids() { doCheckReadingProjectsById(TOTAL_PROJECTS_FOR_BATCH_READING); } + @Test + public void trigger_callback_upon_change_of_watched_aggregate_state() { + final Stand stand = prepareStandWithAggregateRepo(mock(StandStorage.class)); + final TypeUrl customerType = TypeUrl.of(Customer.class); + + final MemoizeStandUpdateCallback memoizeCallback = new MemoizeStandUpdateCallback(); + stand.watch(customerType, memoizeCallback); + assertNull(memoizeCallback.newEntityState); + + final Map.Entry sampleData = fillSampleCustomers(1).entrySet() + .iterator() + .next(); + final CustomerId customerId = sampleData.getKey(); + final Customer customer = sampleData.getValue(); + final Any packedState = AnyPacker.pack(customer); + stand.update(customerId, packedState); + + assertEquals(packedState, memoizeCallback.newEntityState); + } + private static void checkEmptyResultForTargetOnEmptyStorage(Target customerTarget) { final StandStorage standStorageMock = mock(StandStorage.class); // Return an empty collection on {@link StandStorage#readAllByType(TypeUrl)} call. @@ -388,9 +408,8 @@ private static void doCheckReadingProjectsById(int numberOfProjects) { private static void doCheckReadingCustomersById(int numberOfCustomers) { // Define the types and values used as a test data. - final Map sampleCustomers = newHashMap(); final TypeUrl customerType = TypeUrl.of(Customer.class); - fillSampleCustomers(sampleCustomers, numberOfCustomers); + final Map sampleCustomers = fillSampleCustomers(numberOfCustomers); // Prepare the stand and its mock storage to act. final StandStorage standStorageMock = mock(StandStorage.class); @@ -597,7 +616,8 @@ private static void triggerMultipleUpdates(Map sampleCusto } } - private static void fillSampleCustomers(Map sampleCustomers, int numberOfCustomers) { + private static Map fillSampleCustomers(int numberOfCustomers) { + final Map sampleCustomers = newHashMap(); for (int customerIndex = 0; customerIndex < numberOfCustomers; customerIndex++) { final Customer customer = Customer.getDefaultInstance(); @@ -608,6 +628,7 @@ private static void fillSampleCustomers(Map sampleCustomer .build(); sampleCustomers.put(customerId, customer); } + return sampleCustomers; } private static void fillSampleProjects(Map sampleProjects, int numberOfProjects) { @@ -745,4 +766,18 @@ public void onCompleted() { } } + + + /** + * A {@link StreamObserver} storing the state of {@link Query} execution. + */ + private static class MemoizeStandUpdateCallback implements Stand.StandUpdateCallback { + + private Any newEntityState; + + @Override + public void onEntityStateUpdate(Any newEntityState) { + this.newEntityState = newEntityState; + } + } } From 1659a0a402e7e12a6227597add9a0fe5edcfb3a0 Mon Sep 17 00:00:00 2001 From: Alex Tymchenko Date: Thu, 15 Sep 2016 16:26:26 +0300 Subject: [PATCH 68/98] * Define SubscriptionService and extract related objects as separate .proto files; * remove the SubscriptionService-related methods from the ClientService; * perform import cleanup in the modified files. --- .../src/main/proto/spine/client/client.proto | 1 - .../proto/spine/client/client_service.proto | 15 ---- .../main/proto/spine/client/entities.proto | 3 - .../proto/spine/client/query_service.proto | 1 - .../proto/spine/client/subscription.proto | 83 +++++++++++++++++++ .../spine/client/subscription_service.proto | 46 ++++++++++ .../java/org/spine3/server/ClientService.java | 19 ----- 7 files changed, 129 insertions(+), 39 deletions(-) create mode 100644 client/src/main/proto/spine/client/subscription.proto create mode 100644 client/src/main/proto/spine/client/subscription_service.proto diff --git a/client/src/main/proto/spine/client/client.proto b/client/src/main/proto/spine/client/client.proto index a20e7cb2e7b..c9f439fd170 100644 --- a/client/src/main/proto/spine/client/client.proto +++ b/client/src/main/proto/spine/client/client.proto @@ -28,7 +28,6 @@ option java_outer_classname = "ClientProto"; option java_package = "org.spine3.client"; import "spine/annotations.proto"; -import "spine/base/event.proto"; // Version of the code executed on the client. message CodeVersion { diff --git a/client/src/main/proto/spine/client/client_service.proto b/client/src/main/proto/spine/client/client_service.proto index e8a347e8133..d17e8e03585 100644 --- a/client/src/main/proto/spine/client/client_service.proto +++ b/client/src/main/proto/spine/client/client_service.proto @@ -31,26 +31,11 @@ option java_generate_equals_and_hash = true; import "spine/annotations.proto"; import "spine/base/command.proto"; -import "spine/base/event.proto"; import "spine/base/response.proto"; -// A topic of interest the client can subscribe and unsubscribe. -message Topic { - //TODO:2016-01-14:alexander.yevsyukov: Define this type. E.g. there can be some structure, which describes many - // points of interest at once. See Pub-sub for possible API inspiration. Chances are it's going to be one of underlying - // implementations. - string value = 1; -} // A service for sending commands from clients. service ClientService { // Request to handle a command. rpc Post(base.Command) returns (base.Response); - - // Request to receive events on the topic of interest. - rpc Subscribe(Topic) returns (stream base.Event); - - // The request to unsubscribe from the topic. - // This should close the stream opened by `Subscribe` call with the same `Topic` value. - rpc Unsubscribe(Topic) returns (base.Response); } diff --git a/client/src/main/proto/spine/client/entities.proto b/client/src/main/proto/spine/client/entities.proto index 617ef40e6d2..ab9d3596f09 100644 --- a/client/src/main/proto/spine/client/entities.proto +++ b/client/src/main/proto/spine/client/entities.proto @@ -28,11 +28,8 @@ option java_outer_classname = "EntitiesProto"; option java_package = "org.spine3.client"; import "google/protobuf/any.proto"; -import "google/protobuf/field_mask.proto"; import "spine/annotations.proto"; -import "spine/ui/language.proto"; -import "spine/base/response.proto"; // Represents an ID of an entity. // diff --git a/client/src/main/proto/spine/client/query_service.proto b/client/src/main/proto/spine/client/query_service.proto index 4689c7d8c38..36e7afec695 100644 --- a/client/src/main/proto/spine/client/query_service.proto +++ b/client/src/main/proto/spine/client/query_service.proto @@ -27,7 +27,6 @@ option java_multiple_files = true; option java_outer_classname = "QueryServiceProto"; option java_generate_equals_and_hash = true; -import "google/protobuf/any.proto"; import "spine/annotations.proto"; import "spine/client/query.proto"; diff --git a/client/src/main/proto/spine/client/subscription.proto b/client/src/main/proto/spine/client/subscription.proto new file mode 100644 index 00000000000..ddd579cd8c0 --- /dev/null +++ b/client/src/main/proto/spine/client/subscription.proto @@ -0,0 +1,83 @@ +// +// Copyright 2016, TeamDev Ltd. All rights reserved. +// +// Redistribution and use in source and/or binary forms, with or without +// modification, must retain the above copyright notice and the following +// disclaimer. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +syntax = "proto3"; + +package spine.client; + +option (type_url_prefix) = "type.spine3.org"; +option java_generate_equals_and_hash = true; +option java_multiple_files = true; +option java_outer_classname = "SubscriptionProto"; +option java_package = "org.spine3.client"; + + +import "google/protobuf/any.proto"; +import "google/protobuf/field_mask.proto"; + +import "spine/annotations.proto"; +import "spine/base/response.proto"; +import "spine/client/entities.proto"; + +// An object defining a unit of subscription. +// +// Defines the target (entities and criteria) of subscription. +message Topic { + + // Defines the entity of interest, e.g. entity type URL and a set of subscription criteria. + Target target = 1; + + // Field mask to be applied to the entity updates applicable to this topic. + // + // Applied to each of the entity state messages before returning in scope of SubscriptionUpdate. + google.protobuf.FieldMask field_mask = 2; + + // Reserved for utility fields. + reserved 3 to 6; +} + +// Wrapped set of read-side entity updates on a topic with the specific subscription ID. +message SubscriptionUpdate { + + // The ID of the current subscription. + SubscriptionId id = 1; + + // Represents the base part of the response. I.e. whether the Topic subscription requires has been acked or not. + base.Response response = 2; + + // Reserved for more subscription update attributes. + reserved 3 to 9; + + // Entity updates packed as Any. + // + // Each of the update messages is affected by the field mask set for the current subscription. + repeated google.protobuf.Any updates = 10; +} + +// The ID of the subscription. +// +// Created when the client subscribes to a topic. +// See SubscriptionService#Subscribe(Topic). +message SubscriptionId { + + // Unique identifier of the subscription. + // + // Typically built using Java's UUID.toString() functionality. + string uuid = 1; +} diff --git a/client/src/main/proto/spine/client/subscription_service.proto b/client/src/main/proto/spine/client/subscription_service.proto new file mode 100644 index 00000000000..0f679b04671 --- /dev/null +++ b/client/src/main/proto/spine/client/subscription_service.proto @@ -0,0 +1,46 @@ +// +// Copyright 2016, TeamDev Ltd. All rights reserved. +// +// Redistribution and use in source and/or binary forms, with or without +// modification, must retain the above copyright notice and the following +// disclaimer. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +syntax = "proto3"; + +package spine.client; + +option (type_url_prefix) = "type.spine3.org"; +option java_package = "org.spine3.client.grpc"; +option java_multiple_files = true; +option java_outer_classname = "SubscriptionServiceProto"; +option java_generate_equals_and_hash = true; + + +import "spine/annotations.proto"; +import "spine/client/subscription.proto"; +import "spine/base/response.proto"; + +// A service for subscribing to the read-side changes by clients. +service SubscriptionService { + + // Subscribe to a particular read-side updates. + // + // Topic defines the target of subscription and other attributes (like field masks). + // The result is a SubscriptionUpdate stream, + rpc Subscribe (Topic) returns (stream SubscriptionUpdate); + + // Cancel the subscription by its ID. + rpc CancelSubscription (SubscriptionId) returns (base.Response); +} diff --git a/server/src/main/java/org/spine3/server/ClientService.java b/server/src/main/java/org/spine3/server/ClientService.java index 7728c93007a..1ab5f73018b 100644 --- a/server/src/main/java/org/spine3/server/ClientService.java +++ b/server/src/main/java/org/spine3/server/ClientService.java @@ -26,10 +26,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.spine3.base.Command; -import org.spine3.base.Event; import org.spine3.base.Response; -import org.spine3.base.Responses; -import org.spine3.client.grpc.Topic; import org.spine3.server.command.error.CommandException; import org.spine3.server.command.error.UnsupportedCommandException; import org.spine3.server.type.CommandClass; @@ -75,22 +72,6 @@ private static void handleUnsupported(Command request, StreamObserver responseObserver.onError(Statuses.invalidArgumentWithCause(unsupported)); } - @SuppressWarnings("RefusedBequest") // as we override default implementation with `unimplemented` status. - @Override - public void subscribe(Topic request, StreamObserver responseObserver) { - //TODO:2016-05-25:alexander.yevsyukov: Subscribe the client to the topic in the corresponding BoundedContext. - // This API is likely to change to support Firebase-like registration where listening is - // done by the client SDK implementation. - } - - @SuppressWarnings("RefusedBequest") // as we override default implementation with `unimplemented` status. - @Override - public void unsubscribe(Topic request, StreamObserver responseObserver) { - //TODO:2016-05-25:alexander.yevsyukov: Unsubscribe the client from the topic in the corresponding BoundedContext. - responseObserver.onNext(Responses.ok()); - responseObserver.onCompleted(); - } - public static class Builder { private final Set boundedContexts = Sets.newHashSet(); From a23c6b848351acfcc9513f58f65d9b8fce229508 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Thu, 15 Sep 2016 16:32:06 +0300 Subject: [PATCH 69/98] Merge. --- .../java/org/spine3/server/stand/Given.java | 2 +- .../server/stand/StandFunnelShould.java | 10 +++---- .../org/spine3/server/stand/StandShould.java | 29 ++++++++++--------- .../org/spine3/testdata/TestStandFactory.java | 5 ++++ 4 files changed, 26 insertions(+), 20 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/Given.java b/server/src/test/java/org/spine3/server/stand/Given.java index fa15cd2f6df..2184ac0511d 100644 --- a/server/src/test/java/org/spine3/server/stand/Given.java +++ b/server/src/test/java/org/spine3/server/stand/Given.java @@ -109,7 +109,7 @@ public void handle(ProjectCreated event) { } } - private static class StandTestProjection extends Projection { + /*package*/ static class StandTestProjection extends Projection { /** * Creates a new instance. * diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index b91d293681d..5ecf2b8facc 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -31,10 +31,6 @@ import org.spine3.server.storage.memory.InMemoryStorageFactory; import org.spine3.testdata.TestStandFactory; -import java.security.SecureRandom; -import java.util.Map; -import java.util.Random; -import java.util.concurrent.ConcurrentHashMap; import java.security.SecureRandom; import java.util.Map; import java.util.Random; @@ -52,7 +48,6 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; /** * @author Alex Tymchenko @@ -110,7 +105,10 @@ public void execute(Runnable neverCalled) { } @Test public void deliver_mock_updates_to_stand() { - final Stand stand = spy(TestStandFactory.create()); + final Stand stand = TestStandFactory.createMock(); + doNothing().when(stand).update(ArgumentMatchers.any(), any(Any.class)); + final Object id = new Object(); + final Any state = Any.getDefaultInstance(); final StandFunnel funnel = StandFunnel.newBuilder() .setStand(stand) diff --git a/server/src/test/java/org/spine3/server/stand/StandShould.java b/server/src/test/java/org/spine3/server/stand/StandShould.java index dd464363e70..50197c17bae 100644 --- a/server/src/test/java/org/spine3/server/stand/StandShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandShould.java @@ -44,13 +44,16 @@ import org.spine3.protobuf.AnyPacker; import org.spine3.protobuf.TypeUrl; import org.spine3.server.BoundedContext; -import org.spine3.server.Given; +import org.spine3.server.Given.CustomerAggregate; +import org.spine3.server.Given.CustomerAggregateRepository; +import org.spine3.server.projection.ProjectionRepository; import org.spine3.server.stand.Given.StandTestProjectionRepository; import org.spine3.server.storage.EntityStorageRecord; import org.spine3.server.storage.StandStorage; import org.spine3.test.clientservice.customer.Customer; import org.spine3.test.clientservice.customer.CustomerId; import org.spine3.test.projection.Project; +import org.spine3.test.projection.ProjectId; import javax.annotation.Nullable; import java.util.Collection; @@ -138,7 +141,7 @@ public void register_aggregate_repositories() { checkTypesEmpty(stand); - final Given.CustomerAggregateRepository customerAggregateRepo = new Given.CustomerAggregateRepository(boundedContext); + final CustomerAggregateRepository customerAggregateRepo = new CustomerAggregateRepository(boundedContext); stand.registerTypeSupplier(customerAggregateRepo); final Descriptors.Descriptor customerEntityDescriptor = Customer.getDescriptor(); @@ -146,7 +149,7 @@ public void register_aggregate_repositories() { checkHasExactlyOne(stand.getKnownAggregateTypes(), customerEntityDescriptor); @SuppressWarnings("LocalVariableNamingConvention") - final Given.CustomerAggregateRepository anotherCustomerAggregateRepo = new Given.CustomerAggregateRepository(boundedContext); + final CustomerAggregateRepository anotherCustomerAggregateRepo = new CustomerAggregateRepository(boundedContext); stand.registerTypeSupplier(anotherCustomerAggregateRepo); checkHasExactlyOne(stand.getAvailableTypes(), customerEntityDescriptor); checkHasExactlyOne(stand.getKnownAggregateTypes(), customerEntityDescriptor); @@ -183,7 +186,7 @@ public void operate_with_storage_provided_through_builder() { assertNotNull(stand); final BoundedContext boundedContext = newBoundedContext(stand); - final Given.CustomerAggregateRepository customerAggregateRepo = new Given.CustomerAggregateRepository(boundedContext); + final CustomerAggregateRepository customerAggregateRepo = new CustomerAggregateRepository(boundedContext); stand.registerTypeSupplier(customerAggregateRepo); @@ -191,7 +194,7 @@ public void operate_with_storage_provided_through_builder() { final CustomerId customerId = CustomerId.newBuilder() .setNumber(numericIdValue) .build(); - final Given.CustomerAggregate customerAggregate = customerAggregateRepo.create(customerId); + final CustomerAggregate customerAggregate = customerAggregateRepo.create(customerId); final Customer customerState = customerAggregate.getState(); final Any packedState = AnyPacker.pack(customerState); final TypeUrl customerType = TypeUrl.of(Customer.class); @@ -478,10 +481,10 @@ private static void setupExpectedFindAllBehaviour(Map sample StandTestProjectionRepository projectionRepository) { final Set projectIds = sampleProjects.keySet(); - final ImmutableCollection allResults = toProjectionCollection(projectIds); + final ImmutableCollection allResults = toProjectionCollection(projectIds); for (ProjectId projectId : projectIds) { - when(projectionRepository.find(eq(projectId))).thenReturn(new StandTestProjection(projectId)); + when(projectionRepository.find(eq(projectId))).thenReturn(new Given.StandTestProjection(projectId)); } final Iterable matchingIds = argThat(projectionIdsIterableMatcher(projectIds)); @@ -517,16 +520,16 @@ public boolean matches(EntityFilters argument) { }; } - private static ImmutableCollection toProjectionCollection(Collection values) { - final Collection transformed = Collections2.transform(values, new Function() { + private static ImmutableCollection toProjectionCollection(Collection values) { + final Collection transformed = Collections2.transform(values, new Function() { @Nullable @Override - public StandTestProjection apply(@Nullable ProjectId input) { + public Given.StandTestProjection apply(@Nullable ProjectId input) { checkNotNull(input); - return new StandTestProjection(input); + return new Given.StandTestProjection(input); } }); - final ImmutableList result = ImmutableList.copyOf(transformed); + final ImmutableList result = ImmutableList.copyOf(transformed); return result; } @@ -640,7 +643,7 @@ private static Stand prepareStandWithAggregateRepo(StandStorage standStorageMock assertNotNull(stand); final BoundedContext boundedContext = newBoundedContext(stand); - final Given.CustomerAggregateRepository customerAggregateRepo = new Given.CustomerAggregateRepository(boundedContext); + final org.spine3.server.Given.CustomerAggregateRepository customerAggregateRepo = new org.spine3.server.Given.CustomerAggregateRepository(boundedContext); stand.registerTypeSupplier(customerAggregateRepo); return stand; } diff --git a/server/src/test/java/org/spine3/testdata/TestStandFactory.java b/server/src/test/java/org/spine3/testdata/TestStandFactory.java index 474a6226b30..63477a00a7c 100644 --- a/server/src/test/java/org/spine3/testdata/TestStandFactory.java +++ b/server/src/test/java/org/spine3/testdata/TestStandFactory.java @@ -21,6 +21,7 @@ */ package org.spine3.testdata; +import org.mockito.Mockito; import org.spine3.server.stand.Stand; /** @@ -38,4 +39,8 @@ public static Stand create() { .build(); return stand; } + + public static Stand createMock() { + return Mockito.mock(Stand.class); + } } From 7cc18e231d4ee3ea95686c909762f1b82af13788 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Thu, 15 Sep 2016 16:49:57 +0300 Subject: [PATCH 70/98] Address issues in PR. --- .../server/stand/StandFunnelShould.java | 82 ++++++------------- 1 file changed, 27 insertions(+), 55 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index 5ecf2b8facc..43cf6ed579c 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -21,7 +21,9 @@ */ package org.spine3.server.stand; +import com.google.common.util.concurrent.MoreExecutors; import com.google.protobuf.Any; +import io.netty.util.internal.ConcurrentSet; import org.junit.Assert; import org.junit.Test; import org.mockito.ArgumentMatchers; @@ -31,14 +33,10 @@ import org.spine3.server.storage.memory.InMemoryStorageFactory; import org.spine3.testdata.TestStandFactory; -import java.security.SecureRandom; -import java.util.Map; -import java.util.Random; -import java.util.concurrent.ConcurrentHashMap; +import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import static com.google.common.base.Preconditions.checkNotNull; @@ -71,36 +69,14 @@ public void initialize_properly_with_stand_only() { } @Test - public void initialize_properly_with_various_builder_options() { + public void initialize_properly_with_all_options() { final Stand stand = TestStandFactory.create(); - final Executor executor = Executors.newSingleThreadExecutor(new ThreadFactory() { - @Override - public Thread newThread(Runnable r) { - return Thread.currentThread(); - } - }); - final StandFunnel blockingFunnel = StandFunnel.newBuilder() + final StandFunnel standFunnel = StandFunnel.newBuilder() .setStand(stand) - .setExecutor(executor) + .setExecutor(Executors.newSingleThreadExecutor()) .build(); - Assert.assertNotNull(blockingFunnel); - - final StandFunnel funnelForBusyStand = StandFunnel.newBuilder() - .setStand(stand) - .setExecutor(Executors.newSingleThreadExecutor()) - .build(); - Assert.assertNotNull(funnelForBusyStand); - - - final StandFunnel emptyExecutorFunnel = StandFunnel.newBuilder() - .setStand(TestStandFactory.create()) - .setExecutor(new Executor() { - @Override - public void execute(Runnable neverCalled) { } - }) - .build(); - Assert.assertNotNull(emptyExecutorFunnel); + Assert.assertNotNull(standFunnel); } @Test @@ -122,13 +98,8 @@ public void deliver_mock_updates_to_stand() { @Test public void use_executor_from_builder() { - final Stand stand = spy(TestStandFactory.create()); - final Executor executor = spy(new Executor() { - @Override - public void execute(Runnable command) { - - } - }); + final Stand stand = TestStandFactory.createMock(); + final Executor executor = spy(MoreExecutors.directExecutor()); final StandFunnel.Builder builder = StandFunnel.newBuilder() .setStand(stand) .setExecutor(executor); @@ -174,27 +145,26 @@ public void fail_to_initialize_from_empty_builder() { @SuppressWarnings("MethodWithMultipleLoops") @Test public void deliver_updates_from_projection_repository() { - deliverUpdates(false, projectionRepositoryDispatch()); + checkUpdatesDelivery(false, projectionRepositoryDispatch()); } @Test public void deliver_updates_form_aggregate_repository() { - deliverUpdates(false, aggregateRepositoryDispatch()); + checkUpdatesDelivery(false, aggregateRepositoryDispatch()); } @Test public void deliver_updates_from_several_repositories_in_single_thread() { - deliverUpdates(false, getSeveralRepositoryDispatchCalls()); + checkUpdatesDelivery(false, getSeveralRepositoryDispatchCalls()); } @Test public void deliver_updates_from_several_repositories_in_multiple_threads() { - deliverUpdates(true, getSeveralRepositoryDispatchCalls()); + checkUpdatesDelivery(true, getSeveralRepositoryDispatchCalls()); } private static BoundedContextAction[] getSeveralRepositoryDispatchCalls() { final BoundedContextAction[] result = new BoundedContextAction[Given.SEVERAL]; - final Random random = new SecureRandom(); for (int i = 0; i < result.length; i++) { result[i] = (i % 2 == 0) ? aggregateRepositoryDispatch() : projectionRepositoryDispatch(); @@ -203,22 +173,22 @@ private static BoundedContextAction[] getSeveralRepositoryDispatchCalls() { return result; } - private static void deliverUpdates(boolean isMultiThread, BoundedContextAction... dispatchActions) { + private static void checkUpdatesDelivery(boolean isMultiThreaded, BoundedContextAction... dispatchActions) { checkNotNull(dispatchActions); final Stand stand = mock(Stand.class); final BoundedContext boundedContext = spy(Given.boundedContext(stand, - isMultiThread ? + isMultiThreaded ? Given.THREADS_COUNT_IN_POOL_EXECUTOR : 0)); for (BoundedContextAction dispatchAction : dispatchActions) { dispatchAction.perform(boundedContext); } - // Was called as much times as there are dispatch actions. + // Was called as many times as there are dispatch actions. verify(boundedContext, times(dispatchActions.length)).getStandFunnel(); - if (isMultiThread) { + if (isMultiThreaded) { await(Given.AWAIT_SECONDS); } @@ -244,8 +214,10 @@ public void perform(BoundedContext context) { try { repository.dispatch(Given.validCommand()); } catch (IllegalStateException e) { - // Handle null event dispatch after command handling. - Assert.assertTrue(e.getMessage().contains("No record found for command ID: EMPTY")); + // Handle null event dispatching after the command is handled. + if(!e.getMessage().contains("No record found for command ID: EMPTY")) { + throw e; + } } } }; @@ -275,7 +247,7 @@ public void deliver_updates_through_several_threads() throws InterruptedExceptio @SuppressWarnings("LocalVariableNamingConvention") // Too long variable name final int threadExecutionMaxAwaitSeconds = Given.AWAIT_SECONDS; - final Map threadInvocationRegistry = new ConcurrentHashMap<>(threadsCount); + final Set threadInvocationRegistry = new ConcurrentSet<>(); final Stand stand = mock(Stand.class); doNothing().when(stand).update(ArgumentMatchers.any(), any(Any.class)); @@ -284,26 +256,26 @@ public void deliver_updates_through_several_threads() throws InterruptedExceptio .setStand(stand) .build(); - final ExecutorService processes = Executors.newFixedThreadPool(threadsCount); + final ExecutorService executor = Executors.newFixedThreadPool(threadsCount); final Runnable task = new Runnable() { @Override public void run() { final String threadName = Thread.currentThread().getName(); - Assert.assertFalse(threadInvocationRegistry.containsKey(threadName)); + Assert.assertFalse(threadInvocationRegistry.contains(threadName)); standFunnel.post(new Object(), Any.getDefaultInstance()); - threadInvocationRegistry.put(threadName, new Object()); + threadInvocationRegistry.add(threadName); } }; for (int i = 0; i < threadsCount; i++) { - processes.execute(task); + executor.execute(task); } - processes.awaitTermination(threadExecutionMaxAwaitSeconds, TimeUnit.SECONDS); + executor.awaitTermination(threadExecutionMaxAwaitSeconds, TimeUnit.SECONDS); Assert.assertEquals(threadInvocationRegistry.size(), threadsCount); From 36b6d957c86dd680d830425a8638545667cdf485 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Thu, 15 Sep 2016 16:56:10 +0300 Subject: [PATCH 71/98] Fix broken test. --- .../java/org/spine3/server/stand/StandFunnelShould.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index 43cf6ed579c..e69e1a30c30 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -21,7 +21,6 @@ */ package org.spine3.server.stand; -import com.google.common.util.concurrent.MoreExecutors; import com.google.protobuf.Any; import io.netty.util.internal.ConcurrentSet; import org.junit.Assert; @@ -99,7 +98,11 @@ public void deliver_mock_updates_to_stand() { @Test public void use_executor_from_builder() { final Stand stand = TestStandFactory.createMock(); - final Executor executor = spy(MoreExecutors.directExecutor()); + final Executor executor = spy(new Executor() { + @Override + public void execute(Runnable command) { + } + }); final StandFunnel.Builder builder = StandFunnel.newBuilder() .setStand(stand) .setExecutor(executor); From 9b9dde6e23973edc4a94b8c914a9c62d963a2d5b Mon Sep 17 00:00:00 2001 From: Alex Tymchenko Date: Thu, 15 Sep 2016 17:08:01 +0300 Subject: [PATCH 72/98] Resolve compilation errors after moving Topic message. --- .../java/org/spine3/examples/aggregate/ClientApp.java | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/examples/src/main/java/org/spine3/examples/aggregate/ClientApp.java b/examples/src/main/java/org/spine3/examples/aggregate/ClientApp.java index c4599abcb6c..9df51a9117c 100644 --- a/examples/src/main/java/org/spine3/examples/aggregate/ClientApp.java +++ b/examples/src/main/java/org/spine3/examples/aggregate/ClientApp.java @@ -31,7 +31,6 @@ import org.spine3.base.Response; import org.spine3.client.CommandFactory; import org.spine3.client.grpc.ClientServiceGrpc; -import org.spine3.client.grpc.Topic; import org.spine3.examples.aggregate.command.AddOrderLine; import org.spine3.examples.aggregate.command.CreateOrder; import org.spine3.examples.aggregate.command.PayForOrder; @@ -66,9 +65,9 @@ public class ClientApp { private static final int SHUTDOWN_TIMEOUT_SEC = 5; private final CommandFactory commandFactory; - private final Topic topic = Topic.getDefaultInstance(); private final ManagedChannel channel; private final ClientServiceGrpc.ClientServiceBlockingStub blockingClient; + // TODO[alex.tymchenko]: switch to SubscriptionService instead. private final ClientServiceGrpc.ClientServiceStub nonBlockingClient; private final StreamObserver observer = new StreamObserver() { @@ -140,14 +139,10 @@ private Command payForOrder(OrderId orderId) { return commandFactory.create(msg); } - private void subscribe() { - nonBlockingClient.subscribe(topic, observer); - } /** Sends requests to the server. */ public static void main(String[] args) throws InterruptedException { final ClientApp client = new ClientApp(SERVICE_HOST, DEFAULT_CLIENT_SERVICE_PORT); - client.subscribe(); final List requests = client.generateRequests(); @@ -178,7 +173,6 @@ private List generateRequests() { * @throws InterruptedException if waiting is interrupted. */ private void shutdown() throws InterruptedException { - blockingClient.unsubscribe(topic); channel.shutdown().awaitTermination(SHUTDOWN_TIMEOUT_SEC, SECONDS); } From ed8784b4346fc6730f75cc268bc8128c6c350c33 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Tue, 13 Sep 2016 14:10:41 +0300 Subject: [PATCH 73/98] Add creation test covering various Builder params. --- .../server/stand/StandFunnelShould.java | 36 ++++++++++++++++++- .../org/spine3/testdata/TestStandFactory.java | 2 +- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index 9e42f37e68e..5eb8fb42bdf 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -27,6 +27,8 @@ import org.spine3.testdata.TestStandFactory; import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.spy; @@ -40,7 +42,6 @@ public class StandFunnelShould { // **** Positive scenarios (unit) **** /** - * - Initialize properly with various Builder options; * - deliver mock updates to the stand (invoke proper methods with particular arguments) - test the delivery only. */ @@ -53,6 +54,39 @@ public void initialize_properly_with_stand_only() { Assert.assertNotNull(standFunnel); } + @Test + public void initialize_properly_with_various_builder_options() { + final Stand stand = TestStandFactory.create(); + final Executor executor = Executors.newSingleThreadExecutor(new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + return Thread.currentThread(); + } + }); + + final StandFunnel blockingFunnel = StandFunnel.newBuilder() + .setStand(stand) + .setExecutor(executor) + .build(); + Assert.assertNotNull(blockingFunnel); + + final StandFunnel funnelForBusyStand = StandFunnel.newBuilder() + .setStand(stand) + .setExecutor(Executors.newSingleThreadExecutor()) + .build(); + Assert.assertNotNull(funnelForBusyStand); + + + final StandFunnel emptyExecutorFunnel = StandFunnel.newBuilder() + .setStand(TestStandFactory.create()) + .setExecutor(new Executor() { + @Override + public void execute(Runnable neverCalled) { } + }) + .build(); + Assert.assertNotNull(emptyExecutorFunnel); + } + @Test public void use_executor_from_builder() { diff --git a/server/src/test/java/org/spine3/testdata/TestStandFactory.java b/server/src/test/java/org/spine3/testdata/TestStandFactory.java index d54df4fae6d..474a6226b30 100644 --- a/server/src/test/java/org/spine3/testdata/TestStandFactory.java +++ b/server/src/test/java/org/spine3/testdata/TestStandFactory.java @@ -22,13 +22,13 @@ package org.spine3.testdata; import org.spine3.server.stand.Stand; -import org.spine3.server.storage.memory.InMemoryStandStorage; /** * Creates stands for tests. * * @author Alex Tymchenko */ +@SuppressWarnings("UtilityClass") public class TestStandFactory { private TestStandFactory() {} From c5ab931fbfe8cf8257ee636382e965ff45965e18 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Tue, 13 Sep 2016 14:11:56 +0300 Subject: [PATCH 74/98] Bump gradle version, --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 58e7b927f5d..6d19c1542b6 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-3.0-bin.zip From 756df6429802a48a01d8701ade0636bccce3898e Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Tue, 13 Sep 2016 14:37:55 +0300 Subject: [PATCH 75/98] Add some more tests (need to be processed). --- .../org/spine3/server/stand/StandFunnel.java | 2 ++ .../server/stand/StandFunnelShould.java | 35 +++++++++++++++++-- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/server/src/main/java/org/spine3/server/stand/StandFunnel.java b/server/src/main/java/org/spine3/server/stand/StandFunnel.java index 9e3cc659ce0..44997adbdf8 100644 --- a/server/src/main/java/org/spine3/server/stand/StandFunnel.java +++ b/server/src/main/java/org/spine3/server/stand/StandFunnel.java @@ -109,6 +109,7 @@ public Stand getStand() { *

The value must not be null. * *

If this method is not used, a default value will be used. + * // TODO:13-09-16:dmytro.dashenkov: Correct docs. No default value for Stand is used, null value leads to a fail instead. * * @param stand the instance of {@link Stand}. * @return {@code this} instance of {@code Builder} @@ -122,6 +123,7 @@ public Executor getExecutor() { return executor; } + // TODO:13-09-16:dmytro.dashenkov: Complete docs. Here is the place where a default value is used in case of not using the method. /** * Set the {@code Executor} instance for this {@code StandFunnel}. * diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index 5eb8fb42bdf..da5f2bffdfd 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -87,6 +87,21 @@ public void execute(Runnable neverCalled) { } Assert.assertNotNull(emptyExecutorFunnel); } + @Test + public void deliver_mock_updates_to_stand() { + final Stand stand = spy(TestStandFactory.create()); + + final StandFunnel funnel = StandFunnel.newBuilder() + .setStand(stand) + .build(); + + final Object id = new Object(); + final Any state = Any.getDefaultInstance(); + funnel.post(id, state); + + verify(stand).update(id, state); + } + @Test public void use_executor_from_builder() { @@ -114,9 +129,23 @@ public void execute(Runnable command) { // **** Negative scenarios (unit) **** - /** - * - Fail to initialise with improper stand. - */ + @SuppressWarnings("ResultOfMethodCallIgnored") + @Test(expected = NullPointerException.class) + public void fail_to_initialize_with_null_stand() { + @SuppressWarnings("ConstantConditions") + final StandFunnel.Builder builder = StandFunnel.newBuilder().setStand(null); + + builder.build(); + } + + @SuppressWarnings("ResultOfMethodCallIgnored") + @Test(expected = NullPointerException.class) + public void fail_to_initialize_with_importer_stand() { + // TODO:13-09-16:dmytro.dashenkov: Implement. +// @SuppressWarnings("ConstantConditions") +// final StandFunnel.Builder builder = StandFunnel.newBuilder().setStand(null); +// builder.build(); + } @SuppressWarnings("ResultOfMethodCallIgnored") @Test(expected = IllegalStateException.class) From 26fb807f08c2004dbcde4e5942b93ea59f31adc6 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Tue, 13 Sep 2016 15:16:06 +0300 Subject: [PATCH 76/98] Fix deliver mock updates test. --- .../server/stand/StandFunnelShould.java | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index da5f2bffdfd..ef7ba6c2b08 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -31,6 +31,8 @@ import java.util.concurrent.ThreadFactory; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -89,14 +91,16 @@ public void execute(Runnable neverCalled) { } @Test public void deliver_mock_updates_to_stand() { - final Stand stand = spy(TestStandFactory.create()); + final Object id = new Object(); + final Any state = Any.getDefaultInstance(); + + final Stand stand = mock(Stand.class); + doNothing().when(stand).update(id, state); final StandFunnel funnel = StandFunnel.newBuilder() .setStand(stand) .build(); - final Object id = new Object(); - final Any state = Any.getDefaultInstance(); funnel.post(id, state); verify(stand).update(id, state); @@ -131,22 +135,13 @@ public void execute(Runnable command) { @SuppressWarnings("ResultOfMethodCallIgnored") @Test(expected = NullPointerException.class) - public void fail_to_initialize_with_null_stand() { + public void fail_to_initialize_with_inproper_stand() { @SuppressWarnings("ConstantConditions") final StandFunnel.Builder builder = StandFunnel.newBuilder().setStand(null); builder.build(); } - @SuppressWarnings("ResultOfMethodCallIgnored") - @Test(expected = NullPointerException.class) - public void fail_to_initialize_with_importer_stand() { - // TODO:13-09-16:dmytro.dashenkov: Implement. -// @SuppressWarnings("ConstantConditions") -// final StandFunnel.Builder builder = StandFunnel.newBuilder().setStand(null); -// builder.build(); - } - @SuppressWarnings("ResultOfMethodCallIgnored") @Test(expected = IllegalStateException.class) public void fail_to_initialize_from_empty_builder() { @@ -162,5 +157,10 @@ public void fail_to_initialize_from_empty_builder() { * - deliver the updates from several projection and aggregate repositories. */ + @Test + public void deliver_updates_from_projection_repo() { + + } + } From abec37b74eeb37f7da6d8555f504cd3bfcb5ab94 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 13:57:38 +0300 Subject: [PATCH 77/98] Explicit "Given" for stand tests. --- .../java/org/spine3/server/stand/Given.java | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 server/src/test/java/org/spine3/server/stand/Given.java diff --git a/server/src/test/java/org/spine3/server/stand/Given.java b/server/src/test/java/org/spine3/server/stand/Given.java new file mode 100644 index 00000000000..f1b54d0adfe --- /dev/null +++ b/server/src/test/java/org/spine3/server/stand/Given.java @@ -0,0 +1,67 @@ +/* + * Copyright 2016, TeamDev Ltd. All rights reserved. + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.spine3.server.stand; + +import org.spine3.base.Event; +import org.spine3.base.EventContext; +import org.spine3.protobuf.AnyPacker; +import org.spine3.server.BoundedContext; +import org.spine3.server.projection.Projection; +import org.spine3.server.projection.ProjectionRepository; +import org.spine3.test.projection.Project; +import org.spine3.test.projection.ProjectId; +import org.spine3.test.projection.event.ProjectCreated; + +/** + * @author Dmytro Dashenkov + */ +/*package*/ class Given { + + /*package*/static class StandTestProjectionRepository extends ProjectionRepository { + /*package*/ StandTestProjectionRepository(BoundedContext boundedContext) { + super(boundedContext); + } + } + + private static class StandTestProjection extends Projection { + /** + * Creates a new instance. + * + * Required to be public. + * + * @param id the ID for the new instance + * @throws IllegalArgumentException if the ID is not of one of the supported types + */ + public StandTestProjection(ProjectId id) { + super(id); + } + } + + /*package*/ static Event validEvent() { + return Event.newBuilder() + .setMessage(AnyPacker.pack(ProjectCreated.newBuilder() + .setProjectId(ProjectId.newBuilder().setId("12345AD0")) + .build()) + .toBuilder().setTypeUrl(ProjectCreated.getDescriptor().getFullName()).build()) + .setContext(EventContext.getDefaultInstance()) + .build(); + } +} From e3313a87a3f7805a833375a8f764e27e8ca01aab Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 14:14:40 +0300 Subject: [PATCH 78/98] Add threading test. --- .../server/stand/StandFunnelShould.java | 44 ++++++++++++++++++- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index ef7ba6c2b08..899afd85415 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -24,11 +24,16 @@ import com.google.protobuf.Any; import org.junit.Assert; import org.junit.Test; +import org.mockito.ArgumentMatchers; import org.spine3.testdata.TestStandFactory; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doNothing; @@ -38,6 +43,7 @@ /** * @author Alex Tymchenko + * @author Dmytro Dashenkov */ public class StandFunnelShould { @@ -157,10 +163,44 @@ public void fail_to_initialize_from_empty_builder() { * - deliver the updates from several projection and aggregate repositories. */ + + @SuppressWarnings("MethodWithMultipleLoops") @Test - public void deliver_updates_from_projection_repo() { + public void deliver_updates_through_several_threads() throws InterruptedException { + final int threadsCount = 10; - } + final Map threadInvakationRegistry = new ConcurrentHashMap<>(threadsCount); + + final Stand stand = mock(Stand.class); + doNothing().when(stand).update(ArgumentMatchers.any(), any(Any.class)); + + final StandFunnel standFunnel = StandFunnel.newBuilder() + .setStand(stand) + .build(); + + final ExecutorService processes = Executors.newFixedThreadPool(threadsCount); + + + final Runnable task = new Runnable() { + @Override + public void run() { + final String threadName = Thread.currentThread().getName(); + Assert.assertFalse(threadInvakationRegistry.containsKey(threadName)); + + standFunnel.post(new Object(), Any.getDefaultInstance()); + + threadInvakationRegistry.put(threadName, new Object()); + } + }; + for (int i = 0; i < threadsCount; i++) { + processes.execute(task); + } + + processes.awaitTermination(10, TimeUnit.SECONDS); + + Assert.assertEquals(threadInvakationRegistry.size(), threadsCount); + + } } From 1e4338a06eaecc5c69ac6e12144c6f377ce71bf6 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 14:16:54 +0300 Subject: [PATCH 79/98] Reduce executor await time. --- .../test/java/org/spine3/server/stand/StandFunnelShould.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index 899afd85415..f47fa5efb48 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -168,6 +168,7 @@ public void fail_to_initialize_from_empty_builder() { @Test public void deliver_updates_through_several_threads() throws InterruptedException { final int threadsCount = 10; + final int threadExecuteionMaxAwaitSeconds = 2; final Map threadInvakationRegistry = new ConcurrentHashMap<>(threadsCount); @@ -197,7 +198,7 @@ public void run() { processes.execute(task); } - processes.awaitTermination(10, TimeUnit.SECONDS); + processes.awaitTermination(threadExecuteionMaxAwaitSeconds, TimeUnit.SECONDS); Assert.assertEquals(threadInvakationRegistry.size(), threadsCount); From ca292e9184da4202951c407cacc2ba7aa55f2932 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 14:54:11 +0300 Subject: [PATCH 80/98] Add deliver updates form projection repo test skeleton, --- .../java/org/spine3/server/stand/Given.java | 6 ++- .../server/stand/StandFunnelShould.java | 45 ++++++++++++++++--- 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/Given.java b/server/src/test/java/org/spine3/server/stand/Given.java index f1b54d0adfe..2f631d4995f 100644 --- a/server/src/test/java/org/spine3/server/stand/Given.java +++ b/server/src/test/java/org/spine3/server/stand/Given.java @@ -23,6 +23,7 @@ import org.spine3.base.Event; import org.spine3.base.EventContext; import org.spine3.protobuf.AnyPacker; +import org.spine3.protobuf.TypeUrl; import org.spine3.server.BoundedContext; import org.spine3.server.projection.Projection; import org.spine3.server.projection.ProjectionRepository; @@ -60,7 +61,10 @@ public StandTestProjection(ProjectId id) { .setMessage(AnyPacker.pack(ProjectCreated.newBuilder() .setProjectId(ProjectId.newBuilder().setId("12345AD0")) .build()) - .toBuilder().setTypeUrl(ProjectCreated.getDescriptor().getFullName()).build()) + .toBuilder() + .setTypeUrl(TypeUrl.SPINE_TYPE_URL_PREFIX + '/' + + ProjectCreated.getDescriptor().getFullName()) + .build()) .setContext(EventContext.getDefaultInstance()) .build(); } diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index f47fa5efb48..34e7ab2ff0f 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -25,6 +25,8 @@ import org.junit.Assert; import org.junit.Test; import org.mockito.ArgumentMatchers; +import org.spine3.server.BoundedContext; +import org.spine3.server.storage.memory.InMemoryStorageFactory; import org.spine3.testdata.TestStandFactory; import java.util.Map; @@ -40,6 +42,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; /** * @author Alex Tymchenko @@ -163,14 +166,44 @@ public void fail_to_initialize_from_empty_builder() { * - deliver the updates from several projection and aggregate repositories. */ + @Test + public void deliver_updates_from_projection_repository() throws Exception { + final BoundedContext boundedContext = mock(BoundedContext.class); + final Stand stand = mock(Stand.class); + when(boundedContext.getStandFunnel()).thenReturn(StandFunnel.newBuilder() + .setStand(stand) + .setExecutor(new Executor() { + @Override + public void execute(Runnable command) { + command.run(); + } + }) + .build()); + + // Init repository + final Given.StandTestProjectionRepository repository = new Given.StandTestProjectionRepository(boundedContext); + + stand.registerTypeSupplier(repository); + repository.initStorage(InMemoryStorageFactory.getInstance()); + repository.setOnline(); + + // Dispatch an update from projection repo + repository.dispatch(Given.validEvent()); + + // Was called ONCE + verify(boundedContext).getStandFunnel(); + verify(stand).update(ArgumentMatchers.any(), any(Any.class)); + } + @SuppressWarnings("MethodWithMultipleLoops") @Test public void deliver_updates_through_several_threads() throws InterruptedException { final int threadsCount = 10; - final int threadExecuteionMaxAwaitSeconds = 2; + @SuppressWarnings("LocalVariableNamingConvention") // Too long variable name + final int threadExecutionMaxAwaitSeconds = 2; - final Map threadInvakationRegistry = new ConcurrentHashMap<>(threadsCount); + final Map threadInvocationRegistry = new ConcurrentHashMap<>(threadsCount); final Stand stand = mock(Stand.class); doNothing().when(stand).update(ArgumentMatchers.any(), any(Any.class)); @@ -186,11 +219,11 @@ public void deliver_updates_through_several_threads() throws InterruptedExceptio @Override public void run() { final String threadName = Thread.currentThread().getName(); - Assert.assertFalse(threadInvakationRegistry.containsKey(threadName)); + Assert.assertFalse(threadInvocationRegistry.containsKey(threadName)); standFunnel.post(new Object(), Any.getDefaultInstance()); - threadInvakationRegistry.put(threadName, new Object()); + threadInvocationRegistry.put(threadName, new Object()); } }; @@ -198,9 +231,9 @@ public void run() { processes.execute(task); } - processes.awaitTermination(threadExecuteionMaxAwaitSeconds, TimeUnit.SECONDS); + processes.awaitTermination(threadExecutionMaxAwaitSeconds, TimeUnit.SECONDS); - Assert.assertEquals(threadInvakationRegistry.size(), threadsCount); + Assert.assertEquals(threadInvocationRegistry.size(), threadsCount); } From 1fae7e380560d50646ef3d08552734ba8f2e93b3 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 14:57:41 +0300 Subject: [PATCH 81/98] Suppress not yet ready test. --- .../test/java/org/spine3/server/stand/StandFunnelShould.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index 34e7ab2ff0f..c924469b833 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -166,7 +166,7 @@ public void fail_to_initialize_from_empty_builder() { * - deliver the updates from several projection and aggregate repositories. */ - @Test + //@Test public void deliver_updates_from_projection_repository() throws Exception { final BoundedContext boundedContext = mock(BoundedContext.class); final Stand stand = mock(Stand.class); From f9e01c53f7e11e5e7959a8ba936eaee2b7d4461a Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 16:09:19 +0300 Subject: [PATCH 82/98] Finish delivery from projection repo test. --- .../java/org/spine3/server/stand/Given.java | 36 +++++++++++++++++-- .../server/stand/StandFunnelShould.java | 27 +++++++------- 2 files changed, 46 insertions(+), 17 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/Given.java b/server/src/test/java/org/spine3/server/stand/Given.java index 2f631d4995f..c7da6d078b9 100644 --- a/server/src/test/java/org/spine3/server/stand/Given.java +++ b/server/src/test/java/org/spine3/server/stand/Given.java @@ -20,11 +20,16 @@ package org.spine3.server.stand; +import com.google.protobuf.Message; +import org.spine3.base.CommandContext; import org.spine3.base.Event; import org.spine3.base.EventContext; +import org.spine3.base.EventId; +import org.spine3.base.Identifiers; import org.spine3.protobuf.AnyPacker; import org.spine3.protobuf.TypeUrl; import org.spine3.server.BoundedContext; +import org.spine3.server.event.Subscribe; import org.spine3.server.projection.Projection; import org.spine3.server.projection.ProjectionRepository; import org.spine3.test.projection.Project; @@ -36,10 +41,20 @@ */ /*package*/ class Given { + private static final String PROJECT_UUID = Identifiers.newUuid(); + + private Given() { + } + /*package*/static class StandTestProjectionRepository extends ProjectionRepository { /*package*/ StandTestProjectionRepository(BoundedContext boundedContext) { super(boundedContext); } + + @Override + protected ProjectId getEntityId(Message event, EventContext context) { + return ProjectId.newBuilder().setId(PROJECT_UUID).build(); + } } private static class StandTestProjection extends Projection { @@ -54,6 +69,11 @@ private static class StandTestProjection extends Projection public StandTestProjection(ProjectId id) { super(id); } + + @Subscribe + public void handle(ProjectCreated event, EventContext context) { + // Do nothing + } } /*package*/ static Event validEvent() { @@ -62,10 +82,20 @@ public StandTestProjection(ProjectId id) { .setProjectId(ProjectId.newBuilder().setId("12345AD0")) .build()) .toBuilder() - .setTypeUrl(TypeUrl.SPINE_TYPE_URL_PREFIX + '/' + - ProjectCreated.getDescriptor().getFullName()) + .setTypeUrl(TypeUrl.SPINE_TYPE_URL_PREFIX + "/" + ProjectCreated.getDescriptor().getFullName()) .build()) - .setContext(EventContext.getDefaultInstance()) + .setContext(EventContext.newBuilder() + .setDoNotEnrich(true) + .setCommandContext(CommandContext.getDefaultInstance()) + .setEventId(EventId.newBuilder() + .setUuid(PROJECT_UUID) + .build())) .build(); } + + /*package*/ static ProjectionRepository repo(BoundedContext context) { + return new StandTestProjectionRepository(context); + } + + } diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index c924469b833..4c9816159f1 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -26,6 +26,7 @@ import org.junit.Test; import org.mockito.ArgumentMatchers; import org.spine3.server.BoundedContext; +import org.spine3.server.projection.ProjectionRepository; import org.spine3.server.storage.memory.InMemoryStorageFactory; import org.spine3.testdata.TestStandFactory; @@ -42,7 +43,6 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; /** * @author Alex Tymchenko @@ -166,22 +166,21 @@ public void fail_to_initialize_from_empty_builder() { * - deliver the updates from several projection and aggregate repositories. */ - //@Test + @Test public void deliver_updates_from_projection_repository() throws Exception { - final BoundedContext boundedContext = mock(BoundedContext.class); final Stand stand = mock(Stand.class); - when(boundedContext.getStandFunnel()).thenReturn(StandFunnel.newBuilder() - .setStand(stand) - .setExecutor(new Executor() { - @Override - public void execute(Runnable command) { - command.run(); - } - }) - .build()); - + final BoundedContext boundedContext = spy(BoundedContext.newBuilder() + .setStand(stand) + .setStorageFactory(InMemoryStorageFactory.getInstance()) + .setStandFunnelExecutor(new Executor() { // Straightforward executor + @Override + public void execute(Runnable command) { + command.run(); + } + }) + .build()); // Init repository - final Given.StandTestProjectionRepository repository = new Given.StandTestProjectionRepository(boundedContext); + final ProjectionRepository repository = Given.repo(boundedContext); stand.registerTypeSupplier(repository); repository.initStorage(InMemoryStorageFactory.getInstance()); From 5c6e5780718d4789c23a6bccbc977ce667332f76 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 17:26:06 +0300 Subject: [PATCH 83/98] Finish delivery form an aggregate repo test. --- .../java/org/spine3/server/stand/Given.java | 82 ++++++++++++++++++- .../server/stand/StandFunnelShould.java | 44 ++++++---- 2 files changed, 108 insertions(+), 18 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/Given.java b/server/src/test/java/org/spine3/server/stand/Given.java index c7da6d078b9..595397ce31c 100644 --- a/server/src/test/java/org/spine3/server/stand/Given.java +++ b/server/src/test/java/org/spine3/server/stand/Given.java @@ -20,22 +20,34 @@ package org.spine3.server.stand; +import com.google.protobuf.Any; import com.google.protobuf.Message; +import org.spine3.base.Command; import org.spine3.base.CommandContext; import org.spine3.base.Event; import org.spine3.base.EventContext; import org.spine3.base.EventId; +import org.spine3.base.FailureThrowable; import org.spine3.base.Identifiers; import org.spine3.protobuf.AnyPacker; import org.spine3.protobuf.TypeUrl; import org.spine3.server.BoundedContext; +import org.spine3.server.aggregate.Aggregate; +import org.spine3.server.aggregate.AggregateRepository; +import org.spine3.server.aggregate.Apply; +import org.spine3.server.command.Assign; import org.spine3.server.event.Subscribe; import org.spine3.server.projection.Projection; import org.spine3.server.projection.ProjectionRepository; +import org.spine3.server.storage.memory.InMemoryStorageFactory; import org.spine3.test.projection.Project; import org.spine3.test.projection.ProjectId; +import org.spine3.test.projection.command.CreateProject; import org.spine3.test.projection.event.ProjectCreated; +import java.util.List; +import java.util.concurrent.Executor; + /** * @author Dmytro Dashenkov */ @@ -46,7 +58,7 @@ private Given() { } - /*package*/static class StandTestProjectionRepository extends ProjectionRepository { + /*package*/ static class StandTestProjectionRepository extends ProjectionRepository { /*package*/ StandTestProjectionRepository(BoundedContext boundedContext) { super(boundedContext); } @@ -57,6 +69,48 @@ protected ProjectId getEntityId(Message event, EventContext context) { } } + /*package*/ static class StandTestAggregateRepository extends AggregateRepository { + + /** + * Creates a new repository instance. + * + * @param boundedContext the bounded context to which this repository belongs + */ + /*package*/ StandTestAggregateRepository(BoundedContext boundedContext) { + super(boundedContext); + } + } + + /*package*/ static class TestFailure extends FailureThrowable { + + /*package*/ TestFailure() { + super(/*generatedMessage=*/null); + } + } + + private static class StandTestAggregate extends Aggregate { + + /** + * Creates a new aggregate instance. + * + * @param id the ID for the new aggregate + * @throws IllegalArgumentException if the ID is not of one of the supported types + */ + public StandTestAggregate(ProjectId id) { + super(id); + } + + @Assign + public List handle(CreateProject createProject, CommandContext context) { + return null; + } + + @Apply + public void handle(ProjectCreated event) { + // Do nothing + } + } + private static class StandTestProjection extends Projection { /** * Creates a new instance. @@ -76,7 +130,7 @@ public void handle(ProjectCreated event, EventContext context) { } } - /*package*/ static Event validEvent() { + /*package*/ static Event validEvent() { return Event.newBuilder() .setMessage(AnyPacker.pack(ProjectCreated.newBuilder() .setProjectId(ProjectId.newBuilder().setId("12345AD0")) @@ -93,9 +147,31 @@ public void handle(ProjectCreated event, EventContext context) { .build(); } - /*package*/ static ProjectionRepository repo(BoundedContext context) { + /*package*/ static Command validCommand() { + return Command.newBuilder() + .setMessage(AnyPacker.pack(CreateProject.getDefaultInstance())) + .setContext(CommandContext.getDefaultInstance()) + .build(); + } + + /*package*/ static ProjectionRepository projectionRepo(BoundedContext context) { return new StandTestProjectionRepository(context); } + /*package*/ static AggregateRepository aggregateRepo(BoundedContext context) { + return new StandTestAggregateRepository(context); + } + /*package*/ static BoundedContext boundedContext(Stand stand) { + return BoundedContext.newBuilder() + .setStand(stand) + .setStorageFactory(InMemoryStorageFactory.getInstance()) + .setStandFunnelExecutor(new Executor() { // Straightforward executor + @Override + public void execute(Runnable command) { + command.run(); + } + }) + .build(); + } } diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index 4c9816159f1..ad82932db72 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -26,6 +26,7 @@ import org.junit.Test; import org.mockito.ArgumentMatchers; import org.spine3.server.BoundedContext; +import org.spine3.server.aggregate.AggregateRepository; import org.spine3.server.projection.ProjectionRepository; import org.spine3.server.storage.memory.InMemoryStorageFactory; import org.spine3.testdata.TestStandFactory; @@ -161,28 +162,18 @@ public void fail_to_initialize_from_empty_builder() { // **** Integration scenarios ( -> StandFunnel -> Mock Stand) **** /** - * - Deliver updates from projection repo on update; - * - deliver updates from aggregate repo on update; + * - Deliver updates from projection projectionRepo on update; + * - deliver updates from aggregate projectionRepo on update; * - deliver the updates from several projection and aggregate repositories. */ @Test - public void deliver_updates_from_projection_repository() throws Exception { + public void deliver_updates_from_projection_repository() { final Stand stand = mock(Stand.class); - final BoundedContext boundedContext = spy(BoundedContext.newBuilder() - .setStand(stand) - .setStorageFactory(InMemoryStorageFactory.getInstance()) - .setStandFunnelExecutor(new Executor() { // Straightforward executor - @Override - public void execute(Runnable command) { - command.run(); - } - }) - .build()); + final BoundedContext boundedContext = spy(Given.boundedContext(stand)); // Init repository - final ProjectionRepository repository = Given.repo(boundedContext); + final ProjectionRepository repository = Given.projectionRepo(boundedContext); - stand.registerTypeSupplier(repository); repository.initStorage(InMemoryStorageFactory.getInstance()); repository.setOnline(); @@ -194,6 +185,29 @@ public void execute(Runnable command) { verify(stand).update(ArgumentMatchers.any(), any(Any.class)); } + @Test + public void deliver_updates_form_aggregate_repository() { + final Stand stand = mock(Stand.class); + + final BoundedContext boundedContext = spy(Given.boundedContext(stand)); + // Init repository + final AggregateRepository repository = Given.aggregateRepo(boundedContext); + + stand.registerTypeSupplier(repository); + repository.initStorage(InMemoryStorageFactory.getInstance()); + + // Dispatch an update from projection projectionRepo + try { + repository.dispatch(Given.validCommand()); + } catch (IllegalStateException e) { + // Handle null event dispatch after command handling. + Assert.assertTrue(e.getMessage().contains("No record found for command ID: EMPTY")); + } + + // Was called ONCE + verify(boundedContext).getStandFunnel(); + verify(stand).update(ArgumentMatchers.any(), any(Any.class)); + } @SuppressWarnings("MethodWithMultipleLoops") @Test From 196848bc7d0909404d56bdb6e476ff6beb4d999d Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 17:27:38 +0300 Subject: [PATCH 84/98] Delete redundant "TestFailure" declaration. --- server/src/test/java/org/spine3/server/stand/Given.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/Given.java b/server/src/test/java/org/spine3/server/stand/Given.java index 595397ce31c..18b75d76805 100644 --- a/server/src/test/java/org/spine3/server/stand/Given.java +++ b/server/src/test/java/org/spine3/server/stand/Given.java @@ -27,7 +27,6 @@ import org.spine3.base.Event; import org.spine3.base.EventContext; import org.spine3.base.EventId; -import org.spine3.base.FailureThrowable; import org.spine3.base.Identifiers; import org.spine3.protobuf.AnyPacker; import org.spine3.protobuf.TypeUrl; @@ -81,13 +80,6 @@ protected ProjectId getEntityId(Message event, EventContext context) { } } - /*package*/ static class TestFailure extends FailureThrowable { - - /*package*/ TestFailure() { - super(/*generatedMessage=*/null); - } - } - private static class StandTestAggregate extends Aggregate { /** From 0ae977c022060da9eaec3b8c4d2d6fa06a0d639a Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 17:40:54 +0300 Subject: [PATCH 85/98] Extract common test operations. --- .../server/stand/StandFunnelShould.java | 67 ++++++++++++------- 1 file changed, 42 insertions(+), 25 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index ad82932db72..3cac4e4acb7 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -39,6 +39,7 @@ import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; +import static com.google.common.base.Preconditions.checkNotNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; @@ -169,41 +170,53 @@ public void fail_to_initialize_from_empty_builder() { @Test public void deliver_updates_from_projection_repository() { - final Stand stand = mock(Stand.class); - final BoundedContext boundedContext = spy(Given.boundedContext(stand)); - // Init repository - final ProjectionRepository repository = Given.projectionRepo(boundedContext); - - repository.initStorage(InMemoryStorageFactory.getInstance()); - repository.setOnline(); + deliverUpdatesTest(new BoundedContextAction() { + @Override + public void perform(BoundedContext context) { + // Init repository + final ProjectionRepository repository = Given.projectionRepo(context); - // Dispatch an update from projection repo - repository.dispatch(Given.validEvent()); + repository.initStorage(InMemoryStorageFactory.getInstance()); + repository.setOnline(); - // Was called ONCE - verify(boundedContext).getStandFunnel(); - verify(stand).update(ArgumentMatchers.any(), any(Any.class)); + // Dispatch an update from projection repo + repository.dispatch(Given.validEvent()); + } + }); } @Test public void deliver_updates_form_aggregate_repository() { - final Stand stand = mock(Stand.class); + deliverUpdatesTest(new BoundedContextAction() { + @Override + public void perform(BoundedContext context) { + // Init repository + final AggregateRepository repository = Given.aggregateRepo(context); + + repository.initStorage(InMemoryStorageFactory.getInstance()); + try { + repository.dispatch(Given.validCommand()); + } catch (IllegalStateException e) { + // Handle null event dispatch after command handling. + Assert.assertTrue(e.getMessage().contains("No record found for command ID: EMPTY")); + } + + } + }); + } + + private static void deliverUpdatesTest(BoundedContextAction... dispatchActions) { + checkNotNull(dispatchActions); + + final Stand stand = mock(Stand.class); final BoundedContext boundedContext = spy(Given.boundedContext(stand)); - // Init repository - final AggregateRepository repository = Given.aggregateRepo(boundedContext); - - stand.registerTypeSupplier(repository); - repository.initStorage(InMemoryStorageFactory.getInstance()); - - // Dispatch an update from projection projectionRepo - try { - repository.dispatch(Given.validCommand()); - } catch (IllegalStateException e) { - // Handle null event dispatch after command handling. - Assert.assertTrue(e.getMessage().contains("No record found for command ID: EMPTY")); + + for (BoundedContextAction dispatchAction : dispatchActions) { + dispatchAction.perform(boundedContext); } + // Was called ONCE verify(boundedContext).getStandFunnel(); verify(stand).update(ArgumentMatchers.any(), any(Any.class)); @@ -250,4 +263,8 @@ public void run() { } + private interface BoundedContextAction { + void perform(BoundedContext context); + } + } From 5d3c7192804040b6ea3825c76bd489cdbc0f5cc2 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 18:21:34 +0300 Subject: [PATCH 86/98] Refactor and add delivery tests for several repositories successively and in parallel. --- .../java/org/spine3/server/stand/Given.java | 29 ++++-- .../server/stand/StandFunnelShould.java | 89 +++++++++++++------ 2 files changed, 82 insertions(+), 36 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/Given.java b/server/src/test/java/org/spine3/server/stand/Given.java index 18b75d76805..ccad28106a2 100644 --- a/server/src/test/java/org/spine3/server/stand/Given.java +++ b/server/src/test/java/org/spine3/server/stand/Given.java @@ -46,12 +46,16 @@ import java.util.List; import java.util.concurrent.Executor; +import java.util.concurrent.Executors; /** * @author Dmytro Dashenkov */ /*package*/ class Given { + /*package*/ static final int THREADS_COUNT_IN_POOL_EXECUTOR = 10; + /*package*/ static final int SEVERAL = THREADS_COUNT_IN_POOL_EXECUTOR; + private static final String PROJECT_UUID = Identifiers.newUuid(); private Given() { @@ -154,16 +158,23 @@ public void handle(ProjectCreated event, EventContext context) { return new StandTestAggregateRepository(context); } - /*package*/ static BoundedContext boundedContext(Stand stand) { + /*package*/ static BoundedContext boundedContext(Stand stand, int concurrentThreads) { + final Executor executor = concurrentThreads > 0 ? Executors.newFixedThreadPool(concurrentThreads) : + new Executor() { // Straightforward executor + @Override + public void execute(Runnable command) { + command.run(); + } + }; + + return boundedContextBuilder(stand) + .setStandFunnelExecutor(executor) + .build(); + } + + private static BoundedContext.Builder boundedContextBuilder(Stand stand) { return BoundedContext.newBuilder() .setStand(stand) - .setStorageFactory(InMemoryStorageFactory.getInstance()) - .setStandFunnelExecutor(new Executor() { // Straightforward executor - @Override - public void execute(Runnable command) { - command.run(); - } - }) - .build(); + .setStorageFactory(InMemoryStorageFactory.getInstance()); } } diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index 3cac4e4acb7..486697b153e 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -31,7 +31,9 @@ import org.spine3.server.storage.memory.InMemoryStorageFactory; import org.spine3.testdata.TestStandFactory; +import java.security.SecureRandom; import java.util.Map; +import java.util.Random; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; @@ -44,6 +46,7 @@ import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; /** @@ -170,24 +173,55 @@ public void fail_to_initialize_from_empty_builder() { @Test public void deliver_updates_from_projection_repository() { - deliverUpdatesTest(new BoundedContextAction() { - @Override - public void perform(BoundedContext context) { - // Init repository - final ProjectionRepository repository = Given.projectionRepo(context); + deliverUpdates(false, projectionRepositoryDispatch()); + } - repository.initStorage(InMemoryStorageFactory.getInstance()); - repository.setOnline(); + @Test + public void deliver_updates_form_aggregate_repository() { + deliverUpdates(false, aggregateRepositoryDispatch()); + } - // Dispatch an update from projection repo - repository.dispatch(Given.validEvent()); - } - }); + @Test + public void deliver_updates_from_several_repositories_in_single_thread() { + deliverUpdates(false, getSeveralRepositoryDispatchCalls()); } @Test - public void deliver_updates_form_aggregate_repository() { - deliverUpdatesTest(new BoundedContextAction() { + public void deliver_updates_from_several_repositories_in_multiple_threads() { + deliverUpdates(true, getSeveralRepositoryDispatchCalls()); + } + + private static BoundedContextAction[] getSeveralRepositoryDispatchCalls() { + final BoundedContextAction[] result = new BoundedContextAction[Given.SEVERAL]; + final Random random = new SecureRandom(); + + for (int i = 0; i < result.length; i++) { + result[i] = random.nextBoolean() ? aggregateRepositoryDispatch() : projectionRepositoryDispatch(); + } + + return result; + } + + private static void deliverUpdates(boolean isMultiThread, BoundedContextAction... dispatchActions) { + checkNotNull(dispatchActions); + + final Stand stand = mock(Stand.class); + final BoundedContext boundedContext = spy(Given.boundedContext(stand, + isMultiThread ? + Given.THREADS_COUNT_IN_POOL_EXECUTOR : 0)); + + for (BoundedContextAction dispatchAction : dispatchActions) { + dispatchAction.perform(boundedContext); + } + + + // Was called ONCE + verify(boundedContext, times(dispatchActions.length)).getStandFunnel(); + verify(stand, times(dispatchActions.length)).update(ArgumentMatchers.any(), any(Any.class)); + } + + private static BoundedContextAction aggregateRepositoryDispatch() { + return new BoundedContextAction() { @Override public void perform(BoundedContext context) { // Init repository @@ -201,31 +235,32 @@ public void perform(BoundedContext context) { // Handle null event dispatch after command handling. Assert.assertTrue(e.getMessage().contains("No record found for command ID: EMPTY")); } - } - }); + }; } - private static void deliverUpdatesTest(BoundedContextAction... dispatchActions) { - checkNotNull(dispatchActions); + private static BoundedContextAction projectionRepositoryDispatch() { + return new BoundedContextAction() { + @Override + public void perform(BoundedContext context) { + // Init repository + final ProjectionRepository repository = Given.projectionRepo(context); - final Stand stand = mock(Stand.class); - final BoundedContext boundedContext = spy(Given.boundedContext(stand)); + repository.initStorage(InMemoryStorageFactory.getInstance()); + repository.setOnline(); - for (BoundedContextAction dispatchAction : dispatchActions) { - dispatchAction.perform(boundedContext); - } + // Dispatch an update from projection repo + repository.dispatch(Given.validEvent()); + } + }; + } - // Was called ONCE - verify(boundedContext).getStandFunnel(); - verify(stand).update(ArgumentMatchers.any(), any(Any.class)); - } @SuppressWarnings("MethodWithMultipleLoops") @Test public void deliver_updates_through_several_threads() throws InterruptedException { - final int threadsCount = 10; + final int threadsCount = Given.THREADS_COUNT_IN_POOL_EXECUTOR; @SuppressWarnings("LocalVariableNamingConvention") // Too long variable name final int threadExecutionMaxAwaitSeconds = 2; From 94aa4e5d024e6b4e69a13bc1aa112e20453e20a4 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 13:57:38 +0300 Subject: [PATCH 87/98] Explicit "Given" for stand tests. --- .../org/spine3/server/stand/StandShould.java | 21 +------------------ 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/StandShould.java b/server/src/test/java/org/spine3/server/stand/StandShould.java index 9228d51f346..f0a672ae0d3 100644 --- a/server/src/test/java/org/spine3/server/stand/StandShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandShould.java @@ -48,6 +48,7 @@ import org.spine3.server.projection.Projection; import org.spine3.server.projection.ProjectionRepository; import org.spine3.server.storage.EntityStorageRecord; +import org.spine3.server.stand.Given.StandTestProjectionRepository; import org.spine3.server.storage.StandStorage; import org.spine3.test.clientservice.customer.Customer; import org.spine3.test.clientservice.customer.CustomerId; @@ -721,26 +722,6 @@ public void onEntityStateUpdate(Any newEntityState) { // ***** Inner classes used for tests. ***** - private static class StandTestProjection extends Projection { - /** - * Creates a new instance. - * - * @param id the ID for the new instance - * @throws IllegalArgumentException if the ID is not of one of the supported types - */ - public StandTestProjection(ProjectId id) { - super(id); - } - } - - - private static class StandTestProjectionRepository extends ProjectionRepository { - protected StandTestProjectionRepository(BoundedContext boundedContext) { - super(boundedContext); - } - } - - /** * A {@link StreamObserver} storing the state of {@link Query} execution. */ From fa2017a32b850c383ec22d397e5348bcc63ed8cd Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 18:47:07 +0300 Subject: [PATCH 88/98] Fix failing build by adding "await" for a concurrent test. --- .../java/org/spine3/server/stand/Given.java | 1 + .../spine3/server/stand/StandFunnelShould.java | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/Given.java b/server/src/test/java/org/spine3/server/stand/Given.java index ccad28106a2..9e6d31d9f83 100644 --- a/server/src/test/java/org/spine3/server/stand/Given.java +++ b/server/src/test/java/org/spine3/server/stand/Given.java @@ -57,6 +57,7 @@ /*package*/ static final int SEVERAL = THREADS_COUNT_IN_POOL_EXECUTOR; private static final String PROJECT_UUID = Identifiers.newUuid(); + public static final int AWAIT_SECONDS = 2; private Given() { } diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index 486697b153e..0e8f48438fc 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -214,12 +214,23 @@ private static void deliverUpdates(boolean isMultiThread, BoundedContextAction.. dispatchAction.perform(boundedContext); } - - // Was called ONCE + // Was called as much times as there are dispatch actions. verify(boundedContext, times(dispatchActions.length)).getStandFunnel(); + + if (isMultiThread) { + await(Given.AWAIT_SECONDS); + } + verify(stand, times(dispatchActions.length)).update(ArgumentMatchers.any(), any(Any.class)); } + private static void await(int seconds) { + try { + Thread.sleep(seconds); + } catch (InterruptedException ignore) { + } + } + private static BoundedContextAction aggregateRepositoryDispatch() { return new BoundedContextAction() { @Override @@ -256,13 +267,12 @@ public void perform(BoundedContext context) { } - @SuppressWarnings("MethodWithMultipleLoops") @Test public void deliver_updates_through_several_threads() throws InterruptedException { final int threadsCount = Given.THREADS_COUNT_IN_POOL_EXECUTOR; @SuppressWarnings("LocalVariableNamingConvention") // Too long variable name - final int threadExecutionMaxAwaitSeconds = 2; + final int threadExecutionMaxAwaitSeconds = Given.AWAIT_SECONDS; final Map threadInvocationRegistry = new ConcurrentHashMap<>(threadsCount); From 1edb4cce1a9719993c370b19c4f0d5c63c310a84 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 21:22:03 +0300 Subject: [PATCH 89/98] Fix issues form PR. --- .../java/org/spine3/server/stand/Given.java | 10 +-- .../server/stand/StandFunnelShould.java | 89 ++++++++----------- 2 files changed, 41 insertions(+), 58 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/Given.java b/server/src/test/java/org/spine3/server/stand/Given.java index 9e6d31d9f83..4977b9e0a3e 100644 --- a/server/src/test/java/org/spine3/server/stand/Given.java +++ b/server/src/test/java/org/spine3/server/stand/Given.java @@ -20,6 +20,7 @@ package org.spine3.server.stand; +import com.google.common.util.concurrent.MoreExecutors; import com.google.protobuf.Any; import com.google.protobuf.Message; import org.spine3.base.Command; @@ -139,7 +140,7 @@ public void handle(ProjectCreated event, EventContext context) { .setDoNotEnrich(true) .setCommandContext(CommandContext.getDefaultInstance()) .setEventId(EventId.newBuilder() - .setUuid(PROJECT_UUID) + .setUuid(Identifiers.newUuid()) .build())) .build(); } @@ -161,12 +162,7 @@ public void handle(ProjectCreated event, EventContext context) { /*package*/ static BoundedContext boundedContext(Stand stand, int concurrentThreads) { final Executor executor = concurrentThreads > 0 ? Executors.newFixedThreadPool(concurrentThreads) : - new Executor() { // Straightforward executor - @Override - public void execute(Runnable command) { - command.run(); - } - }; + MoreExecutors.directExecutor(); return boundedContextBuilder(stand) .setStandFunnelExecutor(executor) diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index 0e8f48438fc..b405de4777f 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -22,6 +22,7 @@ package org.spine3.server.stand; import com.google.protobuf.Any; +import io.netty.util.internal.ConcurrentSet; import org.junit.Assert; import org.junit.Test; import org.mockito.ArgumentMatchers; @@ -32,13 +33,11 @@ import org.spine3.testdata.TestStandFactory; import java.security.SecureRandom; -import java.util.Map; import java.util.Random; -import java.util.concurrent.ConcurrentHashMap; +import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import static com.google.common.base.Preconditions.checkNotNull; @@ -57,10 +56,6 @@ public class StandFunnelShould { // **** Positive scenarios (unit) **** - /** - * - deliver mock updates to the stand (invoke proper methods with particular arguments) - test the delivery only. - */ - @Test public void initialize_properly_with_stand_only() { final Stand stand = TestStandFactory.create(); @@ -71,36 +66,25 @@ public void initialize_properly_with_stand_only() { } @Test - public void initialize_properly_with_various_builder_options() { + public void initialize_properly_with_all_builder_options() { final Stand stand = TestStandFactory.create(); - final Executor executor = Executors.newSingleThreadExecutor(new ThreadFactory() { - @Override - public Thread newThread(Runnable r) { - return Thread.currentThread(); - } - }); + final Executor executor = Executors.newSingleThreadExecutor(); - final StandFunnel blockingFunnel = StandFunnel.newBuilder() + final StandFunnel funnel = StandFunnel.newBuilder() .setStand(stand) .setExecutor(executor) .build(); - Assert.assertNotNull(blockingFunnel); + Assert.assertNotNull(funnel); + } + + @Test + public void initialize_properly_with_no_executor() { + final Stand stand = TestStandFactory.create(); final StandFunnel funnelForBusyStand = StandFunnel.newBuilder() .setStand(stand) - .setExecutor(Executors.newSingleThreadExecutor()) .build(); Assert.assertNotNull(funnelForBusyStand); - - - final StandFunnel emptyExecutorFunnel = StandFunnel.newBuilder() - .setStand(TestStandFactory.create()) - .setExecutor(new Executor() { - @Override - public void execute(Runnable neverCalled) { } - }) - .build(); - Assert.assertNotNull(emptyExecutorFunnel); } @Test @@ -149,8 +133,8 @@ public void execute(Runnable command) { @SuppressWarnings("ResultOfMethodCallIgnored") @Test(expected = NullPointerException.class) - public void fail_to_initialize_with_inproper_stand() { - @SuppressWarnings("ConstantConditions") + public void fail_to_initialize_with_improper_stand() { + @SuppressWarnings("ConstantConditions") // null is marked as improper with this warning final StandFunnel.Builder builder = StandFunnel.newBuilder().setStand(null); builder.build(); @@ -165,30 +149,24 @@ public void fail_to_initialize_from_empty_builder() { // **** Integration scenarios ( -> StandFunnel -> Mock Stand) **** - /** - * - Deliver updates from projection projectionRepo on update; - * - deliver updates from aggregate projectionRepo on update; - * - deliver the updates from several projection and aggregate repositories. - */ - @Test public void deliver_updates_from_projection_repository() { - deliverUpdates(false, projectionRepositoryDispatch()); + checkUpdatesDelivery(false, projectionRepositoryDispatch()); } @Test public void deliver_updates_form_aggregate_repository() { - deliverUpdates(false, aggregateRepositoryDispatch()); + checkUpdatesDelivery(false, aggregateRepositoryDispatch()); } @Test public void deliver_updates_from_several_repositories_in_single_thread() { - deliverUpdates(false, getSeveralRepositoryDispatchCalls()); + checkUpdatesDelivery(false, getSeveralRepositoryDispatchCalls()); } @Test public void deliver_updates_from_several_repositories_in_multiple_threads() { - deliverUpdates(true, getSeveralRepositoryDispatchCalls()); + checkUpdatesDelivery(true, getSeveralRepositoryDispatchCalls()); } private static BoundedContextAction[] getSeveralRepositoryDispatchCalls() { @@ -202,22 +180,22 @@ private static BoundedContextAction[] getSeveralRepositoryDispatchCalls() { return result; } - private static void deliverUpdates(boolean isMultiThread, BoundedContextAction... dispatchActions) { + private static void checkUpdatesDelivery(boolean isConcurrent, BoundedContextAction... dispatchActions) { checkNotNull(dispatchActions); final Stand stand = mock(Stand.class); final BoundedContext boundedContext = spy(Given.boundedContext(stand, - isMultiThread ? + isConcurrent ? Given.THREADS_COUNT_IN_POOL_EXECUTOR : 0)); for (BoundedContextAction dispatchAction : dispatchActions) { dispatchAction.perform(boundedContext); } - // Was called as much times as there are dispatch actions. + // Was called as many times as there are dispatch actions. verify(boundedContext, times(dispatchActions.length)).getStandFunnel(); - if (isMultiThread) { + if (isConcurrent) { await(Given.AWAIT_SECONDS); } @@ -241,10 +219,19 @@ public void perform(BoundedContext context) { repository.initStorage(InMemoryStorageFactory.getInstance()); try { + // Mock aggregate and mock stand are not able to handle events returned after command handling. + // This causes IllegalStateException to be thrown. + // Note that this is not the end of a test case, so we can't just "expect=IllegalStateException" repository.dispatch(Given.validCommand()); } catch (IllegalStateException e) { - // Handle null event dispatch after command handling. - Assert.assertTrue(e.getMessage().contains("No record found for command ID: EMPTY")); + // Handle null event dispatching after the command is handled. + + // Check if this error is caused by returning nuu or empty list after command processing. + // Proceed crash if it's not + if (!e.getMessage() + .contains("No record found for command ID: EMPTY")) { + throw e; + } } } }; @@ -274,7 +261,7 @@ public void deliver_updates_through_several_threads() throws InterruptedExceptio @SuppressWarnings("LocalVariableNamingConvention") // Too long variable name final int threadExecutionMaxAwaitSeconds = Given.AWAIT_SECONDS; - final Map threadInvocationRegistry = new ConcurrentHashMap<>(threadsCount); + final Set threadInvocationRegistry = new ConcurrentSet<>(); final Stand stand = mock(Stand.class); doNothing().when(stand).update(ArgumentMatchers.any(), any(Any.class)); @@ -283,26 +270,26 @@ public void deliver_updates_through_several_threads() throws InterruptedExceptio .setStand(stand) .build(); - final ExecutorService processes = Executors.newFixedThreadPool(threadsCount); + final ExecutorService executor = Executors.newFixedThreadPool(threadsCount); final Runnable task = new Runnable() { @Override public void run() { final String threadName = Thread.currentThread().getName(); - Assert.assertFalse(threadInvocationRegistry.containsKey(threadName)); + Assert.assertFalse(threadInvocationRegistry.contains(threadName)); standFunnel.post(new Object(), Any.getDefaultInstance()); - threadInvocationRegistry.put(threadName, new Object()); + threadInvocationRegistry.add(threadName); } }; for (int i = 0; i < threadsCount; i++) { - processes.execute(task); + executor.execute(task); } - processes.awaitTermination(threadExecutionMaxAwaitSeconds, TimeUnit.SECONDS); + executor.awaitTermination(threadExecutionMaxAwaitSeconds, TimeUnit.SECONDS); Assert.assertEquals(threadInvocationRegistry.size(), threadsCount); From d31c2aeb1fc5814e437081fec6f10a2a0c633e6b Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 21:36:39 +0300 Subject: [PATCH 90/98] Increase await time for concurrent check execution. --- server/src/test/java/org/spine3/server/stand/Given.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/test/java/org/spine3/server/stand/Given.java b/server/src/test/java/org/spine3/server/stand/Given.java index 4977b9e0a3e..82fc42e059d 100644 --- a/server/src/test/java/org/spine3/server/stand/Given.java +++ b/server/src/test/java/org/spine3/server/stand/Given.java @@ -58,7 +58,7 @@ /*package*/ static final int SEVERAL = THREADS_COUNT_IN_POOL_EXECUTOR; private static final String PROJECT_UUID = Identifiers.newUuid(); - public static final int AWAIT_SECONDS = 2; + public static final int AWAIT_SECONDS = 4; private Given() { } From c534ae8a433f6227fed6fe310fa71957b190ab2a Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 21:51:17 +0300 Subject: [PATCH 91/98] Increase await time for concurrent check execution again. --- server/src/test/java/org/spine3/server/stand/Given.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/test/java/org/spine3/server/stand/Given.java b/server/src/test/java/org/spine3/server/stand/Given.java index 82fc42e059d..e0872d39b9f 100644 --- a/server/src/test/java/org/spine3/server/stand/Given.java +++ b/server/src/test/java/org/spine3/server/stand/Given.java @@ -58,7 +58,7 @@ /*package*/ static final int SEVERAL = THREADS_COUNT_IN_POOL_EXECUTOR; private static final String PROJECT_UUID = Identifiers.newUuid(); - public static final int AWAIT_SECONDS = 4; + /*package*/ static final int AWAIT_SECONDS = 10; private Given() { } From f88c0b171686a2024c5509588cad2aa236f49998 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 21:52:48 +0300 Subject: [PATCH 92/98] Reduce randomness of generated test data. --- server/src/test/java/org/spine3/server/stand/Given.java | 2 +- .../test/java/org/spine3/server/stand/StandFunnelShould.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/Given.java b/server/src/test/java/org/spine3/server/stand/Given.java index e0872d39b9f..d194aecc21f 100644 --- a/server/src/test/java/org/spine3/server/stand/Given.java +++ b/server/src/test/java/org/spine3/server/stand/Given.java @@ -58,7 +58,7 @@ /*package*/ static final int SEVERAL = THREADS_COUNT_IN_POOL_EXECUTOR; private static final String PROJECT_UUID = Identifiers.newUuid(); - /*package*/ static final int AWAIT_SECONDS = 10; + /*package*/ static final int AWAIT_SECONDS = 6; private Given() { } diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index b405de4777f..41be840a692 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -174,7 +174,7 @@ private static BoundedContextAction[] getSeveralRepositoryDispatchCalls() { final Random random = new SecureRandom(); for (int i = 0; i < result.length; i++) { - result[i] = random.nextBoolean() ? aggregateRepositoryDispatch() : projectionRepositoryDispatch(); + result[i] = (i % 2 == 0) ? aggregateRepositoryDispatch() : projectionRepositoryDispatch(); } return result; From 3daae3b0d27c374fedb1db30746c87f8f7e979e8 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 23:29:36 +0300 Subject: [PATCH 93/98] Remove redundant imports. --- .../src/test/java/org/spine3/server/stand/StandShould.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/StandShould.java b/server/src/test/java/org/spine3/server/stand/StandShould.java index f0a672ae0d3..997c95d7c83 100644 --- a/server/src/test/java/org/spine3/server/stand/StandShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandShould.java @@ -45,15 +45,12 @@ import org.spine3.protobuf.TypeUrl; import org.spine3.server.BoundedContext; import org.spine3.server.Given; -import org.spine3.server.projection.Projection; -import org.spine3.server.projection.ProjectionRepository; -import org.spine3.server.storage.EntityStorageRecord; import org.spine3.server.stand.Given.StandTestProjectionRepository; +import org.spine3.server.storage.EntityStorageRecord; import org.spine3.server.storage.StandStorage; import org.spine3.test.clientservice.customer.Customer; import org.spine3.test.clientservice.customer.CustomerId; import org.spine3.test.projection.Project; -import org.spine3.test.projection.ProjectId; import javax.annotation.Nullable; import java.util.Collection; From 91f673c8b2123e9e0d42e89816931090c805cfdb Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 13:57:38 +0300 Subject: [PATCH 94/98] Explicit "Given" for stand tests. --- server/src/test/java/org/spine3/server/stand/StandShould.java | 1 + 1 file changed, 1 insertion(+) diff --git a/server/src/test/java/org/spine3/server/stand/StandShould.java b/server/src/test/java/org/spine3/server/stand/StandShould.java index 997c95d7c83..d2c2c1ac92c 100644 --- a/server/src/test/java/org/spine3/server/stand/StandShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandShould.java @@ -47,6 +47,7 @@ import org.spine3.server.Given; import org.spine3.server.stand.Given.StandTestProjectionRepository; import org.spine3.server.storage.EntityStorageRecord; +import org.spine3.server.stand.Given.StandTestProjectionRepository; import org.spine3.server.storage.StandStorage; import org.spine3.test.clientservice.customer.Customer; import org.spine3.test.clientservice.customer.CustomerId; From 7eb64546ab3e64d4dc05ee93f0d5fbad4e24282c Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 13:57:38 +0300 Subject: [PATCH 95/98] Explicit "Given" for stand tests. --- server/src/test/java/org/spine3/server/stand/Given.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/test/java/org/spine3/server/stand/Given.java b/server/src/test/java/org/spine3/server/stand/Given.java index d194aecc21f..fa15cd2f6df 100644 --- a/server/src/test/java/org/spine3/server/stand/Given.java +++ b/server/src/test/java/org/spine3/server/stand/Given.java @@ -128,7 +128,7 @@ public void handle(ProjectCreated event, EventContext context) { } } - /*package*/ static Event validEvent() { + /*package*/ static Event validEvent() { return Event.newBuilder() .setMessage(AnyPacker.pack(ProjectCreated.newBuilder() .setProjectId(ProjectId.newBuilder().setId("12345AD0")) From 3aeb517e0bee86318a78a2484248f3c8de6ca002 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Wed, 14 Sep 2016 14:54:11 +0300 Subject: [PATCH 96/98] Add deliver updates form projection repo test skeleton, --- .../src/test/java/org/spine3/server/stand/StandFunnelShould.java | 1 + 1 file changed, 1 insertion(+) diff --git a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java index 41be840a692..d952c30ea84 100644 --- a/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandFunnelShould.java @@ -47,6 +47,7 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; /** * @author Alex Tymchenko From 646964a2629e9b2ddb579ebf1b5ad6fb97c61e21 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Thu, 15 Sep 2016 16:32:06 +0300 Subject: [PATCH 97/98] Merge. --- .../java/org/spine3/server/stand/Given.java | 2 +- .../org/spine3/server/stand/StandShould.java | 29 ++++++++++--------- .../org/spine3/testdata/TestStandFactory.java | 5 ++++ 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/server/src/test/java/org/spine3/server/stand/Given.java b/server/src/test/java/org/spine3/server/stand/Given.java index fa15cd2f6df..2184ac0511d 100644 --- a/server/src/test/java/org/spine3/server/stand/Given.java +++ b/server/src/test/java/org/spine3/server/stand/Given.java @@ -109,7 +109,7 @@ public void handle(ProjectCreated event) { } } - private static class StandTestProjection extends Projection { + /*package*/ static class StandTestProjection extends Projection { /** * Creates a new instance. * diff --git a/server/src/test/java/org/spine3/server/stand/StandShould.java b/server/src/test/java/org/spine3/server/stand/StandShould.java index d2c2c1ac92c..2dfcc386eb5 100644 --- a/server/src/test/java/org/spine3/server/stand/StandShould.java +++ b/server/src/test/java/org/spine3/server/stand/StandShould.java @@ -44,7 +44,9 @@ import org.spine3.protobuf.AnyPacker; import org.spine3.protobuf.TypeUrl; import org.spine3.server.BoundedContext; -import org.spine3.server.Given; +import org.spine3.server.Given.CustomerAggregate; +import org.spine3.server.Given.CustomerAggregateRepository; +import org.spine3.server.projection.ProjectionRepository; import org.spine3.server.stand.Given.StandTestProjectionRepository; import org.spine3.server.storage.EntityStorageRecord; import org.spine3.server.stand.Given.StandTestProjectionRepository; @@ -52,6 +54,7 @@ import org.spine3.test.clientservice.customer.Customer; import org.spine3.test.clientservice.customer.CustomerId; import org.spine3.test.projection.Project; +import org.spine3.test.projection.ProjectId; import javax.annotation.Nullable; import java.util.Collection; @@ -139,7 +142,7 @@ public void register_aggregate_repositories() { checkTypesEmpty(stand); - final Given.CustomerAggregateRepository customerAggregateRepo = new Given.CustomerAggregateRepository(boundedContext); + final CustomerAggregateRepository customerAggregateRepo = new CustomerAggregateRepository(boundedContext); stand.registerTypeSupplier(customerAggregateRepo); final Descriptors.Descriptor customerEntityDescriptor = Customer.getDescriptor(); @@ -147,7 +150,7 @@ public void register_aggregate_repositories() { checkHasExactlyOne(stand.getKnownAggregateTypes(), customerEntityDescriptor); @SuppressWarnings("LocalVariableNamingConvention") - final Given.CustomerAggregateRepository anotherCustomerAggregateRepo = new Given.CustomerAggregateRepository(boundedContext); + final CustomerAggregateRepository anotherCustomerAggregateRepo = new CustomerAggregateRepository(boundedContext); stand.registerTypeSupplier(anotherCustomerAggregateRepo); checkHasExactlyOne(stand.getAvailableTypes(), customerEntityDescriptor); checkHasExactlyOne(stand.getKnownAggregateTypes(), customerEntityDescriptor); @@ -184,7 +187,7 @@ public void operate_with_storage_provided_through_builder() { assertNotNull(stand); final BoundedContext boundedContext = newBoundedContext(stand); - final Given.CustomerAggregateRepository customerAggregateRepo = new Given.CustomerAggregateRepository(boundedContext); + final CustomerAggregateRepository customerAggregateRepo = new CustomerAggregateRepository(boundedContext); stand.registerTypeSupplier(customerAggregateRepo); @@ -192,7 +195,7 @@ public void operate_with_storage_provided_through_builder() { final CustomerId customerId = CustomerId.newBuilder() .setNumber(numericIdValue) .build(); - final Given.CustomerAggregate customerAggregate = customerAggregateRepo.create(customerId); + final CustomerAggregate customerAggregate = customerAggregateRepo.create(customerId); final Customer customerState = customerAggregate.getState(); final Any packedState = AnyPacker.pack(customerState); final TypeUrl customerType = TypeUrl.of(Customer.class); @@ -498,10 +501,10 @@ private static void setupExpectedFindAllBehaviour(Map sample StandTestProjectionRepository projectionRepository) { final Set projectIds = sampleProjects.keySet(); - final ImmutableCollection allResults = toProjectionCollection(projectIds); + final ImmutableCollection allResults = toProjectionCollection(projectIds); for (ProjectId projectId : projectIds) { - when(projectionRepository.find(eq(projectId))).thenReturn(new StandTestProjection(projectId)); + when(projectionRepository.find(eq(projectId))).thenReturn(new Given.StandTestProjection(projectId)); } final Iterable matchingIds = argThat(projectionIdsIterableMatcher(projectIds)); @@ -537,16 +540,16 @@ public boolean matches(EntityFilters argument) { }; } - private static ImmutableCollection toProjectionCollection(Collection values) { - final Collection transformed = Collections2.transform(values, new Function() { + private static ImmutableCollection toProjectionCollection(Collection values) { + final Collection transformed = Collections2.transform(values, new Function() { @Nullable @Override - public StandTestProjection apply(@Nullable ProjectId input) { + public Given.StandTestProjection apply(@Nullable ProjectId input) { checkNotNull(input); - return new StandTestProjection(input); + return new Given.StandTestProjection(input); } }); - final ImmutableList result = ImmutableList.copyOf(transformed); + final ImmutableList result = ImmutableList.copyOf(transformed); return result; } @@ -662,7 +665,7 @@ private static Stand prepareStandWithAggregateRepo(StandStorage standStorageMock assertNotNull(stand); final BoundedContext boundedContext = newBoundedContext(stand); - final Given.CustomerAggregateRepository customerAggregateRepo = new Given.CustomerAggregateRepository(boundedContext); + final org.spine3.server.Given.CustomerAggregateRepository customerAggregateRepo = new org.spine3.server.Given.CustomerAggregateRepository(boundedContext); stand.registerTypeSupplier(customerAggregateRepo); return stand; } diff --git a/server/src/test/java/org/spine3/testdata/TestStandFactory.java b/server/src/test/java/org/spine3/testdata/TestStandFactory.java index 474a6226b30..63477a00a7c 100644 --- a/server/src/test/java/org/spine3/testdata/TestStandFactory.java +++ b/server/src/test/java/org/spine3/testdata/TestStandFactory.java @@ -21,6 +21,7 @@ */ package org.spine3.testdata; +import org.mockito.Mockito; import org.spine3.server.stand.Stand; /** @@ -38,4 +39,8 @@ public static Stand create() { .build(); return stand; } + + public static Stand createMock() { + return Mockito.mock(Stand.class); + } } From 64058636ad3eaf8e9d6b8a2cff5f34efa5785727 Mon Sep 17 00:00:00 2001 From: Dmytro Dashenkov Date: Thu, 15 Sep 2016 17:23:32 +0300 Subject: [PATCH 98/98] Correct docs. --- .../src/main/java/org/spine3/server/stand/StandFunnel.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/server/src/main/java/org/spine3/server/stand/StandFunnel.java b/server/src/main/java/org/spine3/server/stand/StandFunnel.java index 44997adbdf8..1c4e9afc294 100644 --- a/server/src/main/java/org/spine3/server/stand/StandFunnel.java +++ b/server/src/main/java/org/spine3/server/stand/StandFunnel.java @@ -108,9 +108,6 @@ public Stand getStand() { * *

The value must not be null. * - *

If this method is not used, a default value will be used. - * // TODO:13-09-16:dmytro.dashenkov: Correct docs. No default value for Stand is used, null value leads to a fail instead. - * * @param stand the instance of {@link Stand}. * @return {@code this} instance of {@code Builder} */ @@ -123,12 +120,13 @@ public Executor getExecutor() { return executor; } - // TODO:13-09-16:dmytro.dashenkov: Complete docs. Here is the place where a default value is used in case of not using the method. /** * Set the {@code Executor} instance for this {@code StandFunnel}. * *

The value must not be {@code null}. * + *

If this method is not used, a default value will be used. + * * @param executor the instance of {@code Executor}. * @return {@code this} instance of {@code Builder} */