/*
 * 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.entity;

import com.google.protobuf.Empty;
import com.google.protobuf.Timestamp;
import io.spine.base.Identifier;
import io.spine.base.Time;
import io.spine.core.EventId;
import io.spine.core.Events;
import io.spine.system.server.EntityCreated;
import io.spine.system.server.EntityStateChanged;
import io.spine.system.server.MemoizingGateway;
import io.spine.system.server.NoOpSystemGateway;
import io.spine.type.TypeUrl;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.util.Optional;

import static com.google.common.collect.ImmutableSet.of;
import static io.spine.option.EntityOption.Kind.ENTITY;
import static io.spine.protobuf.AnyPacker.pack;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertSame;

/**
 * @author Dmytro Dashenkov
 */
@DisplayName("EntityLifecycle should")
class EntityLifecycleTest {

    @Test
    @DisplayName("be created with a default EventFilter")
    void allowCreationWithoutEventFilter() {
        EntityLifecycle lifecycle = EntityLifecycle
                .newBuilder()
                .setGateway(NoOpSystemGateway.INSTANCE)
                .setEntityType(TypeUrl.of(Empty.class))
                .setEntityId("sample-id")
                .build();
        assertNotNull(lifecycle);
    }

    @Test
    @DisplayName("filter system events before posting")
    void filterEventsBeforePosting() {
        EventFilter filter = event -> {
            boolean stateChanged = Events.getMessage(event) instanceof EntityStateChanged;
            return stateChanged
                   ? Optional.empty()
                   : Optional.of(event);
        };
        MemoizingGateway gateway = MemoizingGateway.singleTenant();
        int entityId = 42;
        EntityLifecycle lifecycle = EntityLifecycle
                .newBuilder()
                .setEntityId(entityId)
                .setEntityType(TypeUrl.of(Timestamp.class))
                .setGateway(gateway)
                .setEventFilter(filter)
                .build();
        lifecycle.onEntityCreated(ENTITY);
        MemoizingGateway.MemoizedMessage lastSeenEvent = gateway.lastSeenEvent();
        assertThat(lastSeenEvent.message(), instanceOf(EntityCreated.class));


        EntityRecord previousRecord = EntityRecord
                .newBuilder()
                .setEntityId(Identifier.pack(entityId))
                .setState(pack(Timestamp.getDefaultInstance()))
                .build();
        EntityRecord newRecord = previousRecord
                .toBuilder()
                .setState(pack(Time.getCurrentTime()))
                .build();
        EntityRecordChange change = EntityRecordChange
                .newBuilder()
                .setPreviousValue(previousRecord)
                .setNewValue(newRecord)
                .build();
        lifecycle.onStateChanged(change, of(EventId.getDefaultInstance()));
        assertSame(lastSeenEvent, gateway.lastSeenEvent());
    }
}

@@ -1039,16 +1039,68 @@
 ntity;%0A%0A
+import com.google.common.testing.NullPointerTester;%0A
 import c
@@ -1125,16 +1125,16 @@
 .Empty;%0A
-
 import c
@@ -1667,16 +1667,94 @@
 Set.of;%0A
+import static com.google.common.testing.NullPointerTester.Visibility.PACKAGE;%0A
 import s
@@ -2147,16 +2147,16 @@
 hould%22)%0A
-
 class En
@@ -2176,16 +2176,330 @@
 Test %7B%0A%0A
+    @Test%0A    @DisplayName(%22not allow nulls in Builder%22)%0A    void nullTolerance() %7B%0A        new NullPointerTester()%0A                .setDefault(TypeUrl.class, TypeUrl.of(Empty.class))%0A                .setDefault(Object.class, 42)%0A                .testInstanceMethods(EntityLifecycle.newBuilder(), PACKAGE);%0A    %7D%0A%0A
     @Tes

Add a null tolerance test.