diff --git a/events-domain/src/main/java/io/split/android/client/events/metadata/EventMetadataBuilder.java b/events-domain/src/main/java/io/split/android/client/events/metadata/EventMetadataBuilder.java deleted file mode 100644 index 76cb30289..000000000 --- a/events-domain/src/main/java/io/split/android/client/events/metadata/EventMetadataBuilder.java +++ /dev/null @@ -1,103 +0,0 @@ -package io.split.android.client.events.metadata; - -import androidx.annotation.NonNull; -import androidx.annotation.VisibleForTesting; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import io.split.android.client.api.EventMetadata; - -/** - * Builder for creating {@link EventMetadata} instances. - *

- * Values are validated during put operations. Only String, Number, Boolean, - * and List<String> values are accepted. Invalid values will be silently ignored. - */ -class EventMetadataBuilder { - - private static final MetadataValidator DEFAULT_VALIDATOR = new MetadataValidatorImpl(); - - private final Map mData = new HashMap<>(); - private final MetadataValidator mValidator; - - EventMetadataBuilder() { - this(DEFAULT_VALIDATOR); - } - - @VisibleForTesting - EventMetadataBuilder(@NonNull MetadataValidator validator) { - mValidator = validator; - } - - /** - * Adds a String value to the metadata. - * - * @param key the key - * @param value the String value - * @return this builder - */ - @NonNull - public EventMetadataBuilder put(@NonNull String key, @NonNull String value) { - if (mValidator.isValidValue(value)) { - mData.put(key, value); - } - return this; - } - - /** - * Adds a Number value to the metadata. - * - * @param key the key - * @param value the Number value (Integer, Long, Double, Float, etc.) - * @return this builder - */ - @NonNull - public EventMetadataBuilder put(@NonNull String key, @NonNull Number value) { - if (mValidator.isValidValue(value)) { - mData.put(key, value); - } - return this; - } - - /** - * Adds a Boolean value to the metadata. - * - * @param key the key - * @param value the Boolean value - * @return this builder - */ - @NonNull - public EventMetadataBuilder put(@NonNull String key, boolean value) { - if (mValidator.isValidValue(value)) { - mData.put(key, value); - } - return this; - } - - /** - * Adds a List of Strings to the metadata. - * - * @param key the key - * @param value the list of strings - * @return this builder - */ - @NonNull - public EventMetadataBuilder put(@NonNull String key, @NonNull List value) { - if (mValidator.isValidValue(value)) { - mData.put(key, value); - } - return this; - } - - /** - * Builds the {@link EventMetadata} instance. - * - * @return a new immutable EventMetadata instance - */ - @NonNull - public EventMetadata build() { - return new EventMetadataImpl(mData); - } -} diff --git a/events-domain/src/main/java/io/split/android/client/events/metadata/EventMetadataHelpers.java b/events-domain/src/main/java/io/split/android/client/events/metadata/EventMetadataHelpers.java index d7fc334a7..670a2b24b 100644 --- a/events-domain/src/main/java/io/split/android/client/events/metadata/EventMetadataHelpers.java +++ b/events-domain/src/main/java/io/split/android/client/events/metadata/EventMetadataHelpers.java @@ -3,6 +3,7 @@ import androidx.annotation.Nullable; import java.util.ArrayList; +import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -11,39 +12,85 @@ /** * Helper class for creating {@link EventMetadata} instances. *

- * This keeps the metadata keys in a single place to avoid typos and inconsistencies. + * Use these factory methods to create metadata for different event types. */ public class EventMetadataHelpers { - private static final String KEY_UPDATED_FLAGS = "updatedFlags"; - private static final String KEY_LAST_UPDATE_TIMESTAMP = "lastUpdateTimestamp"; - private static final String KEY_FRESH_INSTALL = "freshInstall"; - private EventMetadataHelpers() { // Utility class } - public static EventMetadata createUpdatedFlagsMetadata(List updatedSplitNames) { - return new EventMetadataBuilder() - .put(KEY_UPDATED_FLAGS, new ArrayList<>(new HashSet<>(updatedSplitNames))) - .build(); + /** + * Creates metadata for a FLAG_UPDATE event. + *

+ * Flag names are deduplicated automatically. + * + * @param flagNames the names of flags that were updated + * @param changeNumber the changeNumber associated with this update, or null if not available + * @return the event metadata + */ + public static EventMetadata createFlagUpdateMetadata( + List flagNames, + @Nullable Long changeNumber) { + // Deduplicate flag names + List uniqueFlags = new ArrayList<>(new HashSet<>(flagNames)); + return new EventMetadataImpl( + EventMetadata.Type.FLAG_UPDATE, + uniqueFlags, + changeNumber + ); } /** - * Creates metadata for the SDK_READY_FROM_CACHE event. + * Creates metadata for a SEGMENT_UPDATE event. + *

+ * Segment names are deduplicated automatically. * - * @param lastUpdateTimestamp the timestamp when the cache was last updated, or null if not available - * @param freshInstall true if this is a fresh install (no prior cache), false if loaded from cache + * @param segmentNames the names of segments that were updated + * @param changeNumber the changeNumber associated with this update, or null if not available * @return the event metadata */ - public static EventMetadata createCacheReadyMetadata(@Nullable Long lastUpdateTimestamp, boolean freshInstall) { - EventMetadataBuilder builder = new EventMetadataBuilder() - .put(KEY_FRESH_INSTALL, freshInstall); + public static EventMetadata createSegmentUpdateMetadata( + List segmentNames, + @Nullable Long changeNumber) { + // Deduplicate segment names + List uniqueSegments = new ArrayList<>(new HashSet<>(segmentNames)); + return new EventMetadataImpl( + EventMetadata.Type.SEGMENT_UPDATE, + uniqueSegments, + changeNumber + ); + } - if (lastUpdateTimestamp != null) { - builder.put(KEY_LAST_UPDATE_TIMESTAMP, lastUpdateTimestamp); - } + /** + * Creates metadata for a FRESH_INSTALL event. + *

+ * This is used when SDK_READY_FROM_CACHE fires but there was no prior cache + * (fresh install scenario). + * + * @return the event metadata + */ + public static EventMetadata createFreshInstallMetadata() { + return new EventMetadataImpl( + EventMetadata.Type.FRESH_INSTALL, + Collections.emptyList(), + null + ); + } - return builder.build(); + /** + * Creates metadata for a FROM_CACHE event. + *

+ * This is used when SDK_READY_FROM_CACHE fires after loading from existing cache. + * + * @param lastUpdateTimestamp the timestamp when the cache was last updated + * @return the event metadata + */ + public static EventMetadata createFromCacheMetadata(long lastUpdateTimestamp) { + return new EventMetadataImpl( + EventMetadata.Type.FROM_CACHE, + Collections.emptyList(), + lastUpdateTimestamp + ); } } diff --git a/events-domain/src/main/java/io/split/android/client/events/metadata/EventMetadataImpl.java b/events-domain/src/main/java/io/split/android/client/events/metadata/EventMetadataImpl.java index 8c9b73ffa..248cb9f86 100644 --- a/events-domain/src/main/java/io/split/android/client/events/metadata/EventMetadataImpl.java +++ b/events-domain/src/main/java/io/split/android/client/events/metadata/EventMetadataImpl.java @@ -4,71 +4,53 @@ import androidx.annotation.Nullable; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import java.util.Set; import io.split.android.client.api.EventMetadata; /** * Implementation of {@link EventMetadata}. - * Use {@link EventMetadataBuilder} to create instances. + * Use {@link EventMetadataHelpers} factory methods to create instances. */ -class EventMetadataImpl implements EventMetadata { +public class EventMetadataImpl implements EventMetadata { - private final Map mData; - - EventMetadataImpl(@NonNull Map data) { - Map copy = new HashMap<>(); - for (Map.Entry entry : data.entrySet()) { - Object value = entry.getValue(); - if (value instanceof List) { - copy.put(entry.getKey(), Collections.unmodifiableList(new ArrayList<>((List) value))); - } else { - copy.put(entry.getKey(), value); - } - } - mData = Collections.unmodifiableMap(copy); + @NonNull + private final Type mType; + @NonNull + private final List mValues; + @Nullable + private final Long mValue; + + /** + * Creates a new EventMetadataImpl. + * + * @param type the type of metadata + * @param values the list of values (flag names, segment names, etc.) + * @param value the numeric value (changeNumber, timestamp, etc.) + */ + public EventMetadataImpl(@NonNull Type type, @NonNull List values, @Nullable Long value) { + mType = type; + // Defensive copy to ensure immutability + mValues = Collections.unmodifiableList(new ArrayList<>(values)); + mValue = value; } @NonNull @Override - public Set keys() { - return mData.keySet(); + public Type getType() { + return mType; } @NonNull @Override - public Collection values() { - return mData.values(); + public List getValues() { + return mValues; } @Nullable @Override - public Object get(@NonNull String key) { - return mData.get(key); - } - - @Override - public boolean containsKey(@NonNull String key) { - return mData.containsKey(key); - } - - @NonNull - @Override - public Map toMap() { - Map copy = new HashMap<>(); - for (Map.Entry entry : mData.entrySet()) { - Object value = entry.getValue(); - if (value instanceof List) { - copy.put(entry.getKey(), new ArrayList<>((List) value)); - } else { - copy.put(entry.getKey(), value); - } - } - return copy; + public Long getValue() { + return mValue; } } diff --git a/events-domain/src/main/java/io/split/android/client/events/metadata/MetadataValidator.java b/events-domain/src/main/java/io/split/android/client/events/metadata/MetadataValidator.java deleted file mode 100644 index 3a25ff3ba..000000000 --- a/events-domain/src/main/java/io/split/android/client/events/metadata/MetadataValidator.java +++ /dev/null @@ -1,8 +0,0 @@ -package io.split.android.client.events.metadata; - -import androidx.annotation.Nullable; - -interface MetadataValidator { - - boolean isValidValue(@Nullable Object value); -} diff --git a/events-domain/src/main/java/io/split/android/client/events/metadata/MetadataValidatorImpl.java b/events-domain/src/main/java/io/split/android/client/events/metadata/MetadataValidatorImpl.java deleted file mode 100644 index 64a579d53..000000000 --- a/events-domain/src/main/java/io/split/android/client/events/metadata/MetadataValidatorImpl.java +++ /dev/null @@ -1,31 +0,0 @@ -package io.split.android.client.events.metadata; - -import androidx.annotation.Nullable; - -import java.util.List; - -class MetadataValidatorImpl implements MetadataValidator { - - @Override - public boolean isValidValue(@Nullable Object value) { - if (value == null) { - return false; - } - - if (value instanceof String || value instanceof Number || value instanceof Boolean) { - return true; - } - - if (value instanceof List) { - List list = (List) value; - for (Object item : list) { - if (!(item instanceof String)) { - return false; - } - } - return true; - } - - return false; - } -} diff --git a/events-domain/src/test/java/io/split/android/client/events/metadata/EventMetadataBuilderTest.java b/events-domain/src/test/java/io/split/android/client/events/metadata/EventMetadataBuilderTest.java deleted file mode 100644 index 4652c1ba5..000000000 --- a/events-domain/src/test/java/io/split/android/client/events/metadata/EventMetadataBuilderTest.java +++ /dev/null @@ -1,201 +0,0 @@ -package io.split.android.client.events.metadata; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.util.Arrays; -import java.util.List; - -import io.split.android.client.api.EventMetadata; - -public class EventMetadataBuilderTest { - - @Mock - private MetadataValidator mValidator; - - @Before - public void setUp() { - MockitoAnnotations.openMocks(this); - } - - @Test - public void putStringUsesValidator() { - when(mValidator.isValidValue(any())).thenReturn(true); - - new EventMetadataBuilder(mValidator) - .put("key", "value"); - - verify(mValidator).isValidValue("value"); - } - - @Test - public void putNumberUsesValidator() { - when(mValidator.isValidValue(any())).thenReturn(true); - - new EventMetadataBuilder(mValidator) - .put("key", 42); - - verify(mValidator).isValidValue(42); - } - - @Test - public void putBooleanUsesValidator() { - when(mValidator.isValidValue(any())).thenReturn(true); - - new EventMetadataBuilder(mValidator) - .put("key", true); - - verify(mValidator).isValidValue(true); - } - - @Test - public void putListUsesValidator() { - when(mValidator.isValidValue(any())).thenReturn(true); - List list = Arrays.asList("a", "b"); - - new EventMetadataBuilder(mValidator) - .put("key", list); - - verify(mValidator).isValidValue(list); - } - - @Test - public void putIgnoresValueWhenValidatorReturnsFalse() { - when(mValidator.isValidValue(any())).thenReturn(false); - - EventMetadata metadata = new EventMetadataBuilder(mValidator) - .put("key", "value") - .build(); - - assertFalse(metadata.containsKey("key")); - } - - @Test - public void putIncludesValueWhenValidatorReturnsTrue() { - when(mValidator.isValidValue(any())).thenReturn(true); - - EventMetadata metadata = new EventMetadataBuilder(mValidator) - .put("key", "value") - .build(); - - assertEquals("value", metadata.get("key")); - } - - @Test - public void buildCreatesEmptyMetadataWhenNothingAdded() { - EventMetadata metadata = new EventMetadataBuilder().build(); - - assertTrue(metadata.keys().isEmpty()); - } - - @Test - public void putStringAddsValue() { - EventMetadata metadata = new EventMetadataBuilder() - .put("key", "value") - .build(); - - assertEquals("value", metadata.get("key")); - } - - @Test - public void putIntegerAddsValue() { - EventMetadata metadata = new EventMetadataBuilder() - .put("count", 42) - .build(); - - assertEquals(42, metadata.get("count")); - } - - @Test - public void putLongAddsValue() { - EventMetadata metadata = new EventMetadataBuilder() - .put("timestamp", 1234567890L) - .build(); - - assertEquals(1234567890L, metadata.get("timestamp")); - } - - @Test - public void putDoubleAddsValue() { - EventMetadata metadata = new EventMetadataBuilder() - .put("rate", 3.14) - .build(); - - assertEquals(3.14, metadata.get("rate")); - } - - @Test - public void putBooleanTrueAddsValue() { - EventMetadata metadata = new EventMetadataBuilder() - .put("enabled", true) - .build(); - - assertEquals(true, metadata.get("enabled")); - } - - @Test - public void putBooleanFalseAddsValue() { - EventMetadata metadata = new EventMetadataBuilder() - .put("disabled", false) - .build(); - - assertEquals(false, metadata.get("disabled")); - } - - @Test - public void putListOfStringsAddsValue() { - List flags = Arrays.asList("flag_1", "flag_2", "flag_3"); - - EventMetadata metadata = new EventMetadataBuilder() - .put("updatedFlags", flags) - .build(); - - assertEquals(flags, metadata.get("updatedFlags")); - } - - @Test - public void chainingMultiplePutsWorks() { - EventMetadata metadata = new EventMetadataBuilder() - .put("string", "text") - .put("number", 100) - .put("flag", true) - .put("list", Arrays.asList("a", "b")) - .build(); - - assertEquals(4, metadata.keys().size()); - assertEquals("text", metadata.get("string")); - assertEquals(100, metadata.get("number")); - assertEquals(true, metadata.get("flag")); - assertEquals(Arrays.asList("a", "b"), metadata.get("list")); - } - - @Test - public void overwritingKeyUsesLastValue() { - EventMetadata metadata = new EventMetadataBuilder() - .put("key", "first") - .put("key", "second") - .build(); - - assertEquals("second", metadata.get("key")); - } - - @Test - public void buildReturnsNewInstanceEachTime() { - EventMetadataBuilder builder = new EventMetadataBuilder() - .put("key", "value"); - - EventMetadata metadata1 = builder.build(); - EventMetadata metadata2 = builder.build(); - - assertEquals(metadata1.get("key"), metadata2.get("key")); - } -} diff --git a/events-domain/src/test/java/io/split/android/client/events/metadata/EventMetadataHelpersTest.java b/events-domain/src/test/java/io/split/android/client/events/metadata/EventMetadataHelpersTest.java index d21bc8d3f..747533fca 100644 --- a/events-domain/src/test/java/io/split/android/client/events/metadata/EventMetadataHelpersTest.java +++ b/events-domain/src/test/java/io/split/android/client/events/metadata/EventMetadataHelpersTest.java @@ -1,85 +1,160 @@ package io.split.android.client.events.metadata; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import org.junit.Test; import java.util.Arrays; +import java.util.Collections; import java.util.List; import io.split.android.client.api.EventMetadata; +/** + * Tests for the {@link EventMetadataHelpers} factory methods. + */ public class EventMetadataHelpersTest { - // Tests for createUpdatedFlagsMetadata (existing) + // Tests for createFlagUpdateMetadata @Test - public void createUpdatedFlagsMetadataContainsFlags() { - List flags = Arrays.asList("flag1", "flag2", "flag3"); - EventMetadata metadata = EventMetadataHelpers.createUpdatedFlagsMetadata(flags); - - assertTrue(metadata.containsKey("updatedFlags")); - @SuppressWarnings("unchecked") - List result = (List) metadata.get("updatedFlags"); - assertEquals(3, result.size()); - assertTrue(result.contains("flag1")); - assertTrue(result.contains("flag2")); - assertTrue(result.contains("flag3")); + public void createFlagUpdateMetadataReturnsCorrectType() { + EventMetadata metadata = EventMetadataHelpers.createFlagUpdateMetadata( + Arrays.asList("flag1", "flag2"), + null + ); + + assertEquals(EventMetadata.Type.FLAG_UPDATE, metadata.getType()); + } + + @Test + public void createFlagUpdateMetadataReturnsCorrectFlagNames() { + List flagNames = Arrays.asList("flag1", "flag2", "flag3"); + EventMetadata metadata = EventMetadataHelpers.createFlagUpdateMetadata(flagNames, null); + + List values = metadata.getValues(); + assertEquals(3, values.size()); + assertTrue(values.contains("flag1")); + assertTrue(values.contains("flag2")); + assertTrue(values.contains("flag3")); + } + + @Test + public void createFlagUpdateMetadataReturnsChangeNumber() { + Long changeNumber = 12345L; + EventMetadata metadata = EventMetadataHelpers.createFlagUpdateMetadata( + Arrays.asList("flag1"), + changeNumber + ); + + assertEquals(changeNumber, metadata.getValue()); + } + + @Test + public void createFlagUpdateMetadataWithNullChangeNumber() { + EventMetadata metadata = EventMetadataHelpers.createFlagUpdateMetadata( + Arrays.asList("flag1"), + null + ); + + assertNull(metadata.getValue()); + } + + @Test + public void createFlagUpdateMetadataDeduplicatesFlagNames() { + List flagNames = Arrays.asList("flag1", "flag2", "flag1", "flag3", "flag2"); + EventMetadata metadata = EventMetadataHelpers.createFlagUpdateMetadata(flagNames, null); + + List values = metadata.getValues(); + // Should have deduplicated - only 3 unique flags + assertEquals(3, values.size()); + } + + // Tests for createSegmentUpdateMetadata + @Test + public void createSegmentUpdateMetadataReturnsCorrectType() { + EventMetadata metadata = EventMetadataHelpers.createSegmentUpdateMetadata( + Arrays.asList("segment1", "segment2"), + null + ); + + assertEquals(EventMetadata.Type.SEGMENT_UPDATE, metadata.getType()); + } + + @Test + public void createSegmentUpdateMetadataReturnsCorrectSegmentNames() { + List segmentNames = Arrays.asList("segment1", "segment2"); + EventMetadata metadata = EventMetadataHelpers.createSegmentUpdateMetadata(segmentNames, null); + + List values = metadata.getValues(); + assertEquals(2, values.size()); + assertTrue(values.contains("segment1")); + assertTrue(values.contains("segment2")); + } + + @Test + public void createSegmentUpdateMetadataReturnsChangeNumber() { + Long changeNumber = 67890L; + EventMetadata metadata = EventMetadataHelpers.createSegmentUpdateMetadata( + Arrays.asList("segment1"), + changeNumber + ); + + assertEquals(changeNumber, metadata.getValue()); + } + + // Tests for createFreshInstallMetadata + @Test + public void createFreshInstallMetadataReturnsCorrectType() { + EventMetadata metadata = EventMetadataHelpers.createFreshInstallMetadata(); + + assertEquals(EventMetadata.Type.FRESH_INSTALL, metadata.getType()); } - // Tests for createCacheReadyMetadata @Test - public void createCacheReadyMetadataWithTimestampAndFreshInstallFalse() { - EventMetadata metadata = EventMetadataHelpers.createCacheReadyMetadata(1234567890L, false); + public void createFreshInstallMetadataReturnsEmptyValues() { + EventMetadata metadata = EventMetadataHelpers.createFreshInstallMetadata(); - assertEquals(1234567890L, metadata.get("lastUpdateTimestamp")); - assertEquals(false, metadata.get("freshInstall")); + assertTrue(metadata.getValues().isEmpty()); } @Test - public void createCacheReadyMetadataWithNullTimestampAndFreshInstallTrue() { - EventMetadata metadata = EventMetadataHelpers.createCacheReadyMetadata(null, true); + public void createFreshInstallMetadataReturnsNullValue() { + EventMetadata metadata = EventMetadataHelpers.createFreshInstallMetadata(); - assertNull(metadata.get("lastUpdateTimestamp")); - assertEquals(true, metadata.get("freshInstall")); + assertNull(metadata.getValue()); } + // Tests for createFromCacheMetadata @Test - public void createCacheReadyMetadataKeysAreCorrect() { - EventMetadata metadata = EventMetadataHelpers.createCacheReadyMetadata(123L, false); + public void createFromCacheMetadataReturnsCorrectType() { + EventMetadata metadata = EventMetadataHelpers.createFromCacheMetadata(1700000000000L); - assertTrue(metadata.containsKey("lastUpdateTimestamp")); - assertTrue(metadata.containsKey("freshInstall")); - assertEquals(2, metadata.keys().size()); + assertEquals(EventMetadata.Type.FROM_CACHE, metadata.getType()); } @Test - public void createCacheReadyMetadataWithZeroTimestamp() { - EventMetadata metadata = EventMetadataHelpers.createCacheReadyMetadata(0L, false); + public void createFromCacheMetadataReturnsEmptyValues() { + EventMetadata metadata = EventMetadataHelpers.createFromCacheMetadata(1700000000000L); - assertEquals(0L, metadata.get("lastUpdateTimestamp")); - assertEquals(false, metadata.get("freshInstall")); + assertTrue(metadata.getValues().isEmpty()); } @Test - public void createCacheReadyMetadataForCachePath() { - // Cache path: freshInstall=false, timestamp from storage - long storedTimestamp = 1700000000000L; - EventMetadata metadata = EventMetadataHelpers.createCacheReadyMetadata(storedTimestamp, false); + public void createFromCacheMetadataReturnsLastUpdateTimestamp() { + long timestamp = 1700000000000L; + EventMetadata metadata = EventMetadataHelpers.createFromCacheMetadata(timestamp); - assertFalse((Boolean) metadata.get("freshInstall")); - assertEquals(storedTimestamp, metadata.get("lastUpdateTimestamp")); + assertEquals(Long.valueOf(timestamp), metadata.getValue()); } @Test - public void createCacheReadyMetadataForSyncPath() { - // Sync path: freshInstall=true, timestamp=null - EventMetadata metadata = EventMetadataHelpers.createCacheReadyMetadata(null, true); + public void createFromCacheMetadataWithZeroTimestamp() { + EventMetadata metadata = EventMetadataHelpers.createFromCacheMetadata(0L); - assertTrue((Boolean) metadata.get("freshInstall")); - assertNull(metadata.get("lastUpdateTimestamp")); + assertEquals(Long.valueOf(0L), metadata.getValue()); } } diff --git a/events-domain/src/test/java/io/split/android/client/events/metadata/EventMetadataImplTest.java b/events-domain/src/test/java/io/split/android/client/events/metadata/EventMetadataImplTest.java index 54059494e..f1c9e895f 100644 --- a/events-domain/src/test/java/io/split/android/client/events/metadata/EventMetadataImplTest.java +++ b/events-domain/src/test/java/io/split/android/client/events/metadata/EventMetadataImplTest.java @@ -1,7 +1,7 @@ package io.split.android.client.events.metadata; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -9,207 +9,216 @@ import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; +import java.util.Collections; import java.util.List; -import java.util.Map; -import java.util.Set; +import io.split.android.client.api.EventMetadata; + +/** + * Tests for the unified {@link EventMetadataImpl}. + */ public class EventMetadataImplTest { + // Tests for FLAG_UPDATE type @Test - public void keysReturnsAllKeys() { - Map data = new HashMap<>(); - data.put("key1", "value1"); - data.put("key2", 42); - data.put("key3", true); - - EventMetadataImpl metadata = new EventMetadataImpl(data); - Set keys = metadata.keys(); - - assertEquals(3, keys.size()); - assertTrue(keys.contains("key1")); - assertTrue(keys.contains("key2")); - assertTrue(keys.contains("key3")); + public void flagUpdateMetadataReturnsCorrectType() { + List flagNames = Arrays.asList("flag1", "flag2"); + EventMetadata metadata = new EventMetadataImpl( + EventMetadata.Type.FLAG_UPDATE, + flagNames, + 12345L + ); + + assertEquals(EventMetadata.Type.FLAG_UPDATE, metadata.getType()); } @Test - public void keysReturnsEmptySetForEmptyMetadata() { - EventMetadataImpl metadata = new EventMetadataImpl(new HashMap<>()); - - assertTrue(metadata.keys().isEmpty()); + public void flagUpdateMetadataReturnsCorrectValues() { + List flagNames = Arrays.asList("flag1", "flag2", "flag3"); + EventMetadata metadata = new EventMetadataImpl( + EventMetadata.Type.FLAG_UPDATE, + flagNames, + null + ); + + List values = metadata.getValues(); + assertEquals(3, values.size()); + assertTrue(values.contains("flag1")); + assertTrue(values.contains("flag2")); + assertTrue(values.contains("flag3")); } @Test - public void valuesReturnsAllValues() { - Map data = new HashMap<>(); - data.put("string", "value"); - data.put("number", 42); - - EventMetadataImpl metadata = new EventMetadataImpl(data); - Collection values = metadata.values(); - - assertEquals(2, values.size()); - assertTrue(values.contains("value")); - assertTrue(values.contains(42)); + public void flagUpdateMetadataReturnsChangeNumber() { + List flagNames = Arrays.asList("flag1"); + Long changeNumber = 999L; + EventMetadata metadata = new EventMetadataImpl( + EventMetadata.Type.FLAG_UPDATE, + flagNames, + changeNumber + ); + + assertEquals(changeNumber, metadata.getValue()); } @Test - public void valuesReturnsEmptyCollectionForEmptyMetadata() { - EventMetadataImpl metadata = new EventMetadataImpl(new HashMap<>()); - - assertTrue(metadata.values().isEmpty()); + public void flagUpdateMetadataWithNullChangeNumber() { + List flagNames = Arrays.asList("flag1"); + EventMetadata metadata = new EventMetadataImpl( + EventMetadata.Type.FLAG_UPDATE, + flagNames, + null + ); + + assertNull(metadata.getValue()); } + // Tests for SEGMENT_UPDATE type @Test - public void getReturnsValueForExistingKey() { - Map data = new HashMap<>(); - data.put("key", "value"); - - EventMetadataImpl metadata = new EventMetadataImpl(data); - - assertEquals("value", metadata.get("key")); + public void segmentUpdateMetadataReturnsCorrectType() { + List segmentNames = Arrays.asList("segment1", "segment2"); + EventMetadata metadata = new EventMetadataImpl( + EventMetadata.Type.SEGMENT_UPDATE, + segmentNames, + null + ); + + assertEquals(EventMetadata.Type.SEGMENT_UPDATE, metadata.getType()); } @Test - public void getReturnsNullForNonExistingKey() { - Map data = new HashMap<>(); - data.put("key", "value"); - - EventMetadataImpl metadata = new EventMetadataImpl(data); - - assertNull(metadata.get("nonExistingKey")); + public void segmentUpdateMetadataReturnsCorrectValues() { + List segmentNames = Arrays.asList("segment1", "segment2"); + EventMetadata metadata = new EventMetadataImpl( + EventMetadata.Type.SEGMENT_UPDATE, + segmentNames, + null + ); + + List values = metadata.getValues(); + assertEquals(2, values.size()); + assertTrue(values.contains("segment1")); + assertTrue(values.contains("segment2")); } + // Tests for FRESH_INSTALL type @Test - public void containsKeyReturnsTrueForExistingKey() { - Map data = new HashMap<>(); - data.put("key", "value"); - - EventMetadataImpl metadata = new EventMetadataImpl(data); - - assertTrue(metadata.containsKey("key")); + public void freshInstallMetadataReturnsCorrectType() { + EventMetadata metadata = new EventMetadataImpl( + EventMetadata.Type.FRESH_INSTALL, + Collections.emptyList(), + null + ); + + assertEquals(EventMetadata.Type.FRESH_INSTALL, metadata.getType()); } @Test - public void containsKeyReturnsFalseForNonExistingKey() { - Map data = new HashMap<>(); - data.put("key", "value"); - - EventMetadataImpl metadata = new EventMetadataImpl(data); - - assertFalse(metadata.containsKey("nonExistingKey")); + public void freshInstallMetadataReturnsEmptyValues() { + EventMetadata metadata = new EventMetadataImpl( + EventMetadata.Type.FRESH_INSTALL, + Collections.emptyList(), + null + ); + + assertTrue(metadata.getValues().isEmpty()); } @Test - public void toMapReturnsACopyOfTheData() { - Map data = new HashMap<>(); - data.put("key", "value"); - - EventMetadataImpl metadata = new EventMetadataImpl(data); - Map copy = metadata.toMap(); - - assertEquals(1, copy.size()); - assertEquals("value", copy.get("key")); - - // Verify it's a copy by modifying it - copy.put("newKey", "newValue"); - assertFalse(metadata.containsKey("newKey")); + public void freshInstallMetadataReturnsNullValue() { + EventMetadata metadata = new EventMetadataImpl( + EventMetadata.Type.FRESH_INSTALL, + Collections.emptyList(), + null + ); + + assertNull(metadata.getValue()); } + // Tests for FROM_CACHE type @Test - public void toMapReturnsEmptyMapForEmptyMetadata() { - EventMetadataImpl metadata = new EventMetadataImpl(new HashMap<>()); - - assertTrue(metadata.toMap().isEmpty()); + public void fromCacheMetadataReturnsCorrectType() { + EventMetadata metadata = new EventMetadataImpl( + EventMetadata.Type.FROM_CACHE, + Collections.emptyList(), + 1700000000000L + ); + + assertEquals(EventMetadata.Type.FROM_CACHE, metadata.getType()); } @Test - public void toMapReturnsModifiableCopyOfLists() { - Map data = new HashMap<>(); - data.put("flags", Arrays.asList("flag_1", "flag_2")); - - EventMetadataImpl metadata = new EventMetadataImpl(data); - Map copy = metadata.toMap(); - - // Should be able to modify the list in the copy - @SuppressWarnings("unchecked") - List listInCopy = (List) copy.get("flags"); - listInCopy.add("flag_3"); - - // Original metadata should not be affected - @SuppressWarnings("unchecked") - List originalList = (List) metadata.get("flags"); - assertEquals(2, originalList.size()); - assertEquals(Arrays.asList("flag_1", "flag_2"), originalList); + public void fromCacheMetadataReturnsEmptyValues() { + EventMetadata metadata = new EventMetadataImpl( + EventMetadata.Type.FROM_CACHE, + Collections.emptyList(), + 1700000000000L + ); + + assertTrue(metadata.getValues().isEmpty()); } @Test - public void toMapListsAreIndependentAcrossCalls() { - Map data = new HashMap<>(); - data.put("flags", Arrays.asList("flag_1", "flag_2")); - - EventMetadataImpl metadata = new EventMetadataImpl(data); - - Map copy1 = metadata.toMap(); - Map copy2 = metadata.toMap(); - - // Modify copy1's list - @SuppressWarnings("unchecked") - List list1 = (List) copy1.get("flags"); - list1.add("flag_3"); - - // copy2's list should not be affected - @SuppressWarnings("unchecked") - List list2 = (List) copy2.get("flags"); - assertEquals(2, list2.size()); - assertEquals(Arrays.asList("flag_1", "flag_2"), list2); + public void fromCacheMetadataReturnsLastUpdateTimestamp() { + long timestamp = 1700000000000L; + EventMetadata metadata = new EventMetadataImpl( + EventMetadata.Type.FROM_CACHE, + Collections.emptyList(), + timestamp + ); + + assertEquals(Long.valueOf(timestamp), metadata.getValue()); } + // Immutability tests @Test - public void metadataIsImmutableAfterConstruction() { - Map data = new HashMap<>(); - data.put("key", "value"); - - EventMetadataImpl metadata = new EventMetadataImpl(data); - - // Modify original map - data.put("newKey", "newValue"); - - // Metadata should not be affected - assertFalse(metadata.containsKey("newKey")); - assertEquals(1, metadata.keys().size()); - } - - @Test - public void listIsDefensivelyCopiedDuringConstruction() { - List originalList = new ArrayList<>(Arrays.asList("flag_1", "flag_2")); - Map data = new HashMap<>(); - data.put("flags", originalList); - - EventMetadataImpl metadata = new EventMetadataImpl(data); + public void valuesListIsImmutable() { + List original = new ArrayList<>(Arrays.asList("flag1", "flag2")); + EventMetadata metadata = new EventMetadataImpl( + EventMetadata.Type.FLAG_UPDATE, + original, + null + ); // Modify original list after construction - originalList.add("flag_3"); + original.add("flag3"); // Metadata should not be affected - @SuppressWarnings("unchecked") - List storedList = (List) metadata.get("flags"); - assertEquals(2, storedList.size()); - assertEquals(Arrays.asList("flag_1", "flag_2"), storedList); + assertEquals(2, metadata.getValues().size()); } @Test(expected = UnsupportedOperationException.class) - public void listReturnedByGetIsUnmodifiable() { - Map data = new HashMap<>(); - data.put("flags", Arrays.asList("flag_1", "flag_2")); + public void valuesListReturnedIsUnmodifiable() { + EventMetadata metadata = new EventMetadataImpl( + EventMetadata.Type.FLAG_UPDATE, + Arrays.asList("flag1", "flag2"), + null + ); - EventMetadataImpl metadata = new EventMetadataImpl(data); + // This should throw UnsupportedOperationException + metadata.getValues().add("flag3"); + } - @SuppressWarnings("unchecked") - List list = (List) metadata.get("flags"); + @Test + public void getValuesNeverReturnsNull() { + EventMetadata metadata = new EventMetadataImpl( + EventMetadata.Type.FRESH_INSTALL, + Collections.emptyList(), + null + ); + + assertNotNull(metadata.getValues()); + } - // This should throw UnsupportedOperationException - list.add("flag_3"); + @Test + public void getTypeNeverReturnsNull() { + EventMetadata metadata = new EventMetadataImpl( + EventMetadata.Type.FLAG_UPDATE, + Collections.emptyList(), + null + ); + + assertNotNull(metadata.getType()); } } + diff --git a/events-domain/src/test/java/io/split/android/client/events/metadata/MetadataValidatorImplTest.java b/events-domain/src/test/java/io/split/android/client/events/metadata/MetadataValidatorImplTest.java deleted file mode 100644 index ad3098ef2..000000000 --- a/events-domain/src/test/java/io/split/android/client/events/metadata/MetadataValidatorImplTest.java +++ /dev/null @@ -1,132 +0,0 @@ -package io.split.android.client.events.metadata; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import org.junit.Before; -import org.junit.Test; - -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; - -public class MetadataValidatorImplTest { - - private MetadataValidator mValidator; - - @Before - public void setUp() { - mValidator = new MetadataValidatorImpl(); - } - - @Test - public void isValidValueReturnsTrueForString() { - assertTrue(mValidator.isValidValue("value")); - } - - @Test - public void isValidValueReturnsTrueForEmptyString() { - assertTrue(mValidator.isValidValue("")); - } - - @Test - public void isValidValueReturnsTrueForInteger() { - assertTrue(mValidator.isValidValue(42)); - } - - @Test - public void isValidValueReturnsTrueForLong() { - assertTrue(mValidator.isValidValue(1234567890L)); - } - - @Test - public void isValidValueReturnsTrueForDouble() { - assertTrue(mValidator.isValidValue(3.14)); - } - - @Test - public void isValidValueReturnsTrueForFloat() { - assertTrue(mValidator.isValidValue(2.5f)); - } - - @Test - public void isValidValueReturnsTrueForBooleanTrue() { - assertTrue(mValidator.isValidValue(true)); - } - - @Test - public void isValidValueReturnsTrueForBooleanFalse() { - assertTrue(mValidator.isValidValue(false)); - } - - @Test - public void isValidValueReturnsTrueForListOfStrings() { - List list = Arrays.asList("flag_1", "flag_2", "flag_3"); - assertTrue(mValidator.isValidValue(list)); - } - - @Test - public void isValidValueReturnsTrueForEmptyList() { - assertTrue(mValidator.isValidValue(Collections.emptyList())); - } - - @Test - public void isValidValueReturnsTrueForSingleElementStringList() { - assertTrue(mValidator.isValidValue(Collections.singletonList("single"))); - } - - @Test - public void isValidValueReturnsFalseForNull() { - assertFalse(mValidator.isValidValue(null)); - } - - @Test - public void isValidValueReturnsFalseForListWithNullElement() { - List list = Arrays.asList("valid", null, "also_valid"); - assertFalse(mValidator.isValidValue(list)); - } - - @Test - public void isValidValueReturnsFalseForListWithMixedTypes() { - List mixedList = Arrays.asList("string", 123, true); - assertFalse(mValidator.isValidValue(mixedList)); - } - - @Test - public void isValidValueReturnsFalseForListOfIntegers() { - List intList = Arrays.asList(1, 2, 3); - assertFalse(mValidator.isValidValue(intList)); - } - - @Test - public void isValidValueReturnsFalseForListOfBooleans() { - List boolList = Arrays.asList(true, false, true); - assertFalse(mValidator.isValidValue(boolList)); - } - - @Test - public void isValidValueReturnsFalseForPlainObject() { - assertFalse(mValidator.isValidValue(new Object())); - } - - @Test - public void isValidValueReturnsFalseForMap() { - assertFalse(mValidator.isValidValue(new HashMap())); - } - - @Test - public void isValidValueReturnsFalseForNestedList() { - List> nestedList = Arrays.asList( - Arrays.asList("a", "b"), - Arrays.asList("c", "d") - ); - assertFalse(mValidator.isValidValue(nestedList)); - } - - @Test - public void isValidValueReturnsFalseForArray() { - String[] array = {"a", "b", "c"}; - assertFalse(mValidator.isValidValue(array)); - } -}