/*
 * Copyright 2018, TeamDev. 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 io.spine.server.procman;

import io.spine.core.Command;
import io.spine.core.Event;
import io.spine.core.Rejection;
import io.spine.grpc.StreamObservers;
import io.spine.server.BoundedContext;
import io.spine.server.delivery.AbstractMessageDeliveryTest;
import io.spine.server.delivery.given.ParallelDispatcher;
import io.spine.server.delivery.given.ThreadStats;
import io.spine.server.procman.given.PmMessageDeliveryTestEnv.DeliveryPm;
import io.spine.server.procman.given.PmMessageDeliveryTestEnv.QuadrupleShardPmRepository;
import io.spine.server.procman.given.PmMessageDeliveryTestEnv.SingleShardPmRepository;
import io.spine.test.procman.ProjectId;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

import static io.spine.server.delivery.given.MessageDeliveryTestEnv.dispatchWaitTime;
import static io.spine.server.procman.given.PmMessageDeliveryTestEnv.cannotStartProject;
import static io.spine.server.procman.given.PmMessageDeliveryTestEnv.createProject;
import static io.spine.server.procman.given.PmMessageDeliveryTestEnv.projectStarted;

/**
 * @author Alex Tymchenko
 */
@SuppressWarnings({"InnerClassMayBeStatic", "ClassCanBeStatic"
        /* JUnit nested classes cannot be static. */,
        "DuplicateStringLiteralInspection" /* Common test display names. */})
@DisplayName("ProcessManager message delivery should")
class PmMessageDeliveryTest extends AbstractMessageDeliveryTest {

    @Override
    @BeforeEach
    protected void setUp() {
        super.setUp();
        DeliveryPm.getStats().clear();
    }

    @Nested
    @DisplayName("in multithreaded env, dispatch commands")
    class DispatchCommands {

        @Test
        @DisplayName("to single shard")
        void toSingleShard() throws Exception {
            ParallelDispatcher<ProjectId, Command> dispatcher =
                    new ParallelDispatcher<ProjectId, Command>(
                            42, 400, dispatchWaitTime()) {
                        @Override
                        protected ThreadStats<ProjectId> getStats() {
                            return DeliveryPm.getStats();
                        }

                        @Override
                        protected Command newMessage() {
                            return createProject();
                        }

                        @Override
                        protected void postToBus(BoundedContext context, Command command) {
                            context.getCommandBus()
                                   .post(command, StreamObservers.noOpObserver());
                        }
                    };

            dispatcher.dispatchMessagesTo(new SingleShardPmRepository());
        }

        @Test
        @DisplayName("to several shards")
        void toSeveralShards() throws Exception {
            ParallelDispatcher<ProjectId, Command> dispatcher =
                    new ParallelDispatcher<ProjectId, Command>(
                            59, 473, dispatchWaitTime()) {
                        @Override
                        protected ThreadStats<ProjectId> getStats() {
                            return DeliveryPm.getStats();
                        }

                        @Override
                        protected Command newMessage() {
                            return createProject();
                        }

                        @Override
                        protected void postToBus(BoundedContext context, Command command) {
                            context.getCommandBus()
                                   .post(command, StreamObservers.noOpObserver());
                        }
                    };

            dispatcher.dispatchMessagesTo(new QuadrupleShardPmRepository());
        }
    }

    @Nested
    @DisplayName("in multithreaded env, dispatch events")
    class DispatchEvents {

        @Test
        @DisplayName("to single shard")
        void toSingleShard() throws Exception {
            ParallelDispatcher<ProjectId, Event> dispatcher =
                    new ParallelDispatcher<ProjectId, Event>(
                            180, 819, dispatchWaitTime()) {
                        @Override
                        protected ThreadStats<ProjectId> getStats() {
                            return DeliveryPm.getStats();
                        }

                        @Override
                        protected Event newMessage() {
                            return projectStarted();
                        }

                        @Override
                        protected void postToBus(BoundedContext context, Event event) {
                            context.getEventBus()
                                   .post(event, StreamObservers.noOpObserver());
                        }
                    };

            dispatcher.dispatchMessagesTo(new SingleShardPmRepository());
        }

        @Test
        @DisplayName("to several shards")
        void toSeveralShards() throws Exception {
            ParallelDispatcher<ProjectId, Event> dispatcher =
                    new ParallelDispatcher<ProjectId, Event>(
                            179, 918, dispatchWaitTime()) {
                        @Override
                        protected ThreadStats<ProjectId> getStats() {
                            return DeliveryPm.getStats();
                        }

                        @Override
                        protected Event newMessage() {
                            return projectStarted();
                        }

                        @Override
                        protected void postToBus(BoundedContext context, Event event) {
                            context.getEventBus()
                                   .post(event, StreamObservers.noOpObserver());
                        }
                    };

            dispatcher.dispatchMessagesTo(new QuadrupleShardPmRepository());
        }
    }

    @Nested
    @DisplayName("in multithreaded env, dispatch rejections")
    class DispatchRejections {

        @Test
        @DisplayName("to single shard")
        void toSingleShard() throws Exception {
            ParallelDispatcher<ProjectId, Rejection> dispatcher =
                    new ParallelDispatcher<ProjectId, Rejection>(
                            30, 619, dispatchWaitTime()) {
                        @Override
                        protected ThreadStats<ProjectId> getStats() {
                            return DeliveryPm.getStats();
                        }

                        @Override
                        protected Rejection newMessage() {
                            return cannotStartProject();
                        }

                        @Override
                        protected void postToBus(BoundedContext context, Rejection rejection) {
                            context.getRejectionBus()
                                   .post(rejection, StreamObservers.noOpObserver());
                        }
                    };

            dispatcher.dispatchMessagesTo(new SingleShardPmRepository());
        }

        @Test
        @DisplayName("to several shards")
        void toSeveralShards() throws Exception {
            ParallelDispatcher<ProjectId, Rejection> dispatcher =
                    new ParallelDispatcher<ProjectId, Rejection>(
                            43, 719, dispatchWaitTime()) {
                        @Override
                        protected ThreadStats<ProjectId> getStats() {
                            return DeliveryPm.getStats();
                        }

                        @Override
                        protected Rejection newMessage() {
                            return cannotStartProject();
                        }

                        @Override
                        protected void postToBus(BoundedContext context, Rejection rejection) {
                            context.getRejectionBus()
                                   .post(rejection, StreamObservers.noOpObserver());
                        }
                    };

            dispatcher.dispatchMessagesTo(new QuadrupleShardPmRepository());
        }
    }
}

@@ -1101,40 +1101,8 @@
 nt;%0A
-import io.spine.core.Rejection;%0A
 impo
@@ -1881,97 +1881,8 @@
 me;%0A
-import static io.spine.server.procman.given.PmMessageDeliveryTestEnv.cannotStartProject;%0A
 impo
@@ -6814,2253 +6814,8 @@
 %7D;%0A%0A
-            dispatcher.dispatchMessagesTo(new QuadrupleShardPmRepository());%0A        %7D%0A    %7D%0A%0A    @Nested%0A    @DisplayName(%22in multithreaded env, dispatch rejections%22)%0A    class DispatchRejections %7B%0A%0A        @Test%0A        @DisplayName(%22to single shard%22)%0A        void toSingleShard() throws Exception %7B%0A            ParallelDispatcher%3CProjectId, Rejection%3E dispatcher =%0A                    new ParallelDispatcher%3CProjectId, Rejection%3E(%0A                            30, 619, dispatchWaitTime()) %7B%0A                        @Override%0A                        protected ThreadStats%3CProjectId%3E getStats() %7B%0A                            return DeliveryPm.getStats();%0A                        %7D%0A%0A                        @Override%0A                        protected Rejection newMessage() %7B%0A                            return cannotStartProject();%0A                        %7D%0A%0A                        @Override%0A                        protected void postToBus(BoundedContext context, Rejection rejection) %7B%0A                            context.getRejectionBus()%0A                                   .post(rejection, StreamObservers.noOpObserver());%0A                        %7D%0A                    %7D;%0A%0A            dispatcher.dispatchMessagesTo(new SingleShardPmRepository());%0A        %7D%0A%0A        @Test%0A        @DisplayName(%22to several shards%22)%0A        void toSeveralShards() throws Exception %7B%0A            ParallelDispatcher%3CProjectId, Rejection%3E dispatcher =%0A                    new ParallelDispatcher%3CProjectId, Rejection%3E(%0A                            43, 719, dispatchWaitTime()) %7B%0A                        @Override%0A                        protected ThreadStats%3CProjectId%3E getStats() %7B%0A                            return DeliveryPm.getStats();%0A                        %7D%0A%0A                        @Override%0A                        protected Rejection newMessage() %7B%0A                            return cannotStartProject();%0A                        %7D%0A%0A                        @Override%0A                        protected void postToBus(BoundedContext context, Rejection rejection) %7B%0A                            context.getRejectionBus()%0A                                   .post(rejection, StreamObservers.noOpObserver());%0A                        %7D%0A                    %7D;%0A%0A
     

Delete obsolete tests.