/*
 * Copyright 2019, 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.tuple;

import com.google.common.testing.EqualsTester;
import com.google.protobuf.Timestamp;
import io.spine.base.Time;
import io.spine.testing.TestValues;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

import java.util.Optional;

import static com.google.common.testing.SerializableTester.reserializeAndAssert;
import static org.junit.jupiter.api.Assertions.assertThrows;

@DisplayName("Element should")
class ElementTest {

    @Test
    @DisplayName("support equality")
    void supportEquality() {
        Timestamp time = Time.currentTime();
        new EqualsTester().addEqualityGroup(new Element(time), new Element(time))
                          .addEqualityGroup(new Element(TestValues.newUuidValue()))
                          .addEqualityGroup(new Element(Optional.empty()))
                          .testEquals();
    }

    @Nested
    @DisplayName("serialize if contains")
    class Serialize {

        @Test
        @DisplayName("Message")
        void message() {
            reserializeAndAssert(new Element(Time.currentTime()));
        }

        @Test
        @DisplayName("Optional with Message")
        void optionalMessage() {
            reserializeAndAssert(new Element(Optional.of(Time.currentTime())));
        }

        @Test
        @DisplayName("empty Optional")
        void emptyOptional() {
            reserializeAndAssert(new Element(Optional.empty()));
        }
    }

    @Test
    @DisplayName("restrict possible value types")
    void restrictPossibleValueTypes() {
        assertThrows(IllegalArgumentException.class, () -> new Element(getClass()));
    }
}

@@ -1116,122 +1116,288 @@
 buf.
-Timestamp;%0Aimport io.spine.base.Time;%0Aimport io.spine.testing.TestValues;%0Aimport org.junit.jupiter.api.DisplayName
+Message;%0Aimport com.google.protobuf.Timestamp;%0Aimport com.google.protobuf.util.Durations;%0Aimport io.spine.base.Time;%0Aimport org.junit.jupiter.api.DisplayName;%0Aimport org.junit.jupiter.api.Nested;%0Aimport org.junit.jupiter.api.Test;%0Aimport org.junit.jupiter.params.ParameterizedTest
 ;%0Aim
@@ -1415,34 +1415,49 @@
 nit.jupiter.
-api.Nested
+params.provider.Arguments
 ;%0Aimport org
@@ -1471,24 +1471,44 @@
 jupiter.
-api.Test
+params.provider.MethodSource
 ;%0A%0Aimpor
@@ -1528,16 +1528,48 @@
 ptional;
+%0Aimport java.util.stream.Stream;
 %0A%0Aimport
@@ -1643,16 +1643,128 @@
 Assert;%0A
+import static com.google.common.truth.Truth.assertThat;%0Aimport static io.spine.testing.TestValues.newUuidValue;%0A
 import s
@@ -2128,27 +2128,16 @@
 Element(
-TestValues.
 newUuidV
@@ -2264,24 +2264,1240 @@
 s();%0A    %7D%0A%0A
+    @ParameterizedTest(name = %22%7B1%7D%22)%0A    @MethodSource(%22elementArgument%22)%0A    @DisplayName(%22allow argument of type%22)%0A    void argumentType(Object arg,%0A                      @SuppressWarnings(%22unused%22) // is used for the type in test display names%0A                              String typeName) %7B%0A        assertThat(new Element(arg).value()).isEqualTo(arg);%0A    %7D%0A%0A    /**%0A     * Provides arguments for %7B@link #argumentType(Object, String)%7D.%0A     *%0A     * %3Cp%3EThe first argument is the value used by the test. The second argument is the type name%0A     * to be used in the display test name.%0A     */%0A    private static Stream%3CObject%3E elementArgument() %7B%0A        return Stream.of(%0A                Arguments.of(EitherOf2.withA(newUuidValue()), Either.class.getSimpleName()),%0A                Arguments.of(Optional.of(Time.currentTime()), Optional.class.getSimpleName()),%0A                Arguments.of(Durations.fromDays(100), Message.class.getSimpleName())%0A        );%0A    %7D%0A%0A    @Test%0A    @DisplayName(%22do not allow types other than %60Either%60, %60Optional%60, or %60Message%60%22)%0A    void noOtherTypes() %7B%0A        assertThrows(IllegalArgumentException.class, () -%3E%0A                new Element(getClass().getName())%0A        );%0A    %7D%0A%0A
     @Nested%0A

Test supported and non-supported args