diff --git a/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/CraftDSimplePropertiesDiffCallbackTest.kt b/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/CraftDSimplePropertiesDiffCallbackTest.kt new file mode 100644 index 0000000..ea8ae5b --- /dev/null +++ b/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/CraftDSimplePropertiesDiffCallbackTest.kt @@ -0,0 +1,244 @@ +package com.github.codandotv.craftd.androidcore + +import androidx.recyclerview.widget.DiffUtil +import com.github.codandotv.craftd.androidcore.data.model.base.SimpleProperties +import io.mockk.mockk +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonPrimitive +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import java.util.AbstractMap + +@RunWith(JUnit4::class) +class CraftDSimplePropertiesDiffCallbackTest { + + private val callback = CraftDSimplePropertiesItemCallback + + @Test + fun `given same key when areItemsTheSame then returns true`() { + val oldItem = SimpleProperties(key = "test_key", value = JsonPrimitive("value1")) + val newItem = SimpleProperties(key = "test_key", value = JsonPrimitive("value2")) + + val result = callback.areItemsTheSame(oldItem, newItem) + + assertTrue(result) + } + + @Test + fun `given different key when areItemsTheSame then returns false`() { + val oldItem = SimpleProperties(key = "key1", value = JsonPrimitive("value")) + val newItem = SimpleProperties(key = "key2", value = JsonPrimitive("value")) + + val result = callback.areItemsTheSame(oldItem, newItem) + + assertFalse(result) + } + + @Test + fun `given exception during key comparison when areItemsTheSame then returns false`() { + val mockOldItem = mockk { + throws(RuntimeException("Test exception")) + } + val newItem = SimpleProperties(key = "key", value = JsonPrimitive("value")) + + val result = callback.areItemsTheSame(mockOldItem, newItem) + + assertFalse(result) + } + + @Test + fun `given identical primitive values when areContentsTheSame then returns true`() { + val value = JsonPrimitive("same_value") + val oldItem = SimpleProperties(key = "key1", value = value) + val newItem = SimpleProperties(key = "key1", value = value) + + val result = callback.areContentsTheSame(oldItem, newItem) + + assertTrue(result) + } + + @Test + fun `given different primitive values when areContentsTheSame then returns false`() { + val oldItem = SimpleProperties(key = "key1", value = JsonPrimitive("value1")) + val newItem = SimpleProperties(key = "key1", value = JsonPrimitive("value2")) + + val result = callback.areContentsTheSame(oldItem, newItem) + + assertFalse(result) + } + + @Test + fun `given identical AbstractMap values when areContentsTheSame then returns true`() { + val oldMap = object : AbstractMap() { + override val entries: Set> = setOf( + SimpleEntry("key", "value") + ) + } + val newMap = object : AbstractMap() { + override val entries: Set> = setOf( + SimpleEntry("key", "value") + ) + } + + val oldItem = SimpleProperties(key = "key1", value = mockk { }) + val newItem = SimpleProperties(key = "key1", value = mockk { }) + + val oldItemWithMap = oldItem.copy().apply { value = oldMap as JsonElement } + val newItemWithMap = newItem.copy().apply { value = newMap as JsonElement } + + val result = callback.areContentsTheSame( + oldItem.copy(), + newItem.copy() + ) + + assertTrue(result) + } + + @Test + fun `given different AbstractMap values when areContentsTheSame then returns false`() { + val oldMap = object : AbstractMap() { + override val entries: Set> = setOf( + SimpleEntry("key", "value1") + ) + } + val newMap = object : AbstractMap() { + override val entries: Set> = setOf( + SimpleEntry("key", "value2") + ) + } + + val oldItem = SimpleProperties(key = "key1", value = JsonPrimitive("value1")) + val newItem = SimpleProperties(key = "key1", value = JsonPrimitive("value2")) + + val result = callback.areContentsTheSame(oldItem, newItem) + + assertFalse(result) + } + + @Test + fun `given null values when areContentsTheSame then returns true`() { + val oldItem = SimpleProperties(key = "key1", value = JsonPrimitive(null)) + val newItem = SimpleProperties(key = "key1", value = JsonPrimitive(null)) + + val result = callback.areContentsTheSame(oldItem, newItem) + + assertTrue(result) + } + + @Test + fun `given one null and one non-null value when areContentsTheSame then returns false`() { + val oldItem = SimpleProperties(key = "key1", value = JsonPrimitive(null)) + val newItem = SimpleProperties(key = "key1", value = JsonPrimitive("value")) + + val result = callback.areContentsTheSame(oldItem, newItem) + + assertFalse(result) + } + + @Test + fun `given numeric JsonElement values when areContentsTheSame then returns true`() { + val value = JsonPrimitive(42) + val oldItem = SimpleProperties(key = "key1", value = value) + val newItem = SimpleProperties(key = "key1", value = value) + + val result = callback.areContentsTheSame(oldItem, newItem) + + assertTrue(result) + } + + @Test + fun `given boolean JsonElement values when areContentsTheSame then returns true`() { + val value = JsonPrimitive(true) + val oldItem = SimpleProperties(key = "key1", value = value) + val newItem = SimpleProperties(key = "key1", value = value) + + val result = callback.areContentsTheSame(oldItem, newItem) + + assertTrue(result) + } + + @Test + fun `given callback is ItemCallback instance then callback is properly initialized`() { + assertTrue(callback is DiffUtil.ItemCallback) + } + + @Test + fun `given different numeric values when areContentsTheSame then returns false`() { + val oldItem = SimpleProperties(key = "key1", value = JsonPrimitive(42)) + val newItem = SimpleProperties(key = "key1", value = JsonPrimitive(43)) + + val result = callback.areContentsTheSame(oldItem, newItem) + + assertFalse(result) + } + + @Test + fun `given empty AbstractMap when areContentsTheSame with AbstractMap comparison then returns true`() { + val oldMap = object : AbstractMap() { + override val entries: Set> = emptySet() + } + val newMap = object : AbstractMap() { + override val entries: Set> = emptySet() + } + + val oldItem = SimpleProperties(key = "key1", value = JsonPrimitive("{}")) + val newItem = SimpleProperties(key = "key1", value = JsonPrimitive("{}")) + + val result = callback.areContentsTheSame(oldItem, newItem) + + assertTrue(result) + } + + @Test + fun `given SimpleProperties with all parameters when constructing then all fields are set correctly`() { + val key = "test_key" + val value = JsonPrimitive("test_value") + + val simpleProperties = SimpleProperties(key = key, value = value) + + assertTrue(simpleProperties.key == key) + assertTrue(simpleProperties.value == value) + } + + @Test + fun `given SimpleProperties with copy when copying with different key then new instance has different key`() { + val original = SimpleProperties(key = "original_key", value = JsonPrimitive("value")) + val copied = original.copy(key = "new_key") + + assertTrue(copied.key == "new_key") + assertTrue(original.key == "original_key") + } + + @Test + fun `given two SimpleProperties with same values when comparing with equals then returns true`() { + val properties1 = SimpleProperties(key = "key", value = JsonPrimitive("value")) + val properties2 = SimpleProperties(key = "key", value = JsonPrimitive("value")) + + assertTrue(properties1 == properties2) + } + + @Test + fun `given two SimpleProperties with different values when comparing with equals then returns false`() { + val properties1 = SimpleProperties(key = "key1", value = JsonPrimitive("value")) + val properties2 = SimpleProperties(key = "key2", value = JsonPrimitive("value")) + + assertFalse(properties1 == properties2) + } + + @Test + fun `given two SimpleProperties with same values when comparing hashCode then returns same hash`() { + val properties1 = SimpleProperties(key = "key", value = JsonPrimitive("value")) + val properties2 = SimpleProperties(key = "key", value = JsonPrimitive("value")) + + assertTrue(properties1.hashCode() == properties2.hashCode()) + } + + private class SimpleEntry( + override val key: K, + override val value: V + ) : Map.Entry + +} \ No newline at end of file diff --git a/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/data/ViewMapperTest.kt b/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/data/ViewMapperTest.kt new file mode 100644 index 0000000..9448707 --- /dev/null +++ b/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/data/ViewMapperTest.kt @@ -0,0 +1,255 @@ +package com.github.codandotv.craftd.androidcore.data + +import com.github.codandotv.craftd.androidcore.data.model.base.SimpleProperties +import com.github.codandotv.craftd.androidcore.data.model.base.SimplePropertiesResponse +import io.mockk.MockKAnnotations +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonPrimitive +import kotlinx.serialization.json.buildJsonObject +import kotlinx.serialization.json.put +import kotlinx.serialization.Serializable +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import kotlin.test.assertEquals +import kotlin.test.assertNull +import kotlin.test.assertNotNull + +@RunWith(JUnit4::class) +class ViewMapperTest { + + @Before + fun setUp() { + MockKAnnotations.init(this) + } + + @Test + fun `given null JsonElement when convertToElement then returns null`() { + val result: String? = null.convertToElement() + assertNull(result) + } + + @Test + fun `given valid JsonElement for string when convertToElement then returns deserialized string`() { + val jsonElement = JsonPrimitive("test_value") + val result: String? = jsonElement.convertToElement() + assertEquals("test_value", result) + } + + @Test + fun `given valid JsonElement for number when convertToElement then returns deserialized int`() { + val jsonElement = JsonPrimitive(42) + val result: Int? = jsonElement.convertToElement() + assertEquals(42, result) + } + + @Test + fun `given valid JsonElement for boolean when convertToElement then returns deserialized boolean`() { + val jsonElement = JsonPrimitive(true) + val result: Boolean? = jsonElement.convertToElement() + assertEquals(true, result) + } + + @Test + fun `given valid complex JsonElement when convertToElement then returns deserialized object`() { + @Serializable + data class TestData(val name: String, val age: Int) + + val jsonElement = buildJsonObject { + put("name", "John") + put("age", 30) + } + val result: TestData? = jsonElement.convertToElement() + assertNotNull(result) + assertEquals("John", result.name) + assertEquals(30, result.age) + } + + @Test + fun `given JsonElement with unknown keys when convertToElement then ignores unknown keys and returns object`() { + @Serializable + data class TestData(val name: String) + + val jsonElement = buildJsonObject { + put("name", "John") + put("unknown_field", "value") + } + val result: TestData? = jsonElement.convertToElement() + assertNotNull(result) + assertEquals("John", result.name) + } + + @Test + fun `given malformed JsonElement when convertToElement then returns null`() { + @Serializable + data class TestData(val age: Int) + + val jsonElement = JsonPrimitive("not_a_number") + val result: TestData? = jsonElement.convertToElement() + assertNull(result) + } + + @Test + fun `given empty list when toListSimpleProperties then returns empty list`() { + val input: List = emptyList() + val result = input.toListSimpleProperties() + assertEquals(0, result.size) + } + + @Test + fun `given single SimplePropertiesResponse when toListSimpleProperties then returns list with single SimpleProperties`() { + val input = listOf( + SimplePropertiesResponse( + key = "test_key", + value = JsonPrimitive("test_value") + ) + ) + val result = input.toListSimpleProperties() + assertEquals(1, result.size) + assertEquals("test_key", result[0].key) + assertEquals(JsonPrimitive("test_value"), result[0].value) + } + + @Test + fun `given multiple SimplePropertiesResponse when toListSimpleProperties then returns list with all SimpleProperties`() { + val input = listOf( + SimplePropertiesResponse( + key = "key1", + value = JsonPrimitive("value1") + ), + SimplePropertiesResponse( + key = "key2", + value = JsonPrimitive("value2") + ), + SimplePropertiesResponse( + key = "key3", + value = JsonPrimitive("value3") + ) + ) + val result = input.toListSimpleProperties() + assertEquals(3, result.size) + assertEquals("key1", result[0].key) + assertEquals("key2", result[1].key) + assertEquals("key3", result[2].key) + } + + @Test + fun `given SimplePropertiesResponse with null value when toListSimpleProperties then preserves null value`() { + val input = listOf( + SimplePropertiesResponse( + key = "test_key", + value = JsonPrimitive(null) + ) + ) + val result = input.toListSimpleProperties() + assertEquals(1, result.size) + assertEquals("test_key", result[0].key) + assertNotNull(result[0].value) + } + + @Test + fun `given SimplePropertiesResponse when toSimpleProperties then returns SimpleProperties with same key and value`() { + val input = SimplePropertiesResponse( + key = "test_key", + value = JsonPrimitive("test_value") + ) + val result = input.toSimpleProperties() + assertEquals("test_key", result.key) + assertEquals(JsonPrimitive("test_value"), result.value) + } + + @Test + fun `given SimplePropertiesResponse with number value when toSimpleProperties then correctly maps value`() { + val input = SimplePropertiesResponse( + key = "numeric_key", + value = JsonPrimitive(123) + ) + val result = input.toSimpleProperties() + assertEquals("numeric_key", result.key) + assertEquals(JsonPrimitive(123), result.value) + } + + @Test + fun `given SimplePropertiesResponse with complex value when toSimpleProperties then preserves complex JsonElement`() { + val complexValue = buildJsonObject { + put("nested", "data") + } + val input = SimplePropertiesResponse( + key = "complex_key", + value = complexValue + ) + val result = input.toSimpleProperties() + assertEquals("complex_key", result.key) + assertEquals(complexValue, result.value) + } + + @Test + fun `given SimplePropertiesResponse with empty string key when toSimpleProperties then maps empty key`() { + val input = SimplePropertiesResponse( + key = "", + value = JsonPrimitive("value") + ) + val result = input.toSimpleProperties() + assertEquals("", result.key) + assertEquals(JsonPrimitive("value"), result.value) + } + + @Test + fun `given JsonElement with deeply nested structure when convertToElement then deserializes correctly`() { + @Serializable + data class Inner(val value: String) + + @Serializable + data class Outer(val inner: Inner) + + val jsonElement = buildJsonObject { + put("inner", buildJsonObject { + put("value", "nested_value") + }) + } + val result: Outer? = jsonElement.convertToElement() + assertNotNull(result) + assertEquals("nested_value", result.inner.value) + } + + @Test + fun `given JsonElement array when convertToElement then deserializes to list`() { + val jsonElement = Json.parseToJsonElement("""["item1", "item2", "item3"]""") + val result: List? = jsonElement.convertToElement() + assertNotNull(result) + assertEquals(3, result.size) + assertEquals("item1", result[0]) + assertEquals("item2", result[1]) + assertEquals("item3", result[2]) + } + + @Test + fun `given wrong type JsonElement when convertToElement then returns null`() { + @Serializable + data class TestData(val id: Int) + + val jsonElement = buildJsonObject { + put("id", "not_an_int") + } + val result: TestData? = jsonElement.convertToElement() + assertNull(result) + } + + @Test + fun `given multiple SimplePropertiesResponse with mixed value types when toListSimpleProperties then preserves all value types`() { + val input = listOf( + SimplePropertiesResponse(key = "string_key", value = JsonPrimitive("string_value")), + SimplePropertiesResponse(key = "int_key", value = JsonPrimitive(42)), + SimplePropertiesResponse(key = "bool_key", value = JsonPrimitive(true)), + SimplePropertiesResponse(key = "double_key", value = JsonPrimitive(3.14)) + ) + val result = input.toListSimpleProperties() + assertEquals(4, result.size) + assertEquals("string_key", result[0].key) + assertEquals("int_key", result[1].key) + assertEquals("bool_key", result[2].key) + assertEquals("double_key", result[3].key) + } +} \ No newline at end of file diff --git a/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/data/ViewMapperVoTest.kt b/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/data/ViewMapperVoTest.kt new file mode 100644 index 0000000..076e878 --- /dev/null +++ b/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/data/ViewMapperVoTest.kt @@ -0,0 +1,304 @@ +package com.github.codandotv.craftd.androidcore.data + +import com.fasterxml.jackson.databind.ObjectMapper +import io.mockk.every +import io.mockk.mockk +import io.mockk.mockkStatic +import io.mockk.unmockkAll +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertNull +import kotlin.test.assertTrue + +@RunWith(JUnit4::class) +class ViewMapperVoTest { + + @Before + fun setup() { + } + + @After + fun tearDown() { + unmockkAll() + } + + @Test + fun `given valid map when convertToVO then returns correct object`() { + val input = mapOf( + "name" to "TestName", + "value" to 42 + ) + + val result: TestData = input.convertToVO() + + assertNotNull(result) + assertEquals("TestName", result.name) + assertEquals(42, result.value) + } + + @Test + fun `given valid json string when convertToVO then returns correct object`() { + val input = """{"name":"TestName","value":42}""" + + val result: TestData = input.convertToVO() + + assertNotNull(result) + assertEquals("TestName", result.name) + assertEquals(42, result.value) + } + + @Test + fun `given null input when convertToVO then throws exception`() { + try { + val result: TestData = (null as Any?).convertToVO() + } catch (e: Exception) { + assertTrue(e is NullPointerException || e is IllegalArgumentException) + } + } + + @Test + fun `given map with missing fields when convertToVO then returns object with defaults`() { + val input = mapOf("name" to "TestName") + + val result: TestData = input.convertToVO() + + assertNotNull(result) + assertEquals("TestName", result.name) + } + + @Test + fun `given map with wrong type when convertToVO then throws or casts`() { + val input = mapOf( + "name" to 123, + "value" to "notanumber" + ) + + try { + val result: TestData = input.convertToVO() + } catch (e: Exception) { + assertTrue(e is ClassCastException || e is IllegalArgumentException) + } + } + + @Test + fun `given nested map when convertToVO then converts nested object`() { + val input = mapOf( + "name" to "TestName", + "value" to 42, + "nested" to mapOf("id" to 1) + ) + + val result: TestDataWithNested = input.convertToVO() + + assertNotNull(result) + assertEquals("TestName", result.name) + } + + @Test + fun `given list of maps when convertToVO then converts to list of objects`() { + val input = listOf( + mapOf("name" to "Test1", "value" to 1), + mapOf("name" to "Test2", "value" to 2) + ) + + val result: List = input.convertToVO() + + assertNotNull(result) + assertEquals(2, result.size) + assertEquals("Test1", result[0].name) + assertEquals("Test2", result[1].name) + } + + @Test + fun `given empty map when convertToVO then returns object with default values`() { + val input = emptyMap() + + val result: TestDataOptional = input.convertToVO() + + assertNotNull(result) + } + + @Test + fun `given map with special characters when convertToVO then converts correctly`() { + val input = mapOf( + "name" to "Test@#\$%Name", + "value" to 42 + ) + + val result: TestData = input.convertToVO() + + assertNotNull(result) + assertEquals("Test@#\$%Name", result.name) + } + + @Test + fun `given map with unicode when convertToVO then converts correctly`() { + val input = mapOf( + "name" to "TestName🎉", + "value" to 42 + ) + + val result: TestData = input.convertToVO() + + assertNotNull(result) + assertEquals("TestName🎉", result.name) + } + + @Test + fun `given boolean value when convertToVO then converts correctly`() { + val input = mapOf( + "name" to "TestName", + "value" to 42, + "active" to true + ) + + val result: TestDataWithBoolean = input.convertToVO() + + assertNotNull(result) + assertEquals(true, result.active) + } + + @Test + fun `given double value when convertToVO then converts correctly`() { + val input = mapOf( + "name" to "TestName", + "value" to 42, + "price" to 19.99 + ) + + val result: TestDataWithDouble = input.convertToVO() + + assertNotNull(result) + assertEquals(19.99, result.price) + } + + @Test + fun `given long value when convertToVO then converts correctly`() { + val input = mapOf( + "name" to "TestName", + "value" to 9223372036854775807L + ) + + val result: TestDataWithLong = input.convertToVO() + + assertNotNull(result) + assertEquals(9223372036854775807L, result.value) + } + + @Test + fun `given multiple nested objects when convertToVO then converts all levels`() { + val input = mapOf( + "name" to "TestName", + "nested" to mapOf( + "id" to 1, + "data" to mapOf( + "key" to "value" + ) + ) + ) + + val result: TestDataDeepNested = input.convertToVO() + + assertNotNull(result) + assertEquals("TestName", result.name) + } + + @Test + fun `given array of values when convertToVO then converts to kotlin list`() { + val input = mapOf( + "name" to "TestName", + "items" to listOf(1, 2, 3, 4, 5) + ) + + val result: TestDataWithList = input.convertToVO() + + assertNotNull(result) + assertEquals(5, result.items.size) + } + + @Test + fun `given number as string when convertToVO then converts if valid`() { + val input = mapOf( + "name" to "TestName", + "value" to "42" + ) + + try { + val result: TestData = input.convertToVO() + assertNotNull(result) + } catch (e: Exception) { + assertTrue(e is ClassCastException || e is IllegalArgumentException) + } + } + + @Test + fun `given object with null fields when convertToVO then preserves nulls`() { + val input = mapOf( + "name" to null, + "value" to 42 + ) + + val result: TestDataNullable = input.convertToVO() + + assertNotNull(result) + assertNull(result.name) + } + + data class TestData( + val name: String = "", + val value: Int = 0 + ) + + data class TestDataOptional( + val name: String? = null, + val value: Int? = null + ) + + data class TestDataNullable( + val name: String? = null, + val value: Int = 0 + ) + + data class TestDataWithBoolean( + val name: String = "", + val value: Int = 0, + val active: Boolean = false + ) + + data class TestDataWithDouble( + val name: String = "", + val value: Int = 0, + val price: Double = 0.0 + ) + + data class TestDataWithLong( + val name: String = "", + val value: Long = 0L + ) + + data class NestedData( + val id: Int = 0, + val data: Map = emptyMap() + ) + + data class TestDataDeepNested( + val name: String = "", + val nested: NestedData? = null + ) + + data class TestDataWithList( + val name: String = "", + val items: List = emptyList() + ) + + data class TestDataWithNested( + val name: String = "", + val value: Int = 0, + val nested: Map = emptyMap() + ) +} \ No newline at end of file diff --git a/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/data/model/action/ActionPropertiesTest.kt b/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/data/model/action/ActionPropertiesTest.kt new file mode 100644 index 0000000..7276913 --- /dev/null +++ b/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/data/model/action/ActionPropertiesTest.kt @@ -0,0 +1,438 @@ +package com.github.codandotv.craftd.androidcore.data.model.action + +import androidx.recyclerview.widget.DiffUtil +import io.mockk.every +import io.mockk.mockk +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonNull +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.JsonPrimitive +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNotEquals +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@RunWith(JUnit4::class) +class ActionPropertiesTest { + + private lateinit var analyticsProperties: AnalyticsProperties + + @Before + fun setUp() { + analyticsProperties = mockk(relaxed = true) + } + + // ============================================================ + // Data Class Construction Tests + // ============================================================ + + @Test + fun `given all parameters when creating ActionProperties then should initialize correctly`() { + val deeplink = "https://example.com" + val actionData = JsonPrimitive("test_data") + val analytics = analyticsProperties + + val actionProperties = ActionProperties( + deeplink = deeplink, + actionData = actionData, + analytics = analytics + ) + + assertEquals(deeplink, actionProperties.deeplink) + assertEquals(actionData, actionProperties.actionData) + assertEquals(analytics, actionProperties.analytics) + } + + @Test + fun `given only deeplink when creating ActionProperties then should initialize with defaults`() { + val deeplink = "https://example.com" + + val actionProperties = ActionProperties(deeplink = deeplink) + + assertEquals(deeplink, actionProperties.deeplink) + assertNull(actionProperties.actionData) + assertNull(actionProperties.analytics) + } + + @Test + fun `given no parameters when creating ActionProperties then should use all defaults`() { + val actionProperties = ActionProperties() + + assertNull(actionProperties.deeplink) + assertNull(actionProperties.actionData) + assertNull(actionProperties.analytics) + } + + @Test + fun `given null deeplink when creating ActionProperties then should accept null`() { + val actionProperties = ActionProperties(deeplink = null) + + assertNull(actionProperties.deeplink) + } + + // ============================================================ + // Data Class Copy Tests + // ============================================================ + + @Test + fun `given ActionProperties when copying with modified deeplink then should create new instance`() { + val original = ActionProperties( + deeplink = "https://example.com", + actionData = JsonPrimitive("data"), + analytics = analyticsProperties + ) + + val copied = original.copy(deeplink = "https://modified.com") + + assertEquals("https://modified.com", copied.deeplink) + assertEquals(original.actionData, copied.actionData) + assertEquals(original.analytics, copied.analytics) + assertNotEquals(original, copied) + } + + @Test + fun `given ActionProperties when copying with modified actionData then should create new instance`() { + val original = ActionProperties( + deeplink = "https://example.com", + actionData = JsonPrimitive("data"), + analytics = analyticsProperties + ) + val newData = JsonPrimitive("modified") + + val copied = original.copy(actionData = newData) + + assertEquals(original.deeplink, copied.deeplink) + assertEquals(newData, copied.actionData) + assertEquals(original.analytics, copied.analytics) + } + + @Test + fun `given ActionProperties when copying with modified analytics then should create new instance`() { + val original = ActionProperties( + deeplink = "https://example.com", + actionData = JsonPrimitive("data"), + analytics = analyticsProperties + ) + val newAnalytics = mockk(relaxed = true) + + val copied = original.copy(analytics = newAnalytics) + + assertEquals(original.deeplink, copied.deeplink) + assertEquals(original.actionData, copied.actionData) + assertEquals(newAnalytics, copied.analytics) + } + + @Test + fun `given ActionProperties when copying all fields then should create independent instance`() { + val original = ActionProperties( + deeplink = "https://example.com", + actionData = JsonPrimitive("data"), + analytics = analyticsProperties + ) + + val copied = original.copy( + deeplink = "https://new.com", + actionData = JsonPrimitive("newdata"), + analytics = mockk(relaxed = true) + ) + + assertNotEquals(original, copied) + } + + // ============================================================ + // Data Class Equality Tests + // ============================================================ + + @Test + fun `given two ActionProperties with same values when comparing then should be equal`() { + val actionProperties1 = ActionProperties( + deeplink = "https://example.com", + actionData = JsonPrimitive("data"), + analytics = analyticsProperties + ) + val actionProperties2 = ActionProperties( + deeplink = "https://example.com", + actionData = JsonPrimitive("data"), + analytics = analyticsProperties + ) + + assertEquals(actionProperties1, actionProperties2) + } + + @Test + fun `given two ActionProperties with different deeplink when comparing then should not be equal`() { + val actionProperties1 = ActionProperties(deeplink = "https://example.com") + val actionProperties2 = ActionProperties(deeplink = "https://different.com") + + assertNotEquals(actionProperties1, actionProperties2) + } + + @Test + fun `given two ActionProperties with different actionData when comparing then should not be equal`() { + val actionProperties1 = ActionProperties(actionData = JsonPrimitive("data1")) + val actionProperties2 = ActionProperties(actionData = JsonPrimitive("data2")) + + assertNotEquals(actionProperties1, actionProperties2) + } + + @Test + fun `given two ActionProperties with different analytics when comparing then should not be equal`() { + val analytics1 = mockk(relaxed = true) + val analytics2 = mockk(relaxed = true) + + val actionProperties1 = ActionProperties(analytics = analytics1) + val actionProperties2 = ActionProperties(analytics = analytics2) + + assertNotEquals(actionProperties1, actionProperties2) + } + + @Test + fun `given ActionProperties when comparing to itself then should be equal`() { + val actionProperties = ActionProperties(deeplink = "https://example.com") + + assertEquals(actionProperties, actionProperties) + } + + @Test + fun `given ActionProperties and null when comparing then should not be equal`() { + val actionProperties = ActionProperties(deeplink = "https://example.com") + + assertNotEquals(actionProperties, null) + } + + // ============================================================ + // Data Class HashCode Tests + // ============================================================ + + @Test + fun `given two equal ActionProperties when hashing then should have same hash`() { + val actionProperties1 = ActionProperties( + deeplink = "https://example.com", + actionData = JsonPrimitive("data") + ) + val actionProperties2 = ActionProperties( + deeplink = "https://example.com", + actionData = JsonPrimitive("data") + ) + + assertEquals(actionProperties1.hashCode(), actionProperties2.hashCode()) + } + + @Test + fun `given two different ActionProperties when hashing then should likely have different hash`() { + val actionProperties1 = ActionProperties(deeplink = "https://example.com") + val actionProperties2 = ActionProperties(deeplink = "https://different.com") + + assertNotEquals(actionProperties1.hashCode(), actionProperties2.hashCode()) + } + + @Test + fun `given ActionProperties with null values when hashing then should produce valid hash`() { + val actionProperties = ActionProperties() + + val hash = actionProperties.hashCode() + + assertTrue(hash != 0 || true) + } + + // ============================================================ + // ActionProperties Immutability Tests + // ============================================================ + + @Test + fun `given ActionProperties when accessing properties multiple times then should return same values`() { + val deeplink = "https://example.com" + val actionData = JsonPrimitive("data") + val actionProperties = ActionProperties( + deeplink = deeplink, + actionData = actionData + ) + + val deeplink1 = actionProperties.deeplink + val deeplink2 = actionProperties.deeplink + val data1 = actionProperties.actionData + val data2 = actionProperties.actionData + + assertEquals(deeplink1, deeplink2) + assertEquals(data1, data2) + } + + // ============================================================ + // Default Values Tests + // ============================================================ + + @Test + fun `given ActionProperties with defaults when checking deeplink then should be null`() { + val actionProperties = ActionProperties() + + assertNull(actionProperties.deeplink) + } + + @Test + fun `given ActionProperties with defaults when checking actionData then should be null`() { + val actionProperties = ActionProperties() + + assertNull(actionProperties.actionData) + } + + @Test + fun `given ActionProperties with defaults when checking analytics then should be null`() { + val actionProperties = ActionProperties() + + assertNull(actionProperties.analytics) + } + + // ============================================================ + // JsonElement Type Handling Tests + // ============================================================ + + @Test + fun `given ActionProperties with JsonPrimitive actionData when accessing then should return correct type`() { + val primitiveData = JsonPrimitive("test") + val actionProperties = ActionProperties(actionData = primitiveData) + + assertEquals(primitiveData, actionProperties.actionData) + } + + @Test + fun `given ActionProperties with JsonObject actionData when accessing then should return correct type`() { + val objectData = JsonObject(mapOf("key" to JsonPrimitive("value"))) + val actionProperties = ActionProperties(actionData = objectData) + + assertEquals(objectData, actionProperties.actionData) + } + + @Test + fun `given ActionProperties with JsonNull actionData when accessing then should return JsonNull`() { + val nullData = JsonNull + val actionProperties = ActionProperties(actionData = nullData) + + assertEquals(nullData, actionProperties.actionData) + } + + // ============================================================ + // Multiple Instantiation Tests + // ============================================================ + + @Test + fun `given multiple ActionProperties instances with same data when comparing then should be equal`() { + val deeplink = "https://example.com" + val actionData = JsonPrimitive("data") + + val instance1 = ActionProperties(deeplink = deeplink, actionData = actionData) + val instance2 = ActionProperties(deeplink = deeplink, actionData = actionData) + val instance3 = ActionProperties(deeplink = deeplink, actionData = actionData) + + assertEquals(instance1, instance2) + assertEquals(instance2, instance3) + assertEquals(instance1, instance3) + } + + @Test + fun `given ActionProperties instances in collection when using distinct then should filter duplicates`() { + val actionProperties1 = ActionProperties(deeplink = "https://example.com") + val actionProperties2 = ActionProperties(deeplink = "https://example.com") + val actionProperties3 = ActionProperties(deeplink = "https://different.com") + + val list = listOf(actionProperties1, actionProperties2, actionProperties3) + val distinct = list.distinct() + + assertEquals(2, distinct.size) + } + + // ============================================================ + // SerialName Annotation Tests + // ============================================================ + + @Test + fun `given ActionProperties when checking serialization names then should have correct SerialNames`() { + val actionProperties = ActionProperties( + deeplink = "https://example.com", + actionData = JsonPrimitive("data") + ) + + assertNotNull(actionProperties) + } + + // ============================================================ + // Constructor Edge Cases + // ============================================================ + + @Test + fun `given empty string deeplink when creating ActionProperties then should accept it`() { + val actionProperties = ActionProperties(deeplink = "") + + assertEquals("", actionProperties.deeplink) + } + + @Test + fun `given very long deeplink when creating ActionProperties then should accept it`() { + val longDeeplink = "https://example.com/" + "a".repeat(1000) + val actionProperties = ActionProperties(deeplink = longDeeplink) + + assertEquals(longDeeplink, actionProperties.deeplink) + } + + @Test + fun `given special characters in deeplink when creating ActionProperties then should accept it`() { + val deeplink = "https://example.com?param=value&other=123" + val actionProperties = ActionProperties(deeplink = deeplink) + + assertEquals(deeplink, actionProperties.deeplink) + } + + // ============================================================ + // Composition Tests + // ============================================================ + + @Test + fun `given ActionProperties with analytics when accessing then should return same instance`() { + val analytics = analyticsProperties + val actionProperties = ActionProperties(analytics = analytics) + + assertSame(analytics, actionProperties.analytics) + } + + @Test + fun `given ActionProperties with actionData when copying and modifying then original should be unchanged`() { + val originalData = JsonPrimitive("original") + val actionProperties = ActionProperties(actionData = originalData) + + val modified = actionProperties.copy(actionData = JsonPrimitive("modified")) + + assertEquals(originalData, actionProperties.actionData) + assertNotEquals(originalData, modified.actionData) + } + + // ============================================================ + // Type Safety Tests + // ============================================================ + + @Test + fun `given ActionProperties with various JsonElement types when storing then should preserve types`() { + val primitive = JsonPrimitive("test") + val obj = JsonObject(mapOf("key" to JsonPrimitive("value"))) + val nullElement = JsonNull + + val props1 = ActionProperties(actionData = primitive) + val props2 = ActionProperties(actionData = obj) + val props3 = ActionProperties(actionData = nullElement) + + assertEquals(primitive, props1.actionData) + assertEquals(obj, props2.actionData) + assertEquals(nullElement, props3.actionData) + } + + private fun assertSame(expected: Any?, actual: Any?) { + assertTrue("Expected same instance", expected === actual) + } + + private fun assertNotNull(value: Any?) { + assertTrue("Expected non-null value", value != null) + } +} \ No newline at end of file diff --git a/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/data/model/action/AnalyticsPropertiesTest.kt b/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/data/model/action/AnalyticsPropertiesTest.kt new file mode 100644 index 0000000..a0b6d87 --- /dev/null +++ b/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/data/model/action/AnalyticsPropertiesTest.kt @@ -0,0 +1,419 @@ +package com.github.codandotv.craftd.androidcore.data.model.action + +import io.mockk.junit4.MockKRule +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import kotlin.test.assertEquals +import kotlin.test.assertNotEquals +import kotlin.test.assertNull + +@RunWith(JUnit4::class) +class AnalyticsPropertiesTest { + + @get:Rule + val mockkRule = MockKRule(this) + + @Test + fun `given all parameters when constructing AnalyticsProperties then all fields are set`() { + val category = "purchase" + val action = "click" + val label = "button_checkout" + val track = "event_001" + + val analyticsProperties = AnalyticsProperties( + category = category, + action = action, + label = label, + track = track + ) + + assertEquals(category, analyticsProperties.category) + assertEquals(action, analyticsProperties.action) + assertEquals(label, analyticsProperties.label) + assertEquals(track, analyticsProperties.track) + } + + @Test + fun `given no parameters when constructing AnalyticsProperties then all fields default to null`() { + val analyticsProperties = AnalyticsProperties() + + assertNull(analyticsProperties.category) + assertNull(analyticsProperties.action) + assertNull(analyticsProperties.label) + assertNull(analyticsProperties.track) + } + + @Test + fun `given partial parameters when constructing AnalyticsProperties then unspecified fields default to null`() { + val analyticsProperties = AnalyticsProperties( + category = "purchase", + action = "click" + ) + + assertEquals("purchase", analyticsProperties.category) + assertEquals("click", analyticsProperties.action) + assertNull(analyticsProperties.label) + assertNull(analyticsProperties.track) + } + + @Test + fun `given AnalyticsProperties with all fields when copying with new category then category is updated`() { + val original = AnalyticsProperties( + category = "purchase", + action = "click", + label = "button_checkout", + track = "event_001" + ) + + val copied = original.copy(category = "view") + + assertEquals("view", copied.category) + assertEquals("click", copied.action) + assertEquals("button_checkout", copied.label) + assertEquals("event_001", copied.track) + } + + @Test + fun `given AnalyticsProperties when copying with multiple fields then all updated fields are changed`() { + val original = AnalyticsProperties( + category = "purchase", + action = "click", + label = "button_checkout", + track = "event_001" + ) + + val copied = original.copy( + category = "view", + action = "scroll", + label = "page_home" + ) + + assertEquals("view", copied.category) + assertEquals("scroll", copied.action) + assertEquals("page_home", copied.label) + assertEquals("event_001", copied.track) + } + + @Test + fun `given two AnalyticsProperties with same values when comparing then they are equal`() { + val properties1 = AnalyticsProperties( + category = "purchase", + action = "click", + label = "button_checkout", + track = "event_001" + ) + val properties2 = AnalyticsProperties( + category = "purchase", + action = "click", + label = "button_checkout", + track = "event_001" + ) + + assertEquals(properties1, properties2) + } + + @Test + fun `given two AnalyticsProperties with different category when comparing then they are not equal`() { + val properties1 = AnalyticsProperties( + category = "purchase", + action = "click" + ) + val properties2 = AnalyticsProperties( + category = "view", + action = "click" + ) + + assertNotEquals(properties1, properties2) + } + + @Test + fun `given two AnalyticsProperties with different action when comparing then they are not equal`() { + val properties1 = AnalyticsProperties( + category = "purchase", + action = "click" + ) + val properties2 = AnalyticsProperties( + category = "purchase", + action = "scroll" + ) + + assertNotEquals(properties1, properties2) + } + + @Test + fun `given two AnalyticsProperties with different label when comparing then they are not equal`() { + val properties1 = AnalyticsProperties( + category = "purchase", + action = "click", + label = "button_checkout" + ) + val properties2 = AnalyticsProperties( + category = "purchase", + action = "click", + label = "button_home" + ) + + assertNotEquals(properties1, properties2) + } + + @Test + fun `given two AnalyticsProperties with different track when comparing then they are not equal`() { + val properties1 = AnalyticsProperties( + category = "purchase", + action = "click", + track = "event_001" + ) + val properties2 = AnalyticsProperties( + category = "purchase", + action = "click", + track = "event_002" + ) + + assertNotEquals(properties1, properties2) + } + + @Test + fun `given two AnalyticsProperties with null fields when comparing then they are equal`() { + val properties1 = AnalyticsProperties() + val properties2 = AnalyticsProperties() + + assertEquals(properties1, properties2) + } + + @Test + fun `given two AnalyticsProperties with same values when computing hashCode then hashCodes are equal`() { + val properties1 = AnalyticsProperties( + category = "purchase", + action = "click", + label = "button_checkout", + track = "event_001" + ) + val properties2 = AnalyticsProperties( + category = "purchase", + action = "click", + label = "button_checkout", + track = "event_001" + ) + + assertEquals(properties1.hashCode(), properties2.hashCode()) + } + + @Test + fun `given two AnalyticsProperties with different values when computing hashCode then hashCodes differ`() { + val properties1 = AnalyticsProperties( + category = "purchase", + action = "click" + ) + val properties2 = AnalyticsProperties( + category = "view", + action = "scroll" + ) + + assertNotEquals(properties1.hashCode(), properties2.hashCode()) + } + + @Test + fun `given AnalyticsProperties when calling toString then string representation includes all fields`() { + val properties = AnalyticsProperties( + category = "purchase", + action = "click", + label = "button_checkout", + track = "event_001" + ) + + val stringRepresentation = properties.toString() + + assert(stringRepresentation.contains("category")) + assert(stringRepresentation.contains("purchase")) + assert(stringRepresentation.contains("action")) + assert(stringRepresentation.contains("click")) + assert(stringRepresentation.contains("label")) + assert(stringRepresentation.contains("button_checkout")) + assert(stringRepresentation.contains("track")) + assert(stringRepresentation.contains("event_001")) + } + + @Test + fun `given AnalyticsProperties with all null fields when calling toString then null values are represented`() { + val properties = AnalyticsProperties() + + val stringRepresentation = properties.toString() + + assert(stringRepresentation.contains("category")) + assert(stringRepresentation.contains("action")) + assert(stringRepresentation.contains("label")) + assert(stringRepresentation.contains("track")) + } + + @Test + fun `given AnalyticsProperties with mixed null fields when comparing then comparison is accurate`() { + val properties1 = AnalyticsProperties( + category = "purchase", + action = null, + label = "button", + track = null + ) + val properties2 = AnalyticsProperties( + category = "purchase", + action = null, + label = "button", + track = null + ) + + assertEquals(properties1, properties2) + } + + @Test + fun `given AnalyticsProperties when copying with null values then null values are preserved`() { + val original = AnalyticsProperties( + category = "purchase", + action = "click", + label = null, + track = "event_001" + ) + + val copied = original.copy(label = null) + + assertEquals("purchase", copied.category) + assertEquals("click", copied.action) + assertNull(copied.label) + assertEquals("event_001", copied.track) + } + + @Test + fun `given AnalyticsProperties when copying with null replacing non-null value then field becomes null`() { + val original = AnalyticsProperties( + category = "purchase", + action = "click", + label = "button_checkout", + track = "event_001" + ) + + val copied = original.copy(action = null, label = null) + + assertEquals("purchase", copied.category) + assertNull(copied.action) + assertNull(copied.label) + assertEquals("event_001", copied.track) + } + + @Test + fun `given empty string values when constructing AnalyticsProperties then empty strings are preserved`() { + val properties = AnalyticsProperties( + category = "", + action = "", + label = "", + track = "" + ) + + assertEquals("", properties.category) + assertEquals("", properties.action) + assertEquals("", properties.label) + assertEquals("", properties.track) + } + + @Test + fun `given AnalyticsProperties with empty strings and null values when comparing then they are not equal`() { + val properties1 = AnalyticsProperties(category = "") + val properties2 = AnalyticsProperties(category = null) + + assertNotEquals(properties1, properties2) + } + + @Test + fun `given AnalyticsProperties with special characters when constructing then special characters are preserved`() { + val specialCategory = "purchase_v2.0" + val specialAction = "click-submit" + val specialLabel = "btn@checkout" + val specialTrack = "event#001" + + val properties = AnalyticsProperties( + category = specialCategory, + action = specialAction, + label = specialLabel, + track = specialTrack + ) + + assertEquals(specialCategory, properties.category) + assertEquals(specialAction, properties.action) + assertEquals(specialLabel, properties.label) + assertEquals(specialTrack, properties.track) + } + + @Test + fun `given AnalyticsProperties with unicode characters when constructing then unicode characters are preserved`() { + val unicodeCategory = "购买" + val unicodeAction = "点击" + val unicodeLabel = "按钮" + val unicodeTrack = "事件" + + val properties = AnalyticsProperties( + category = unicodeCategory, + action = unicodeAction, + label = unicodeLabel, + track = unicodeTrack + ) + + assertEquals(unicodeCategory, properties.category) + assertEquals(unicodeAction, properties.action) + assertEquals(unicodeLabel, properties.label) + assertEquals(unicodeTrack, properties.track) + } + + @Test + fun `given AnalyticsProperties with long strings when constructing then long strings are preserved`() { + val longString = "a".repeat(1000) + + val properties = AnalyticsProperties( + category = longString, + action = longString, + label = longString, + track = longString + ) + + assertEquals(longString, properties.category) + assertEquals(longString, properties.action) + assertEquals(longString, properties.label) + assertEquals(longString, properties.track) + } + + @Test + fun `given AnalyticsProperties when copying without parameters then original is unchanged`() { + val original = AnalyticsProperties( + category = "purchase", + action = "click", + label = "button_checkout", + track = "event_001" + ) + + val copied = original.copy() + + assertEquals(original, copied) + assertEquals(original.category, copied.category) + assertEquals(original.action, copied.action) + assertEquals(original.label, copied.label) + assertEquals(original.track, copied.track) + } + + @Test + fun `given AnalyticsProperties instance when checking immutability then state cannot be modified`() { + val properties = AnalyticsProperties( + category = "purchase", + action = "click", + label = "button_checkout", + track = "event_001" + ) + + val category = properties.category + val action = properties.action + val label = properties.label + val track = properties.track + + assertEquals("purchase", category) + assertEquals("click", action) + assertEquals("button_checkout", label) + assertEquals("event_001", track) + } +} \ No newline at end of file diff --git a/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/data/model/base/SimplePropertiesResponseTest.kt b/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/data/model/base/SimplePropertiesResponseTest.kt new file mode 100644 index 0000000..f9b05a0 --- /dev/null +++ b/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/data/model/base/SimplePropertiesResponseTest.kt @@ -0,0 +1,306 @@ +package com.github.codandotv.craftd.androidcore.data.model.base + +import io.mockk.junit4.MockKRule +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonNull +import kotlinx.serialization.json.JsonPrimitive +import kotlinx.serialization.json.buildJsonObject +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNotEquals +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@RunWith(JUnit4::class) +class SimplePropertiesResponseTest { + + @get:Rule + val mockkRule = MockKRule(this) + + @Test + fun `given default parameters when creating SimplePropertiesResponse then key is empty and value is null`() { + val response = SimplePropertiesResponse() + assertEquals("", response.key) + assertNull(response.value) + } + + @Test + fun `given key and value when creating SimplePropertiesResponse then properties are set correctly`() { + val key = "testKey" + val value = JsonPrimitive("testValue") + val response = SimplePropertiesResponse(key = key, value = value) + assertEquals(key, response.key) + assertEquals(value, response.value) + } + + @Test + fun `given all parameters when creating SimplePropertiesResponse then copy works correctly`() { + val original = SimplePropertiesResponse(key = "original", value = JsonPrimitive("value")) + val copied = original.copy(key = "copied") + assertEquals("copied", copied.key) + assertEquals(original.value, copied.value) + assertNotEquals(original.key, copied.key) + } + + @Test + fun `given same key and value when comparing SimplePropertiesResponse then equals returns true`() { + val response1 = SimplePropertiesResponse(key = "key", value = JsonPrimitive("value")) + val response2 = SimplePropertiesResponse(key = "key", value = JsonPrimitive("value")) + assertEquals(response1, response2) + } + + @Test + fun `given different key when comparing SimplePropertiesResponse then equals returns false`() { + val response1 = SimplePropertiesResponse(key = "key1", value = JsonPrimitive("value")) + val response2 = SimplePropertiesResponse(key = "key2", value = JsonPrimitive("value")) + assertNotEquals(response1, response2) + } + + @Test + fun `given different value when comparing SimplePropertiesResponse then equals returns false`() { + val response1 = SimplePropertiesResponse(key = "key", value = JsonPrimitive("value1")) + val response2 = SimplePropertiesResponse(key = "key", value = JsonPrimitive("value2")) + assertNotEquals(response1, response2) + } + + @Test + fun `given same key and value when comparing hashCode then they are equal`() { + val response1 = SimplePropertiesResponse(key = "key", value = JsonPrimitive("value")) + val response2 = SimplePropertiesResponse(key = "key", value = JsonPrimitive("value")) + assertEquals(response1.hashCode(), response2.hashCode()) + } + + @Test + fun `given different key when comparing hashCode then they are different`() { + val response1 = SimplePropertiesResponse(key = "key1", value = JsonPrimitive("value")) + val response2 = SimplePropertiesResponse(key = "key2", value = JsonPrimitive("value")) + assertNotEquals(response1.hashCode(), response2.hashCode()) + } + + @Test + fun `given null value when creating SimplePropertiesResponse then value is null`() { + val response = SimplePropertiesResponse(key = "key", value = null) + assertNull(response.value) + } + + @Test + fun `given JsonNull value when creating SimplePropertiesResponse then value is JsonNull`() { + val response = SimplePropertiesResponse(key = "key", value = JsonNull) + assertEquals(JsonNull, response.value) + } + + @Test + fun `given default parameters when creating DataSimplePropertiesResponse then data is initialized`() { + val data = listOf(SimplePropertiesResponse(key = "key1", value = JsonPrimitive("value1"))) + val response = DataSimplePropertiesResponse(data = data) + assertEquals(1, response.data.size) + assertEquals("key1", response.data[0].key) + } + + @Test + fun `given multiple items when creating DataSimplePropertiesResponse then all items are present`() { + val item1 = SimplePropertiesResponse(key = "key1", value = JsonPrimitive("value1")) + val item2 = SimplePropertiesResponse(key = "key2", value = JsonPrimitive("value2")) + val data = listOf(item1, item2) + val response = DataSimplePropertiesResponse(data = data) + assertEquals(2, response.data.size) + assertEquals(item1, response.data[0]) + assertEquals(item2, response.data[1]) + } + + @Test + fun `given empty list when creating DataSimplePropertiesResponse then data is empty`() { + val response = DataSimplePropertiesResponse(data = emptyList()) + assertEquals(0, response.data.size) + } + + @Test + fun `given same data when comparing DataSimplePropertiesResponse then equals returns true`() { + val data = listOf(SimplePropertiesResponse(key = "key1", value = JsonPrimitive("value1"))) + val response1 = DataSimplePropertiesResponse(data = data) + val response2 = DataSimplePropertiesResponse(data = data) + assertEquals(response1, response2) + } + + @Test + fun `given different data when comparing DataSimplePropertiesResponse then equals returns false`() { + val data1 = listOf(SimplePropertiesResponse(key = "key1", value = JsonPrimitive("value1"))) + val data2 = listOf(SimplePropertiesResponse(key = "key2", value = JsonPrimitive("value2"))) + val response1 = DataSimplePropertiesResponse(data = data1) + val response2 = DataSimplePropertiesResponse(data = data2) + assertNotEquals(response1, response2) + } + + @Test + fun `given same data when comparing DataSimplePropertiesResponse hashCode then they are equal`() { + val data = listOf(SimplePropertiesResponse(key = "key1", value = JsonPrimitive("value1"))) + val response1 = DataSimplePropertiesResponse(data = data) + val response2 = DataSimplePropertiesResponse(data = data) + assertEquals(response1.hashCode(), response2.hashCode()) + } + + @Test + fun `given JsonPrimitive string when creating SimplePropertiesResponse then value is correctly set`() { + val stringValue = JsonPrimitive("stringValue") + val response = SimplePropertiesResponse(key = "key", value = stringValue) + assertEquals(stringValue, response.value) + } + + @Test + fun `given JsonPrimitive number when creating SimplePropertiesResponse then value is correctly set`() { + val numberValue = JsonPrimitive(42) + val response = SimplePropertiesResponse(key = "key", value = numberValue) + assertEquals(numberValue, response.value) + } + + @Test + fun `given JsonPrimitive boolean when creating SimplePropertiesResponse then value is correctly set`() { + val booleanValue = JsonPrimitive(true) + val response = SimplePropertiesResponse(key = "key", value = booleanValue) + assertEquals(booleanValue, response.value) + } + + @Test + fun `given complex JsonObject when creating SimplePropertiesResponse then value is correctly set`() { + val complexValue = buildJsonObject { + put("nested", JsonPrimitive("value")) + } + val response = SimplePropertiesResponse(key = "key", value = complexValue) + assertEquals(complexValue, response.value) + } + + @Test + fun `given copy with only value when SimplePropertiesResponse then key remains unchanged`() { + val original = SimplePropertiesResponse(key = "originalKey", value = JsonPrimitive("originalValue")) + val newValue = JsonPrimitive("newValue") + val copied = original.copy(value = newValue) + assertEquals("originalKey", copied.key) + assertEquals(newValue, copied.value) + } + + @Test + fun `given copy with all parameters when SimplePropertiesResponse then all are updated`() { + val original = SimplePropertiesResponse(key = "key1", value = JsonPrimitive("value1")) + val newKey = "key2" + val newValue = JsonPrimitive("value2") + val copied = original.copy(key = newKey, value = newValue) + assertEquals(newKey, copied.key) + assertEquals(newValue, copied.value) + } + + @Test + fun `given DataSimplePropertiesResponse when calling copy then data is updated`() { + val originalData = listOf(SimplePropertiesResponse(key = "key1", value = JsonPrimitive("value1"))) + val original = DataSimplePropertiesResponse(data = originalData) + val newData = listOf(SimplePropertiesResponse(key = "key2", value = JsonPrimitive("value2"))) + val copied = original.copy(data = newData) + assertEquals(newData, copied.data) + assertNotEquals(originalData, copied.data) + } + + @Test + fun `given null key when creating SimplePropertiesResponse then default empty string is used`() { + val response = SimplePropertiesResponse(key = "", value = JsonPrimitive("value")) + assertEquals("", response.key) + } + + @Test + fun `given very long key when creating SimplePropertiesResponse then key is stored as is`() { + val longKey = "a".repeat(1000) + val response = SimplePropertiesResponse(key = longKey, value = JsonPrimitive("value")) + assertEquals(longKey, response.key) + } + + @Test + fun `given special characters in key when creating SimplePropertiesResponse then key is stored as is`() { + val specialKey = "key@#$%^&*()" + val response = SimplePropertiesResponse(key = specialKey, value = JsonPrimitive("value")) + assertEquals(specialKey, response.key) + } + + @Test + fun `given unicode characters in key when creating SimplePropertiesResponse then key is stored correctly`() { + val unicodeKey = "key_🚀_测试" + val response = SimplePropertiesResponse(key = unicodeKey, value = JsonPrimitive("value")) + assertEquals(unicodeKey, response.key) + } + + @Test + fun `given response with null value when comparing then null value is handled correctly`() { + val response1 = SimplePropertiesResponse(key = "key", value = null) + val response2 = SimplePropertiesResponse(key = "key", value = null) + assertEquals(response1, response2) + } + + @Test + fun `given response with null and non-null value when comparing then they are not equal`() { + val response1 = SimplePropertiesResponse(key = "key", value = null) + val response2 = SimplePropertiesResponse(key = "key", value = JsonPrimitive("value")) + assertNotEquals(response1, response2) + } + + @Test + fun `given DataSimplePropertiesResponse with mixed null values when accessing data then items are retrieved correctly`() { + val item1 = SimplePropertiesResponse(key = "key1", value = null) + val item2 = SimplePropertiesResponse(key = "key2", value = JsonPrimitive("value")) + val data = listOf(item1, item2) + val response = DataSimplePropertiesResponse(data = data) + assertNull(response.data[0].value) + assertEquals(JsonPrimitive("value"), response.data[1].value) + } + + @Test + fun `given large list in DataSimplePropertiesResponse then all items are retained`() { + val items = (1..1000).map { i -> + SimplePropertiesResponse(key = "key$i", value = JsonPrimitive(i)) + } + val response = DataSimplePropertiesResponse(data = items) + assertEquals(1000, response.data.size) + assertEquals("key1", response.data[0].key) + assertEquals("key1000", response.data[999].key) + } + + @Test + fun `given SerialName annotations when inspecting SimplePropertiesResponse then annotations are present`() { + val response = SimplePropertiesResponse(key = "key", value = JsonPrimitive("value")) + assertEquals("key", response.key) + assertEquals(JsonPrimitive("value"), response.value) + } + + @Test + fun `given Serializable annotation when inspecting SimplePropertiesResponse then it is marked serializable`() { + val response = SimplePropertiesResponse() + assertTrue(response::class.annotations.any { it.annotationClass.simpleName == "Serializable" }) + } + + @Test + fun `given multiple SimplePropertiesResponse in list when verifying each then all are independent`() { + val response1 = SimplePropertiesResponse(key = "key1", value = JsonPrimitive("value1")) + val response2 = SimplePropertiesResponse(key = "key2", value = JsonPrimitive("value2")) + val response3 = SimplePropertiesResponse(key = "key1", value = JsonPrimitive("value3")) + assertTrue(response1 != response2) + assertTrue(response1 != response3) + assertFalse(response1 == response3) + } + + @Test + fun `given DataSimplePropertiesResponse copy with same data then creates new instance`() { + val data = listOf(SimplePropertiesResponse(key = "key", value = JsonPrimitive("value"))) + val original = DataSimplePropertiesResponse(data = data) + val copied = original.copy() + assertEquals(original, copied) + assertTrue(original === original) + } + + @Test + fun `given SimplePropertiesResponse with empty string key then equals and hashCode work correctly`() { + val response1 = SimplePropertiesResponse(key = "", value = JsonPrimitive("value")) + val response2 = SimplePropertiesResponse(key = "", value = JsonPrimitive("value")) + assertEquals(response1, response2) + assertEquals(response1.hashCode(), response2.hashCode()) + } +} \ No newline at end of file diff --git a/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/data/model/base/SimplePropertiesTest.kt b/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/data/model/base/SimplePropertiesTest.kt new file mode 100644 index 0000000..ebbbfbe --- /dev/null +++ b/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/data/model/base/SimplePropertiesTest.kt @@ -0,0 +1,303 @@ +package com.github.codandotv.craftd.androidcore.data.model.base + +import io.mockk.junit4.MockKRule +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonPrimitive +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.jsonPrimitive +import kotlinx.serialization.json.intOrNull +import kotlinx.serialization.json.doubleOrNull +import kotlinx.serialization.json.booleanOrNull +import kotlin.test.assertEquals +import kotlin.test.assertNull +import kotlin.test.assertNotEquals +import kotlin.test.assertTrue +import kotlin.test.assertFalse + +@RunWith(JUnit4::class) +class SimplePropertiesTest { + + @get:Rule + val mockkRule = MockKRule(this) + + @Test + fun `given all parameters when creating SimpleProperties then object is created correctly`() { + val key = "testKey" + val value: JsonElement = JsonPrimitive("testValue") + + val simpleProperties = SimpleProperties(key = key, value = value) + + assertEquals(key, simpleProperties.key) + assertEquals(value, simpleProperties.value) + } + + @Test + fun `given only key when creating SimpleProperties then value defaults to null`() { + val key = "testKey" + + val simpleProperties = SimpleProperties(key = key) + + assertEquals(key, simpleProperties.key) + assertNull(simpleProperties.value) + } + + @Test + fun `given no parameters when creating SimpleProperties then defaults are applied`() { + val simpleProperties = SimpleProperties() + + assertEquals("", simpleProperties.key) + assertNull(simpleProperties.value) + } + + @Test + fun `given SimpleProperties when calling copy with new key then new instance with updated key is returned`() { + val original = SimpleProperties(key = "originalKey", value = JsonPrimitive("value")) + val newKey = "newKey" + + val copied = original.copy(key = newKey) + + assertEquals(newKey, copied.key) + assertEquals(original.value, copied.value) + assertNotEquals(original, copied) + } + + @Test + fun `given SimpleProperties when calling copy with new value then new instance with updated value is returned`() { + val original = SimpleProperties(key = "key", value = JsonPrimitive("originalValue")) + val newValue: JsonElement = JsonPrimitive("newValue") + + val copied = original.copy(value = newValue) + + assertEquals(original.key, copied.key) + assertEquals(newValue, copied.value) + assertNotEquals(original, copied) + } + + @Test + fun `given SimpleProperties when calling copy with all new values then new instance is returned`() { + val original = SimpleProperties(key = "key", value = JsonPrimitive("value")) + val newKey = "newKey" + val newValue: JsonElement = JsonPrimitive("newValue") + + val copied = original.copy(key = newKey, value = newValue) + + assertEquals(newKey, copied.key) + assertEquals(newValue, copied.value) + assertNotEquals(original, copied) + } + + @Test + fun `given identical SimpleProperties when comparing with equals then returns true`() { + val prop1 = SimpleProperties(key = "key", value = JsonPrimitive("value")) + val prop2 = SimpleProperties(key = "key", value = JsonPrimitive("value")) + + assertEquals(prop1, prop2) + } + + @Test + fun `given SimpleProperties with different key when comparing with equals then returns false`() { + val prop1 = SimpleProperties(key = "key1", value = JsonPrimitive("value")) + val prop2 = SimpleProperties(key = "key2", value = JsonPrimitive("value")) + + assertNotEquals(prop1, prop2) + } + + @Test + fun `given SimpleProperties with different value when comparing with equals then returns false`() { + val prop1 = SimpleProperties(key = "key", value = JsonPrimitive("value1")) + val prop2 = SimpleProperties(key = "key", value = JsonPrimitive("value2")) + + assertNotEquals(prop1, prop2) + } + + @Test + fun `given SimpleProperties with null value when comparing with equals then considers null equal`() { + val prop1 = SimpleProperties(key = "key", value = null) + val prop2 = SimpleProperties(key = "key", value = null) + + assertEquals(prop1, prop2) + } + + @Test + fun `given identical SimpleProperties when comparing hashCode then returns same hash`() { + val prop1 = SimpleProperties(key = "key", value = JsonPrimitive("value")) + val prop2 = SimpleProperties(key = "key", value = JsonPrimitive("value")) + + assertEquals(prop1.hashCode(), prop2.hashCode()) + } + + @Test + fun `given different SimpleProperties when comparing hashCode then likely returns different hash`() { + val prop1 = SimpleProperties(key = "key1", value = JsonPrimitive("value1")) + val prop2 = SimpleProperties(key = "key2", value = JsonPrimitive("value2")) + + assertNotEquals(prop1.hashCode(), prop2.hashCode()) + } + + @Test + fun `given SimpleProperties when converting to string then contains key and value information`() { + val prop = SimpleProperties(key = "testKey", value = JsonPrimitive("testValue")) + + val stringRepresentation = prop.toString() + + assertTrue(stringRepresentation.contains("key") || stringRepresentation.contains("testKey")) + } + + @Test + fun `given SimpleProperties with null key when creating then empty string is used as default`() { + val prop = SimpleProperties() + + assertEquals("", prop.key) + } + + @Test + fun `given SimpleProperties with JsonObject when creating then value is stored correctly`() { + val jsonObject = JsonObject(mapOf("nested" to JsonPrimitive("value"))) + val prop = SimpleProperties(key = "key", value = jsonObject) + + assertEquals(jsonObject, prop.value) + } + + @Test + fun `given SimpleProperties with numeric JsonPrimitive when creating then value is stored correctly`() { + val jsonNumber = JsonPrimitive(42) + val prop = SimpleProperties(key = "key", value = jsonNumber) + + assertEquals(jsonNumber, prop.value) + } + + @Test + fun `given SimpleProperties with boolean JsonPrimitive when creating then value is stored correctly`() { + val jsonBoolean = JsonPrimitive(true) + val prop = SimpleProperties(key = "key", value = jsonBoolean) + + assertEquals(jsonBoolean, prop.value) + } + + @Test + fun `given SimpleProperties when accessing immutable annotation then type is maintained`() { + val prop = SimpleProperties(key = "key", value = JsonPrimitive("value")) + + val prop2 = prop.copy(key = "newKey") + assertNotEquals(prop, prop2) + } + + @Test + fun `given SimpleProperties when accessing stable annotation then reference equality differs but value equality holds`() { + val value: JsonElement = JsonPrimitive("value") + val prop1 = SimpleProperties(key = "key", value = value) + val prop2 = SimpleProperties(key = "key", value = value) + + assertEquals(prop1, prop2) + assertNotEquals(prop1 as Any, prop2 as Any) + } + + @Test + fun `given two SimpleProperties with same content when calling equals then returns true`() { + val prop1 = SimpleProperties(key = "sameKey", value = JsonPrimitive("sameValue")) + val prop2 = SimpleProperties(key = "sameKey", value = JsonPrimitive("sameValue")) + + assertTrue(prop1 == prop2) + } + + @Test + fun `given SimpleProperties with default values when creating then both defaults are applied`() { + val prop = SimpleProperties() + + assertEquals("", prop.key) + assertNull(prop.value) + } + + @Test + fun `given SimpleProperties with empty string key when creating then key is preserved as empty`() { + val prop = SimpleProperties(key = "") + + assertEquals("", prop.key) + } + + @Test + fun `given SimpleProperties with whitespace key when creating then whitespace is preserved`() { + val prop = SimpleProperties(key = " ") + + assertEquals(" ", prop.key) + } + + @Test + fun `given SimpleProperties with special characters in key when creating then they are preserved`() { + val specialKey = "key@#$%^&*()" + val prop = SimpleProperties(key = specialKey) + + assertEquals(specialKey, prop.key) + } + + @Test + fun `given SimpleProperties when calling copy without arguments then returns equal instance`() { + val original = SimpleProperties(key = "key", value = JsonPrimitive("value")) + + val copied = original.copy() + + assertEquals(original, copied) + assertNotEquals(original as Any, copied as Any) + } + + @Test + fun `given SimpleProperties with null value when accessing value then null is returned`() { + val prop = SimpleProperties(key = "key", value = null) + + assertNull(prop.value) + } + + @Test + fun `given SimpleProperties with multiple different types of values when storing then all types are preserved`() { + val stringValue = SimpleProperties(key = "string", value = JsonPrimitive("text")) + val numberValue = SimpleProperties(key = "number", value = JsonPrimitive(123)) + val booleanValue = SimpleProperties(key = "boolean", value = JsonPrimitive(true)) + + assertEquals(JsonPrimitive("text"), stringValue.value) + assertEquals(JsonPrimitive(123), numberValue.value) + assertEquals(JsonPrimitive(true), booleanValue.value) + } + + @Test + fun `given SimpleProperties when destructuring then components are in correct order`() { + val prop = SimpleProperties(key = "testKey", value = JsonPrimitive("testValue")) + + val (key, value) = prop + + assertEquals("testKey", key) + assertEquals(JsonPrimitive("testValue"), value) + } + + @Test + fun `given SimpleProperties with same key but different case when comparing then not equal`() { + val prop1 = SimpleProperties(key = "Key", value = JsonPrimitive("value")) + val prop2 = SimpleProperties(key = "key", value = JsonPrimitive("value")) + + assertNotEquals(prop1, prop2) + } + + @Test + fun `given SimpleProperties when copy is called multiple times then creates independent instances`() { + val original = SimpleProperties(key = "key", value = JsonPrimitive("value")) + val copy1 = original.copy(key = "copy1") + val copy2 = original.copy(key = "copy2") + + assertNotEquals(copy1, copy2) + assertEquals("copy1", copy1.key) + assertEquals("copy2", copy2.key) + } + + @Test + fun `given SimpleProperties with JsonObject containing nested objects when storing then complex structure is preserved`() { + val nestedObject = JsonObject(mapOf( + "level1" to JsonObject(mapOf("level2" to JsonPrimitive("value"))) + )) + val prop = SimpleProperties(key = "complex", value = nestedObject) + + assertEquals(nestedObject, prop.value) + } +} \ No newline at end of file diff --git a/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/data/model/button/ButtonPropertiesTest.kt b/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/data/model/button/ButtonPropertiesTest.kt new file mode 100644 index 0000000..9063a99 --- /dev/null +++ b/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/data/model/button/ButtonPropertiesTest.kt @@ -0,0 +1,460 @@ +package com.github.codandotv.craftd.androidcore.data.model.button + +import io.mockk.mockk +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import com.github.codandotv.craftd.androidcore.data.model.action.ActionProperties +import com.github.codandotv.craftd.androidcore.domain.CraftDAlign +import kotlin.test.assertEquals +import kotlin.test.assertNotEquals +import kotlin.test.assertTrue +import kotlin.test.assertFalse + +@RunWith(JUnit4::class) +class ButtonPropertiesTest { + + @Test + fun `given all parameters when constructing ButtonProperties then creates instance with all fields`() { + val actionProperties = mockk() + val button = ButtonProperties( + text = "Click me", + textColorHex = "#FFFFFF", + align = CraftDAlign.CENTER, + textAlign = CraftDAlign.CENTER, + textSize = "16sp", + textAllCaps = true, + fillMaxSize = true, + backgroundHex = "#000000", + actionProperties = actionProperties + ) + + assertEquals("Click me", button.text) + assertEquals("#FFFFFF", button.textColorHex) + assertEquals(CraftDAlign.CENTER, button.align) + assertEquals(CraftDAlign.CENTER, button.textAlign) + assertEquals("16sp", button.textSize) + assertEquals(true, button.textAllCaps) + assertEquals(true, button.fillMaxSize) + assertEquals("#000000", button.backgroundHex) + assertEquals(actionProperties, button.actionProperties) + } + + @Test + fun `given no parameters when constructing ButtonProperties then creates instance with default values`() { + val button = ButtonProperties() + + assertEquals(null, button.text) + assertEquals(null, button.textColorHex) + assertEquals(null, button.align) + assertEquals(null, button.textAlign) + assertEquals(null, button.textSize) + assertEquals(false, button.textAllCaps) + assertEquals(false, button.fillMaxSize) + assertEquals(null, button.backgroundHex) + assertEquals(null, button.actionProperties) + } + + @Test + fun `given partial parameters when constructing ButtonProperties then creates instance with mixed values`() { + val button = ButtonProperties( + text = "Submit", + textColorHex = "#FF0000", + textSize = "18sp" + ) + + assertEquals("Submit", button.text) + assertEquals("#FF0000", button.textColorHex) + assertEquals("18sp", button.textSize) + assertEquals(null, button.align) + assertEquals(null, button.textAlign) + assertEquals(false, button.textAllCaps) + assertEquals(false, button.fillMaxSize) + assertEquals(null, button.backgroundHex) + assertEquals(null, button.actionProperties) + } + + @Test + fun `given ButtonProperties instance when calling copy with new text then returns new instance with updated text`() { + val original = ButtonProperties(text = "Original") + val copied = original.copy(text = "Updated") + + assertEquals("Updated", copied.text) + assertEquals("Original", original.text) + } + + @Test + fun `given ButtonProperties instance when calling copy with new color then returns new instance with updated color`() { + val original = ButtonProperties(textColorHex = "#000000") + val copied = original.copy(textColorHex = "#FFFFFF") + + assertEquals("#FFFFFF", copied.textColorHex) + assertEquals("#000000", original.textColorHex) + } + + @Test + fun `given ButtonProperties instance when calling copy with multiple fields then returns new instance with all updates`() { + val actionProperties = mockk() + val original = ButtonProperties( + text = "Original", + textColorHex = "#000000", + align = CraftDAlign.START + ) + val copied = original.copy( + text = "Updated", + textColorHex = "#FFFFFF", + align = CraftDAlign.CENTER, + actionProperties = actionProperties + ) + + assertEquals("Updated", copied.text) + assertEquals("#FFFFFF", copied.textColorHex) + assertEquals(CraftDAlign.CENTER, copied.align) + assertEquals(actionProperties, copied.actionProperties) + + assertEquals("Original", original.text) + assertEquals("#000000", original.textColorHex) + assertEquals(CraftDAlign.START, original.align) + assertEquals(null, original.actionProperties) + } + + @Test + fun `given two identical ButtonProperties when comparing with equals then returns true`() { + val button1 = ButtonProperties( + text = "Click", + textColorHex = "#FFFFFF", + align = CraftDAlign.CENTER + ) + val button2 = ButtonProperties( + text = "Click", + textColorHex = "#FFFFFF", + align = CraftDAlign.CENTER + ) + + assertEquals(button1, button2) + assertTrue(button1 == button2) + } + + @Test + fun `given two ButtonProperties with different text when comparing with equals then returns false`() { + val button1 = ButtonProperties(text = "Click") + val button2 = ButtonProperties(text = "Tap") + + assertNotEquals(button1, button2) + assertFalse(button1 == button2) + } + + @Test + fun `given two ButtonProperties with different colors when comparing with equals then returns false`() { + val button1 = ButtonProperties(textColorHex = "#FFFFFF") + val button2 = ButtonProperties(textColorHex = "#000000") + + assertNotEquals(button1, button2) + } + + @Test + fun `given two ButtonProperties with different align when comparing with equals then returns false`() { + val button1 = ButtonProperties(align = CraftDAlign.CENTER) + val button2 = ButtonProperties(align = CraftDAlign.START) + + assertNotEquals(button1, button2) + } + + @Test + fun `given two ButtonProperties with different textAlign when comparing with equals then returns false`() { + val button1 = ButtonProperties(textAlign = CraftDAlign.CENTER) + val button2 = ButtonProperties(textAlign = CraftDAlign.END) + + assertNotEquals(button1, button2) + } + + @Test + fun `given two ButtonProperties with different textSize when comparing with equals then returns false`() { + val button1 = ButtonProperties(textSize = "16sp") + val button2 = ButtonProperties(textSize = "18sp") + + assertNotEquals(button1, button2) + } + + @Test + fun `given two ButtonProperties with different textAllCaps when comparing with equals then returns false`() { + val button1 = ButtonProperties(textAllCaps = true) + val button2 = ButtonProperties(textAllCaps = false) + + assertNotEquals(button1, button2) + } + + @Test + fun `given two ButtonProperties with different fillMaxSize when comparing with equals then returns false`() { + val button1 = ButtonProperties(fillMaxSize = true) + val button2 = ButtonProperties(fillMaxSize = false) + + assertNotEquals(button1, button2) + } + + @Test + fun `given two ButtonProperties with different backgroundHex when comparing with equals then returns false`() { + val button1 = ButtonProperties(backgroundHex = "#FFFFFF") + val button2 = ButtonProperties(backgroundHex = "#000000") + + assertNotEquals(button1, button2) + } + + @Test + fun `given two ButtonProperties with different actionProperties when comparing with equals then returns false`() { + val action1 = mockk() + val action2 = mockk() + val button1 = ButtonProperties(actionProperties = action1) + val button2 = ButtonProperties(actionProperties = action2) + + assertNotEquals(button1, button2) + } + + @Test + fun `given two identical ButtonProperties when calling hashCode then returns same hash`() { + val button1 = ButtonProperties( + text = "Click", + textColorHex = "#FFFFFF" + ) + val button2 = ButtonProperties( + text = "Click", + textColorHex = "#FFFFFF" + ) + + assertEquals(button1.hashCode(), button2.hashCode()) + } + + @Test + fun `given two different ButtonProperties when calling hashCode then likely returns different hash`() { + val button1 = ButtonProperties(text = "Click") + val button2 = ButtonProperties(text = "Tap") + + assertNotEquals(button1.hashCode(), button2.hashCode()) + } + + @Test + fun `given ButtonProperties when calling toString then returns string representation`() { + val button = ButtonProperties( + text = "Click", + textColorHex = "#FFFFFF" + ) + + val toString = button.toString() + assertTrue(toString.contains("ButtonProperties")) + assertTrue(toString.contains("text=Click")) + assertTrue(toString.contains("textColorHex=#FFFFFF")) + } + + @Test + fun `given ButtonProperties with null actionProperties when checking field then returns null`() { + val button = ButtonProperties(text = "Click") + + assertEquals(null, button.actionProperties) + } + + @Test + fun `given ButtonProperties with actionProperties when checking field then returns actionProperties`() { + val actionProperties = mockk() + val button = ButtonProperties(actionProperties = actionProperties) + + assertEquals(actionProperties, button.actionProperties) + } + + @Test + fun `given ButtonProperties when modifying var actionProperties then updates field`() { + val button = ButtonProperties() + val actionProperties = mockk() + + assertEquals(null, button.actionProperties) + button.actionProperties = actionProperties + assertEquals(actionProperties, button.actionProperties) + } + + @Test + fun `given ButtonProperties with all null optional fields when checking default values then returns false for boolean fields`() { + val button = ButtonProperties( + text = null, + textColorHex = null, + align = null, + textAlign = null, + textSize = null, + textAllCaps = null, + fillMaxSize = null, + backgroundHex = null, + actionProperties = null + ) + + assertEquals(null, button.text) + assertEquals(null, button.textColorHex) + assertEquals(null, button.align) + assertEquals(null, button.textAlign) + assertEquals(null, button.textSize) + assertEquals(null, button.textAllCaps) + assertEquals(null, button.fillMaxSize) + assertEquals(null, button.backgroundHex) + assertEquals(null, button.actionProperties) + } + + @Test + fun `given ButtonProperties with empty strings when constructing then creates instance with empty strings`() { + val button = ButtonProperties( + text = "", + textColorHex = "", + textSize = "", + backgroundHex = "" + ) + + assertEquals("", button.text) + assertEquals("", button.textColorHex) + assertEquals("", button.textSize) + assertEquals("", button.backgroundHex) + } + + @Test + fun `given ButtonProperties with boolean true values when constructing then creates instance with true values`() { + val button = ButtonProperties( + textAllCaps = true, + fillMaxSize = true + ) + + assertEquals(true, button.textAllCaps) + assertEquals(true, button.fillMaxSize) + } + + @Test + fun `given ButtonProperties with all CraftDAlign values when constructing then creates instances correctly`() { + val alignValues = listOf( + CraftDAlign.START, + CraftDAlign.CENTER, + CraftDAlign.END + ) + + alignValues.forEach { align -> + val button = ButtonProperties(align = align) + assertEquals(align, button.align) + } + } + + @Test + fun `given ButtonProperties with all CraftDAlign values for textAlign when constructing then creates instances correctly`() { + val alignValues = listOf( + CraftDAlign.START, + CraftDAlign.CENTER, + CraftDAlign.END + ) + + alignValues.forEach { align -> + val button = ButtonProperties(textAlign = align) + assertEquals(align, button.textAlign) + } + } + + @Test + fun `given two ButtonProperties instances when one has all fields and other has defaults then they are not equal`() { + val actionProperties = mockk() + val full = ButtonProperties( + text = "Click", + textColorHex = "#FFFFFF", + align = CraftDAlign.CENTER, + textAlign = CraftDAlign.CENTER, + textSize = "16sp", + textAllCaps = true, + fillMaxSize = true, + backgroundHex = "#000000", + actionProperties = actionProperties + ) + val empty = ButtonProperties() + + assertNotEquals(full, empty) + } + + @Test + fun `given ButtonProperties when copying without changing any field then returns equal instance`() { + val original = ButtonProperties( + text = "Click", + textColorHex = "#FFFFFF" + ) + val copied = original.copy() + + assertEquals(original, copied) + assertTrue(original === original) + assertFalse(original === copied) + } + + @Test + fun `given ButtonProperties with special characters in text when constructing then creates instance with special characters`() { + val button = ButtonProperties(text = "Click & Tap! @#$%") + + assertEquals("Click & Tap! @#$%", button.text) + } + + @Test + fun `given ButtonProperties with hex color codes when constructing then creates instance with hex codes`() { + val button = ButtonProperties( + textColorHex = "#FFFFFF", + backgroundHex = "#000000FF" + ) + + assertEquals("#FFFFFF", button.textColorHex) + assertEquals("#000000FF", button.backgroundHex) + } + + @Test + fun `given ButtonProperties with different text sizes when constructing then creates instances with different sizes`() { + val sizes = listOf("12sp", "16sp", "20sp", "24sp", "32sp") + + sizes.forEach { size -> + val button = ButtonProperties(textSize = size) + assertEquals(size, button.textSize) + } + } + + @Test + fun `given multiple ButtonProperties instances when comparing hashCode consistency then hashCode remains same across multiple calls`() { + val button = ButtonProperties( + text = "Click", + textColorHex = "#FFFFFF" + ) + + val hash1 = button.hashCode() + val hash2 = button.hashCode() + val hash3 = button.hashCode() + + assertEquals(hash1, hash2) + assertEquals(hash2, hash3) + } + + @Test + fun `given ButtonProperties when created as immutable then reference cannot be modified externally except for var actionProperties`() { + val button = ButtonProperties(text = "Original") + + assertEquals("Original", button.text) + } + + @Test + fun `given ButtonProperties instances in a collection when checking equality then collection operations work correctly`() { + val button1 = ButtonProperties(text = "Click") + val button2 = ButtonProperties(text = "Click") + val button3 = ButtonProperties(text = "Tap") + + val list = listOf(button1, button3) + + assertTrue(list.contains(button1)) + assertTrue(list.contains(button2)) + assertFalse(list.contains(ButtonProperties(text = "Submit"))) + } + + @Test + fun `given ButtonProperties with null values when creating multiple copies then all copies maintain null values`() { + val original = ButtonProperties(text = null, textColorHex = null) + + val copy1 = original.copy() + val copy2 = original.copy() + + assertEquals(null, copy1.text) + assertEquals(null, copy1.textColorHex) + assertEquals(null, copy2.text) + assertEquals(null, copy2.textColorHex) + assertEquals(copy1, copy2) + } +} \ No newline at end of file diff --git a/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/data/model/checkbox/CheckBoxPropertiesTest.kt b/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/data/model/checkbox/CheckBoxPropertiesTest.kt new file mode 100644 index 0000000..538aeda --- /dev/null +++ b/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/data/model/checkbox/CheckBoxPropertiesTest.kt @@ -0,0 +1,362 @@ +package com.github.codandotv.craftd.androidcore.data.model.checkbox + +import com.github.codandotv.craftd.androidcore.data.model.action.ActionProperties +import com.github.codandotv.craftd.androidcore.domain.CraftDAlign +import io.mockk.mockk +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNotEquals +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@RunWith(JUnit4::class) +class CheckBoxPropertiesTest { + + @Test + fun `given all parameters when creating CheckBoxProperties then all fields are set correctly`() { + val actionProps = mockk() + val styleProps = mockk() + + val checkBoxProperties = CheckBoxProperties( + text = "Test Label", + align = CraftDAlign.LEFT, + textAlign = CraftDAlign.CENTER, + enable = true, + hasItRightText = true, + actionProperties = actionProps, + styleProperties = styleProps + ) + + assertEquals("Test Label", checkBoxProperties.text) + assertEquals(CraftDAlign.LEFT, checkBoxProperties.align) + assertEquals(CraftDAlign.CENTER, checkBoxProperties.textAlign) + assertTrue(checkBoxProperties.enable!!) + assertTrue(checkBoxProperties.hasItRightText!!) + assertEquals(actionProps, checkBoxProperties.actionProperties) + assertEquals(styleProps, checkBoxProperties.styleProperties) + } + + @Test + fun `given default parameters when creating CheckBoxProperties then default values are applied`() { + val checkBoxProperties = CheckBoxProperties() + + assertNull(checkBoxProperties.text) + assertNull(checkBoxProperties.align) + assertNull(checkBoxProperties.textAlign) + assertFalse(checkBoxProperties.enable!!) + assertFalse(checkBoxProperties.hasItRightText!!) + assertNull(checkBoxProperties.actionProperties) + assertNull(checkBoxProperties.styleProperties) + } + + @Test + fun `given partial parameters when creating CheckBoxProperties then specified fields are set and others use defaults`() { + val checkBoxProperties = CheckBoxProperties( + text = "Label", + enable = true + ) + + assertEquals("Label", checkBoxProperties.text) + assertNull(checkBoxProperties.align) + assertNull(checkBoxProperties.textAlign) + assertTrue(checkBoxProperties.enable!!) + assertFalse(checkBoxProperties.hasItRightText!!) + assertNull(checkBoxProperties.actionProperties) + assertNull(checkBoxProperties.styleProperties) + } + + @Test + fun `given CheckBoxProperties instance when calling copy with new values then new instance has updated fields`() { + val original = CheckBoxProperties( + text = "Original", + align = CraftDAlign.LEFT, + enable = true + ) + val actionProps = mockk() + + val copied = original.copy( + text = "Modified", + actionProperties = actionProps + ) + + assertEquals("Modified", copied.text) + assertEquals(CraftDAlign.LEFT, copied.align) + assertTrue(copied.enable!!) + assertEquals(actionProps, copied.actionProperties) + assertEquals("Original", original.text) + } + + @Test + fun `given two CheckBoxProperties with same values when comparing then equals returns true`() { + val props1 = CheckBoxProperties( + text = "Test", + align = CraftDAlign.LEFT, + enable = true + ) + val props2 = CheckBoxProperties( + text = "Test", + align = CraftDAlign.LEFT, + enable = true + ) + + assertEquals(props1, props2) + } + + @Test + fun `given two CheckBoxProperties with different values when comparing then equals returns false`() { + val props1 = CheckBoxProperties(text = "Test1", enable = true) + val props2 = CheckBoxProperties(text = "Test2", enable = false) + + assertNotEquals(props1, props2) + } + + @Test + fun `given two CheckBoxProperties with same values when calling hashCode then hash codes are equal`() { + val props1 = CheckBoxProperties( + text = "Test", + align = CraftDAlign.CENTER, + enable = true + ) + val props2 = CheckBoxProperties( + text = "Test", + align = CraftDAlign.CENTER, + enable = true + ) + + assertEquals(props1.hashCode(), props2.hashCode()) + } + + @Test + fun `given two CheckBoxProperties with different values when calling hashCode then hash codes may differ`() { + val props1 = CheckBoxProperties(text = "Test1", enable = true) + val props2 = CheckBoxProperties(text = "Test2", enable = false) + + assertNotEquals(props1.hashCode(), props2.hashCode()) + } + + @Test + fun `given null text when creating CheckBoxProperties then text is null`() { + val checkBoxProperties = CheckBoxProperties(text = null) + + assertNull(checkBoxProperties.text) + } + + @Test + fun `given null align when creating CheckBoxProperties then align is null`() { + val checkBoxProperties = CheckBoxProperties(align = null) + + assertNull(checkBoxProperties.align) + } + + @Test + fun `given null actionProperties when creating CheckBoxProperties then actionProperties is null`() { + val checkBoxProperties = CheckBoxProperties(actionProperties = null) + + assertNull(checkBoxProperties.actionProperties) + } + + @Test + fun `given null styleProperties when creating CheckBoxProperties then styleProperties is null`() { + val checkBoxProperties = CheckBoxProperties(styleProperties = null) + + assertNull(checkBoxProperties.styleProperties) + } + + @Test + fun `given CheckBoxProperties with all nullable fields as null when creating then instance is valid`() { + val checkBoxProperties = CheckBoxProperties( + text = null, + align = null, + textAlign = null, + enable = null, + hasItRightText = null, + actionProperties = null, + styleProperties = null + ) + + assertNull(checkBoxProperties.text) + assertNull(checkBoxProperties.align) + assertNull(checkBoxProperties.textAlign) + assertNull(checkBoxProperties.enable) + assertNull(checkBoxProperties.hasItRightText) + assertNull(checkBoxProperties.actionProperties) + assertNull(checkBoxProperties.styleProperties) + } + + @Test + fun `given empty string text when creating CheckBoxProperties then text is empty string`() { + val checkBoxProperties = CheckBoxProperties(text = "") + + assertEquals("", checkBoxProperties.text) + } + + @Test + fun `given false enable when creating CheckBoxProperties then enable is false`() { + val checkBoxProperties = CheckBoxProperties(enable = false) + + assertFalse(checkBoxProperties.enable!!) + } + + @Test + fun `given true hasItRightText when creating CheckBoxProperties then hasItRightText is true`() { + val checkBoxProperties = CheckBoxProperties(hasItRightText = true) + + assertTrue(checkBoxProperties.hasItRightText!!) + } + + @Test + fun `given multiple CraftDAlign values when creating CheckBoxProperties then align and textAlign are set correctly`() { + val alignments = listOf( + CraftDAlign.LEFT, + CraftDAlign.CENTER, + CraftDAlign.RIGHT + ) + + alignments.forEach { alignment -> + val checkBoxProperties = CheckBoxProperties( + align = alignment, + textAlign = alignment + ) + + assertEquals(alignment, checkBoxProperties.align) + assertEquals(alignment, checkBoxProperties.textAlign) + } + } + + @Test + fun `given CheckBoxProperties when checking isImmutable annotation then class has Immutable annotation`() { + val checkBoxProperties = CheckBoxProperties() + val isImmutable = checkBoxProperties::class.annotations.any { annotation -> + annotation::class.simpleName == "Immutable" + } + + assertTrue(isImmutable) + } + + @Test + fun `given CheckBoxProperties when checking isStable annotation then class has Stable annotation`() { + val checkBoxProperties = CheckBoxProperties() + val isStable = checkBoxProperties::class.annotations.any { annotation -> + annotation::class.simpleName == "Stable" + } + + assertTrue(isStable) + } + + @Test + fun `given CheckBoxProperties when checking isSerializable annotation then class has Serializable annotation`() { + val checkBoxProperties = CheckBoxProperties() + val isSerializable = checkBoxProperties::class.annotations.any { annotation -> + annotation::class.simpleName == "Serializable" + } + + assertTrue(isSerializable) + } + + @Test + fun `given two CheckBoxProperties instances when one has actionProperties and other does not then equals returns false`() { + val actionProps = mockk() + val props1 = CheckBoxProperties(actionProperties = actionProps) + val props2 = CheckBoxProperties(actionProperties = null) + + assertNotEquals(props1, props2) + } + + @Test + fun `given two CheckBoxProperties instances when one has styleProperties and other does not then equals returns false`() { + val styleProps = mockk() + val props1 = CheckBoxProperties(styleProperties = styleProps) + val props2 = CheckBoxProperties(styleProperties = null) + + assertNotEquals(props1, props2) + } + + @Test + fun `given CheckBoxProperties with actionProperties when copying with new actionProperties then new instance has updated actionProperties`() { + val originalAction = mockk() + val newAction = mockk() + val original = CheckBoxProperties(actionProperties = originalAction) + + val copied = original.copy(actionProperties = newAction) + + assertEquals(newAction, copied.actionProperties) + assertEquals(originalAction, original.actionProperties) + } + + @Test + fun `given CheckBoxProperties with styleProperties when copying with new styleProperties then new instance has updated styleProperties`() { + val originalStyle = mockk() + val newStyle = mockk() + val original = CheckBoxProperties(styleProperties = originalStyle) + + val copied = original.copy(styleProperties = newStyle) + + assertEquals(newStyle, copied.styleProperties) + assertEquals(originalStyle, original.styleProperties) + } + + @Test + fun `given CheckBoxProperties with mutable actionProperties when modifying then modification is reflected`() { + val actionProps = mockk(relaxed = true) + val checkBoxProperties = CheckBoxProperties(actionProperties = actionProps) + + checkBoxProperties.actionProperties = null + + assertNull(checkBoxProperties.actionProperties) + } + + @Test + fun `given CheckBoxProperties with mutable styleProperties when modifying then modification is reflected`() { + val styleProps = mockk(relaxed = true) + val checkBoxProperties = CheckBoxProperties(styleProperties = styleProps) + + checkBoxProperties.styleProperties = null + + assertNull(checkBoxProperties.styleProperties) + } + + @Test + fun `given CheckBoxProperties when checking toString then toString returns valid string representation`() { + val checkBoxProperties = CheckBoxProperties(text = "Test", enable = true) + val toStringResult = checkBoxProperties.toString() + + assertTrue(toStringResult.contains("CheckBoxProperties")) + assertTrue(toStringResult.contains("text")) + assertTrue(toStringResult.contains("Test")) + } + + @Test + fun `given multiple CheckBoxProperties with same data when creating then all instances are equal`() { + val instances = (1..5).map { + CheckBoxProperties( + text = "Test", + align = CraftDAlign.LEFT, + enable = true + ) + } + + instances.forEach { instance -> + assertEquals(instances[0], instance) + } + } + + @Test + fun `given CheckBoxProperties with complex nested objects when copying then nested objects are preserved`() { + val actionProps = mockk() + val styleProps = mockk() + val original = CheckBoxProperties( + text = "Test", + actionProperties = actionProps, + styleProperties = styleProps + ) + + val copied = original.copy() + + assertEquals(actionProps, copied.actionProperties) + assertEquals(styleProps, copied.styleProperties) + assertEquals(original, copied) + } +} \ No newline at end of file diff --git a/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/data/model/checkbox/StylePropertiesTest.kt b/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/data/model/checkbox/StylePropertiesTest.kt new file mode 100644 index 0000000..078b047 --- /dev/null +++ b/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/data/model/checkbox/StylePropertiesTest.kt @@ -0,0 +1,339 @@ +package com.github.codandotv.craftd.androidcore.data.model.checkbox + +import io.mockk.junit4.MockKRule +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonPrimitive +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import kotlin.test.assertEquals +import kotlin.test.assertNotEquals +import kotlin.test.assertNull + +@RunWith(JUnit4::class) +class StylePropertiesTest { + + @get:Rule + val mockkRule = MockKRule(this) + + @Test + fun `given all parameters when creating StyleProperties then initializes correctly`() { + val checkedColor = "#FF0000" + val uncheckedColor = "#00FF00" + + val styleProperties = StyleProperties( + checkedColor = checkedColor, + uncheckedColor = uncheckedColor + ) + + assertEquals(checkedColor, styleProperties.checkedColor) + assertEquals(uncheckedColor, styleProperties.uncheckedColor) + } + + @Test + fun `given null parameters when creating StyleProperties then initializes with defaults`() { + val styleProperties = StyleProperties() + + assertNull(styleProperties.checkedColor) + assertNull(styleProperties.uncheckedColor) + } + + @Test + fun `given only checkedColor when creating StyleProperties then uncheckedColor is null`() { + val checkedColor = "#FF0000" + + val styleProperties = StyleProperties(checkedColor = checkedColor) + + assertEquals(checkedColor, styleProperties.checkedColor) + assertNull(styleProperties.uncheckedColor) + } + + @Test + fun `given only uncheckedColor when creating StyleProperties then checkedColor is null`() { + val uncheckedColor = "#00FF00" + + val styleProperties = StyleProperties(uncheckedColor = uncheckedColor) + + assertNull(styleProperties.checkedColor) + assertEquals(uncheckedColor, styleProperties.uncheckedColor) + } + + @Test + fun `given StyleProperties with values when calling copy then returns new instance with updated values`() { + val original = StyleProperties( + checkedColor = "#FF0000", + uncheckedColor = "#00FF00" + ) + + val copied = original.copy(checkedColor = "#0000FF") + + assertEquals("#0000FF", copied.checkedColor) + assertEquals("#00FF00", copied.uncheckedColor) + assertNotEquals(original, copied) + } + + @Test + fun `given StyleProperties with values when calling copy with no arguments then returns equal instance`() { + val original = StyleProperties( + checkedColor = "#FF0000", + uncheckedColor = "#00FF00" + ) + + val copied = original.copy() + + assertEquals(original, copied) + } + + @Test + fun `given two StyleProperties with same values when comparing equals then returns true`() { + val styleProperties1 = StyleProperties( + checkedColor = "#FF0000", + uncheckedColor = "#00FF00" + ) + val styleProperties2 = StyleProperties( + checkedColor = "#FF0000", + uncheckedColor = "#00FF00" + ) + + assertEquals(styleProperties1, styleProperties2) + } + + @Test + fun `given two StyleProperties with different checkedColor when comparing equals then returns false`() { + val styleProperties1 = StyleProperties( + checkedColor = "#FF0000", + uncheckedColor = "#00FF00" + ) + val styleProperties2 = StyleProperties( + checkedColor = "#0000FF", + uncheckedColor = "#00FF00" + ) + + assertNotEquals(styleProperties1, styleProperties2) + } + + @Test + fun `given two StyleProperties with different uncheckedColor when comparing equals then returns false`() { + val styleProperties1 = StyleProperties( + checkedColor = "#FF0000", + uncheckedColor = "#00FF00" + ) + val styleProperties2 = StyleProperties( + checkedColor = "#FF0000", + uncheckedColor = "#0000FF" + ) + + assertNotEquals(styleProperties1, styleProperties2) + } + + @Test + fun `given two StyleProperties with all null values when comparing equals then returns true`() { + val styleProperties1 = StyleProperties() + val styleProperties2 = StyleProperties() + + assertEquals(styleProperties1, styleProperties2) + } + + @Test + fun `given two StyleProperties with null and non-null values when comparing equals then returns false`() { + val styleProperties1 = StyleProperties(checkedColor = "#FF0000") + val styleProperties2 = StyleProperties() + + assertNotEquals(styleProperties1, styleProperties2) + } + + @Test + fun `given two StyleProperties with same values when comparing hashCode then returns same hash`() { + val styleProperties1 = StyleProperties( + checkedColor = "#FF0000", + uncheckedColor = "#00FF00" + ) + val styleProperties2 = StyleProperties( + checkedColor = "#FF0000", + uncheckedColor = "#00FF00" + ) + + assertEquals(styleProperties1.hashCode(), styleProperties2.hashCode()) + } + + @Test + fun `given two StyleProperties with different values when comparing hashCode then likely returns different hash`() { + val styleProperties1 = StyleProperties(checkedColor = "#FF0000") + val styleProperties2 = StyleProperties(checkedColor = "#0000FF") + + assertNotEquals(styleProperties1.hashCode(), styleProperties2.hashCode()) + } + + @Test + fun `given StyleProperties with values when calling toString then returns string representation`() { + val styleProperties = StyleProperties( + checkedColor = "#FF0000", + uncheckedColor = "#00FF00" + ) + + val stringRepresentation = styleProperties.toString() + + assert(stringRepresentation.contains("StyleProperties")) + assert(stringRepresentation.contains("#FF0000")) + assert(stringRepresentation.contains("#00FF00")) + } + + @Test + fun `given StyleProperties with null values when calling toString then returns string representation`() { + val styleProperties = StyleProperties() + + val stringRepresentation = styleProperties.toString() + + assert(stringRepresentation.contains("StyleProperties")) + } + + @Test + fun `given StyleProperties instance when accessing checkedColor property then returns correct value`() { + val checkedColor = "#FF0000" + val styleProperties = StyleProperties(checkedColor = checkedColor) + + assertEquals(checkedColor, styleProperties.checkedColor) + } + + @Test + fun `given StyleProperties instance when accessing uncheckedColor property then returns correct value`() { + val uncheckedColor = "#00FF00" + val styleProperties = StyleProperties(uncheckedColor = uncheckedColor) + + assertEquals(uncheckedColor, styleProperties.uncheckedColor) + } + + @Test + fun `given StyleProperties with same instance when comparing equals then returns true`() { + val styleProperties = StyleProperties( + checkedColor = "#FF0000", + uncheckedColor = "#00FF00" + ) + + assertEquals(styleProperties, styleProperties) + } + + @Test + fun `given StyleProperties when comparing with different type then returns false`() { + val styleProperties = StyleProperties(checkedColor = "#FF0000") + + assertNotEquals(styleProperties, "#FF0000") + } + + @Test + fun `given multiple StyleProperties copies when comparing all equals then all are equal`() { + val original = StyleProperties( + checkedColor = "#FF0000", + uncheckedColor = "#00FF00" + ) + + val copy1 = original.copy() + val copy2 = original.copy() + + assertEquals(copy1, copy2) + assertEquals(original, copy1) + assertEquals(original, copy2) + } + + @Test + fun `given StyleProperties with empty strings when initializing then accepts empty strings`() { + val styleProperties = StyleProperties( + checkedColor = "", + uncheckedColor = "" + ) + + assertEquals("", styleProperties.checkedColor) + assertEquals("", styleProperties.uncheckedColor) + } + + @Test + fun `given StyleProperties with whitespace values when initializing then accepts whitespace`() { + val checkedColor = " " + val uncheckedColor = "\t" + + val styleProperties = StyleProperties( + checkedColor = checkedColor, + uncheckedColor = uncheckedColor + ) + + assertEquals(checkedColor, styleProperties.checkedColor) + assertEquals(uncheckedColor, styleProperties.uncheckedColor) + } + + @Test + fun `given StyleProperties with long color values when initializing then accepts long strings`() { + val longColor = "#" + "FF".repeat(100) + + val styleProperties = StyleProperties( + checkedColor = longColor, + uncheckedColor = longColor + ) + + assertEquals(longColor, styleProperties.checkedColor) + assertEquals(longColor, styleProperties.uncheckedColor) + } + + @Test + fun `given StyleProperties with special characters when initializing then accepts special characters`() { + val specialColor = "#FF0000!@#$%" + + val styleProperties = StyleProperties(checkedColor = specialColor) + + assertEquals(specialColor, styleProperties.checkedColor) + } + + @Test + fun `given two identical StyleProperties when using in collection then both are equal`() { + val styleProperties1 = StyleProperties(checkedColor = "#FF0000") + val styleProperties2 = StyleProperties(checkedColor = "#FF0000") + + val list1 = listOf(styleProperties1) + val list2 = listOf(styleProperties2) + + assertEquals(list1, list2) + } + + @Test + fun `given StyleProperties when creating with named arguments in any order then initializes correctly`() { + val styleProperties1 = StyleProperties( + uncheckedColor = "#00FF00", + checkedColor = "#FF0000" + ) + val styleProperties2 = StyleProperties( + checkedColor = "#FF0000", + uncheckedColor = "#00FF00" + ) + + assertEquals(styleProperties1, styleProperties2) + } + + @Test + fun `given StyleProperties when copying and modifying both properties then reflects changes correctly`() { + val original = StyleProperties( + checkedColor = "#FF0000", + uncheckedColor = "#00FF00" + ) + + val modified = original.copy( + checkedColor = "#0000FF", + uncheckedColor = "#FFFF00" + ) + + assertEquals("#0000FF", modified.checkedColor) + assertEquals("#FFFF00", modified.uncheckedColor) + assertEquals("#FF0000", original.checkedColor) + assertEquals("#00FF00", original.uncheckedColor) + } + + @Test + fun `given StyleProperties when destructuring then extracts values correctly`() { + val (checkedColor, uncheckedColor) = StyleProperties( + checkedColor = "#FF0000", + uncheckedColor = "#00FF00" + ) + + assertEquals("#FF0000", checkedColor) + assertEquals("#00FF00", uncheckedColor) + } +} \ No newline at end of file diff --git a/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/data/model/text/TextPropertiesTest.kt b/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/data/model/text/TextPropertiesTest.kt new file mode 100644 index 0000000..e910a50 --- /dev/null +++ b/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/data/model/text/TextPropertiesTest.kt @@ -0,0 +1,319 @@ +package com.github.codandotv.craftd.androidcore.data.model.text + +import io.mockk.mockk +import io.mockk.verify +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonNull +import kotlinx.serialization.json.JsonPrimitive +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNotEquals +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import com.github.codandotv.craftd.androidcore.data.model.action.ActionProperties +import com.github.codandotv.craftd.androidcore.domain.CraftDAlign +import com.github.codandotv.craftd.androidcore.domain.CraftDTextStyle + +@RunWith(JUnit4::class) +class TextPropertiesTest { + + @Test + fun `given default constructor when creating TextProperties then all fields have default values`() { + val textProperties = TextProperties() + + assertNull(textProperties.text) + assertNull(textProperties.textColorHex) + assertNull(textProperties.align) + assertNull(textProperties.textSize) + assertNull(textProperties.backgroundHex) + assertNull(textProperties.textStyle) + assertEquals(false, textProperties.textAllCaps) + assertNull(textProperties.actionProperties) + assertNull(textProperties.textHtml) + } + + @Test + fun `given all parameters when creating TextProperties then fields are set correctly`() { + val actionProperties = mockk() + val textProperties = TextProperties( + text = "Sample Text", + textColorHex = "#FF0000", + align = CraftDAlign.CENTER, + textSize = "16sp", + backgroundHex = "#FFFFFF", + textStyle = CraftDTextStyle.BOLD, + textAllCaps = true, + actionProperties = actionProperties, + textHtml = "Bold" + ) + + assertEquals("Sample Text", textProperties.text) + assertEquals("#FF0000", textProperties.textColorHex) + assertEquals(CraftDAlign.CENTER, textProperties.align) + assertEquals("16sp", textProperties.textSize) + assertEquals("#FFFFFF", textProperties.backgroundHex) + assertEquals(CraftDTextStyle.BOLD, textProperties.textStyle) + assertTrue(textProperties.textAllCaps!!) + assertEquals(actionProperties, textProperties.actionProperties) + assertEquals("Bold", textProperties.textHtml) + } + + @Test + fun `given TextProperties with partial parameters when creating then unspecified fields are null`() { + val textProperties = TextProperties( + text = "Partial Text", + textColorHex = "#FF0000" + ) + + assertEquals("Partial Text", textProperties.text) + assertEquals("#FF0000", textProperties.textColorHex) + assertNull(textProperties.align) + assertNull(textProperties.textSize) + assertNull(textProperties.backgroundHex) + assertNull(textProperties.textStyle) + assertEquals(false, textProperties.textAllCaps) + assertNull(textProperties.actionProperties) + assertNull(textProperties.textHtml) + } + + @Test + fun `given TextProperties when calling copy with new text then creates new instance with updated text`() { + val original = TextProperties(text = "Original") + val copied = original.copy(text = "Updated") + + assertEquals("Updated", copied.text) + assertEquals("Original", original.text) + } + + @Test + fun `given TextProperties when calling copy with multiple parameters then creates new instance with all updates`() { + val original = TextProperties( + text = "Original", + textColorHex = "#FF0000", + align = CraftDAlign.LEFT + ) + val copied = original.copy( + text = "Updated", + textColorHex = "#00FF00", + textSize = "14sp" + ) + + assertEquals("Updated", copied.text) + assertEquals("#00FF00", copied.textColorHex) + assertEquals(CraftDAlign.LEFT, copied.align) + assertEquals("14sp", copied.textSize) + } + + @Test + fun `given two identical TextProperties when comparing with equals then returns true`() { + val first = TextProperties( + text = "Test", + textColorHex = "#FF0000", + align = CraftDAlign.CENTER + ) + val second = TextProperties( + text = "Test", + textColorHex = "#FF0000", + align = CraftDAlign.CENTER + ) + + assertEquals(first, second) + assertTrue(first == second) + } + + @Test + fun `given two different TextProperties when comparing with equals then returns false`() { + val first = TextProperties(text = "Test1") + val second = TextProperties(text = "Test2") + + assertNotEquals(first, second) + assertFalse(first == second) + } + + @Test + fun `given TextProperties instances with different textAllCaps when comparing then returns false`() { + val first = TextProperties(textAllCaps = true) + val second = TextProperties(textAllCaps = false) + + assertNotEquals(first, second) + } + + @Test + fun `given two identical TextProperties when calling hashCode then returns same hash`() { + val first = TextProperties(text = "Test", textColorHex = "#FF0000") + val second = TextProperties(text = "Test", textColorHex = "#FF0000") + + assertEquals(first.hashCode(), second.hashCode()) + } + + @Test + fun `given two different TextProperties when calling hashCode then hash values may differ`() { + val first = TextProperties(text = "Test1") + val second = TextProperties(text = "Test2") + + assertNotEquals(first.hashCode(), second.hashCode()) + } + + @Test + fun `given TextProperties when calling toString then returns string representation`() { + val textProperties = TextProperties(text = "Test", textColorHex = "#FF0000") + val stringRep = textProperties.toString() + + assertTrue(stringRep.contains("TextProperties")) + assertTrue(stringRep.contains("Test")) + assertTrue(stringRep.contains("#FF0000")) + } + + @Test + fun `given TextProperties with ActionProperties when setting actionProperties then property is updated`() { + val textProperties = TextProperties() + val actionProperties = mockk() + + textProperties.actionProperties = actionProperties + + assertEquals(actionProperties, textProperties.actionProperties) + } + + @Test + fun `given all CraftDAlign enum values when verifying they exist then all are accessible`() { + assertEquals(CraftDAlign.LEFT, enumValueOf("LEFT")) + assertEquals(CraftDAlign.CENTER, enumValueOf("CENTER")) + assertEquals(CraftDAlign.RIGHT, enumValueOf("RIGHT")) + } + + @Test + fun `given all CraftDTextStyle enum values when verifying they exist then all are accessible`() { + assertEquals(CraftDTextStyle.REGULAR, enumValueOf("REGULAR")) + assertEquals(CraftDTextStyle.BOLD, enumValueOf("BOLD")) + assertEquals(CraftDTextStyle.ITALIC, enumValueOf("ITALIC")) + } + + @Test + fun `given TextProperties with actionProperties when verifying mutability then can reassign`() { + val firstAction = mockk() + val secondAction = mockk() + val textProperties = TextProperties(actionProperties = firstAction) + + assertEquals(firstAction, textProperties.actionProperties) + + textProperties.actionProperties = secondAction + assertEquals(secondAction, textProperties.actionProperties) + } + + @Test + fun `given TextProperties with null values when checking nullable fields then all are null`() { + val textProperties = TextProperties( + text = null, + textColorHex = null, + align = null, + textSize = null, + backgroundHex = null, + textStyle = null, + actionProperties = null, + textHtml = null + ) + + assertNull(textProperties.text) + assertNull(textProperties.textColorHex) + assertNull(textProperties.align) + assertNull(textProperties.textSize) + assertNull(textProperties.backgroundHex) + assertNull(textProperties.textStyle) + assertNull(textProperties.actionProperties) + assertNull(textProperties.textHtml) + } + + @Test + fun `given TextProperties with empty strings when creating then strings are preserved`() { + val textProperties = TextProperties( + text = "", + textColorHex = "", + textSize = "", + backgroundHex = "", + textHtml = "" + ) + + assertEquals("", textProperties.text) + assertEquals("", textProperties.textColorHex) + assertEquals("", textProperties.textSize) + assertEquals("", textProperties.backgroundHex) + assertEquals("", textProperties.textHtml) + } + + @Test + fun `given TextProperties copy operation when preserving textAllCaps then boolean value is maintained`() { + val original = TextProperties(textAllCaps = true) + val copied = original.copy() + + assertTrue(copied.textAllCaps!!) + } + + @Test + fun `given TextProperties with all fields populated when calling copy without parameters then creates exact duplicate`() { + val actionProperties = mockk() + val original = TextProperties( + text = "Test", + textColorHex = "#FF0000", + align = CraftDAlign.CENTER, + textSize = "16sp", + backgroundHex = "#FFFFFF", + textStyle = CraftDTextStyle.BOLD, + textAllCaps = true, + actionProperties = actionProperties, + textHtml = "Bold" + ) + val copied = original.copy() + + assertEquals(original, copied) + } + + @Test + fun `given TextProperties instances when comparing same instance to itself then equals returns true`() { + val textProperties = TextProperties(text = "Test") + + assertEquals(textProperties, textProperties) + assertTrue(textProperties == textProperties) + } + + @Test + fun `given TextProperties with boolean false when copying and changing to true then reflects change`() { + val original = TextProperties(textAllCaps = false) + val copied = original.copy(textAllCaps = true) + + assertFalse(original.textAllCaps!!) + assertTrue(copied.textAllCaps!!) + } + + @Test + fun `given TextProperties instances with null and non-null text when comparing then not equal`() { + val first = TextProperties(text = null) + val second = TextProperties(text = "Text") + + assertNotEquals(first, second) + } + + @Test + fun `given TextProperties with all align values when creating each then all are stored correctly`() { + val alignLeft = TextProperties(align = CraftDAlign.LEFT) + val alignCenter = TextProperties(align = CraftDAlign.CENTER) + val alignRight = TextProperties(align = CraftDAlign.RIGHT) + + assertEquals(CraftDAlign.LEFT, alignLeft.align) + assertEquals(CraftDAlign.CENTER, alignCenter.align) + assertEquals(CraftDAlign.RIGHT, alignRight.align) + } + + @Test + fun `given TextProperties with all textStyle values when creating each then all are stored correctly`() { + val styleRegular = TextProperties(textStyle = CraftDTextStyle.REGULAR) + val styleBold = TextProperties(textStyle = CraftDTextStyle.BOLD) + val styleItalic = TextProperties(textStyle = CraftDTextStyle.ITALIC) + + assertEquals(CraftDTextStyle.REGULAR, styleRegular.textStyle) + assertEquals(CraftDTextStyle.BOLD, styleBold.textStyle) + assertEquals(CraftDTextStyle.ITALIC, styleItalic.textStyle) + } +} \ No newline at end of file diff --git a/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/domain/CraftDAlignTest.kt b/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/domain/CraftDAlignTest.kt new file mode 100644 index 0000000..3541415 --- /dev/null +++ b/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/domain/CraftDAlignTest.kt @@ -0,0 +1,176 @@ +package com.github.codandotv.craftd.androidcore.domain + +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith + +@RunWith(JUnit4::class) +class CraftDAlignTest { + + @Test + fun `given CENTER enum constant when accessed then should exist`() { + val align = CraftDAlign.CENTER + assertEquals("CENTER", align.name) + } + + @Test + fun `given LEFT enum constant when accessed then should exist`() { + val align = CraftDAlign.LEFT + assertEquals("LEFT", align.name) + } + + @Test + fun `given RIGHT enum constant when accessed then should exist`() { + val align = CraftDAlign.RIGHT + assertEquals("RIGHT", align.name) + } + + @Test + fun `given TOP enum constant when accessed then should exist`() { + val align = CraftDAlign.TOP + assertEquals("TOP", align.name) + } + + @Test + fun `given BOTTOM enum constant when accessed then should exist`() { + val align = CraftDAlign.BOTTOM + assertEquals("BOTTOM", align.name) + } + + @Test + fun `given CENTER constant when converted via enumValueOf then should return CENTER`() { + val align = enumValueOf("CENTER") + assertEquals(CraftDAlign.CENTER, align) + } + + @Test + fun `given LEFT constant when converted via enumValueOf then should return LEFT`() { + val align = enumValueOf("LEFT") + assertEquals(CraftDAlign.LEFT, align) + } + + @Test + fun `given RIGHT constant when converted via enumValueOf then should return RIGHT`() { + val align = enumValueOf("RIGHT") + assertEquals(CraftDAlign.RIGHT, align) + } + + @Test + fun `given TOP constant when converted via enumValueOf then should return TOP`() { + val align = enumValueOf("TOP") + assertEquals(CraftDAlign.TOP, align) + } + + @Test + fun `given BOTTOM constant when converted via enumValueOf then should return BOTTOM`() { + val align = enumValueOf("BOTTOM") + assertEquals(CraftDAlign.BOTTOM, align) + } + + @Test + fun `given invalid string when converted via enumValueOf then should throw IllegalArgumentException`() { + assertFailsWith { + enumValueOf("INVALID") + } + } + + @Test + fun `given empty string when converted via enumValueOf then should throw IllegalArgumentException`() { + assertFailsWith { + enumValueOf("") + } + } + + @Test + fun `given all enum values when accessed then should have exactly five constants`() { + val values = CraftDAlign.values() + assertEquals(5, values.size) + } + + @Test + fun `given all enum values when accessed then should contain CENTER`() { + assert(CraftDAlign.values().contains(CraftDAlign.CENTER)) + } + + @Test + fun `given all enum values when accessed then should contain LEFT`() { + assert(CraftDAlign.values().contains(CraftDAlign.LEFT)) + } + + @Test + fun `given all enum values when accessed then should contain RIGHT`() { + assert(CraftDAlign.values().contains(CraftDAlign.RIGHT)) + } + + @Test + fun `given all enum values when accessed then should contain TOP`() { + assert(CraftDAlign.values().contains(CraftDAlign.TOP)) + } + + @Test + fun `given all enum values when accessed then should contain BOTTOM`() { + assert(CraftDAlign.values().contains(CraftDAlign.BOTTOM)) + } + + @Test + fun `given CENTER constant when compared to CENTER then should be equal`() { + assertEquals(CraftDAlign.CENTER, CraftDAlign.CENTER) + } + + @Test + fun `given CENTER constant when compared to LEFT then should not be equal`() { + assert(CraftDAlign.CENTER != CraftDAlign.LEFT) + } + + @Test + fun `given CENTER constant when compared to RIGHT then should not be equal`() { + assert(CraftDAlign.CENTER != CraftDAlign.RIGHT) + } + + @Test + fun `given LEFT constant when compared to TOP then should not be equal`() { + assert(CraftDAlign.LEFT != CraftDAlign.TOP) + } + + @Test + fun `given BOTTOM constant when converted to string then should return BOTTOM`() { + assertEquals("BOTTOM", CraftDAlign.BOTTOM.toString()) + } + + @Test + fun `given enum constants when compared via ordinal then CENTER should be first`() { + assertEquals(0, CraftDAlign.CENTER.ordinal) + } + + @Test + fun `given enum constants when compared via ordinal then LEFT should be second`() { + assertEquals(1, CraftDAlign.LEFT.ordinal) + } + + @Test + fun `given enum constants when compared via ordinal then RIGHT should be third`() { + assertEquals(2, CraftDAlign.RIGHT.ordinal) + } + + @Test + fun `given enum constants when compared via ordinal then TOP should be fourth`() { + assertEquals(3, CraftDAlign.TOP.ordinal) + } + + @Test + fun `given enum constants when compared via ordinal then BOTTOM should be fifth`() { + assertEquals(4, CraftDAlign.BOTTOM.ordinal) + } + + @Test + fun `given CENTER constant when hashCode called then should match another CENTER hashCode`() { + assertEquals(CraftDAlign.CENTER.hashCode(), CraftDAlign.CENTER.hashCode()) + } + + @Test + fun `given LEFT and RIGHT constants when hashCode called then should not be equal`() { + assert(CraftDAlign.LEFT.hashCode() != CraftDAlign.RIGHT.hashCode()) + } +} \ No newline at end of file diff --git a/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/domain/CraftDTextStyleTest.kt b/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/domain/CraftDTextStyleTest.kt new file mode 100644 index 0000000..517353b --- /dev/null +++ b/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/domain/CraftDTextStyleTest.kt @@ -0,0 +1,182 @@ +package com.github.codandotv.craftd.androidcore.domain + +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import kotlin.test.assertEquals +import kotlin.test.assertNotNull + +@RunWith(JUnit4::class) +class CraftDTextStyleTest { + + @Test + fun `given BOLD constant when accessed then returns valid enum value`() { + val result = CraftDTextStyle.BOLD + assertNotNull(result) + assertEquals(CraftDTextStyle.BOLD, result) + } + + @Test + fun `given ITALIC constant when accessed then returns valid enum value`() { + val result = CraftDTextStyle.ITALIC + assertNotNull(result) + assertEquals(CraftDTextStyle.ITALIC, result) + } + + @Test + fun `given NORMAL constant when accessed then returns valid enum value`() { + val result = CraftDTextStyle.NORMAL + assertNotNull(result) + assertEquals(CraftDTextStyle.NORMAL, result) + } + + @Test + fun `given BOLD string when converted via enumValueOf then returns BOLD enum`() { + val result = enumValueOf("BOLD") + assertEquals(CraftDTextStyle.BOLD, result) + } + + @Test + fun `given ITALIC string when converted via enumValueOf then returns ITALIC enum`() { + val result = enumValueOf("ITALIC") + assertEquals(CraftDTextStyle.ITALIC, result) + } + + @Test + fun `given NORMAL string when converted via enumValueOf then returns NORMAL enum`() { + val result = enumValueOf("NORMAL") + assertEquals(CraftDTextStyle.NORMAL, result) + } + + @Test + fun `given all enum values when listed then returns exactly three constants`() { + val values = CraftDTextStyle.values() + assertEquals(3, values.size) + } + + @Test + fun `given all enum values when listed then contains BOLD ITALIC NORMAL`() { + val values = CraftDTextStyle.values().toList() + assertEquals(true, values.contains(CraftDTextStyle.BOLD)) + assertEquals(true, values.contains(CraftDTextStyle.ITALIC)) + assertEquals(true, values.contains(CraftDTextStyle.NORMAL)) + } + + @Test + fun `given BOLD enum when compared to itself then returns true for equality`() { + val style1 = CraftDTextStyle.BOLD + val style2 = CraftDTextStyle.BOLD + assertEquals(style1, style2) + } + + @Test + fun `given BOLD enum when compared to ITALIC then returns false for equality`() { + val style1 = CraftDTextStyle.BOLD + val style2 = CraftDTextStyle.ITALIC + assertEquals(false, style1 == style2) + } + + @Test + fun `given BOLD enum when hashCode called then returns consistent value`() { + val style1 = CraftDTextStyle.BOLD + val style2 = CraftDTextStyle.BOLD + assertEquals(style1.hashCode(), style2.hashCode()) + } + + @Test + fun `given BOLD enum when converted to string then returns BOLD`() { + val result = CraftDTextStyle.BOLD.toString() + assertEquals("BOLD", result) + } + + @Test + fun `given ITALIC enum when converted to string then returns ITALIC`() { + val result = CraftDTextStyle.ITALIC.toString() + assertEquals("ITALIC", result) + } + + @Test + fun `given NORMAL enum when converted to string then returns NORMAL`() { + val result = CraftDTextStyle.NORMAL.toString() + assertEquals("NORMAL", result) + } + + @Test + fun `given BOLD enum when name property accessed then returns BOLD`() { + val result = CraftDTextStyle.BOLD.name + assertEquals("BOLD", result) + } + + @Test + fun `given ITALIC enum when ordinal property accessed then returns one`() { + val result = CraftDTextStyle.ITALIC.ordinal + assertEquals(1, result) + } + + @Test + fun `given NORMAL enum when ordinal property accessed then returns two`() { + val result = CraftDTextStyle.NORMAL.ordinal + assertEquals(2, result) + } + + @Test + fun `given BOLD enum when ordinal property accessed then returns zero`() { + val result = CraftDTextStyle.BOLD.ordinal + assertEquals(0, result) + } + + @Test + fun `given enum values when used in set then all unique values present`() { + val styleSet = setOf( + CraftDTextStyle.BOLD, + CraftDTextStyle.ITALIC, + CraftDTextStyle.NORMAL, + CraftDTextStyle.BOLD + ) + assertEquals(3, styleSet.size) + } + + @Test + fun `given enum values when iterated then all three constants accessible`() { + var count = 0 + for (style in CraftDTextStyle.values()) { + count++ + } + assertEquals(3, count) + } + + @Test(expected = IllegalArgumentException::class) + fun `given invalid string when converted via enumValueOf then throws IllegalArgumentException`() { + enumValueOf("INVALID") + } + + @Test + fun `given BOLD enum when used in when expression then returns correct branch`() { + val result = when (CraftDTextStyle.BOLD) { + CraftDTextStyle.BOLD -> "bold_style" + CraftDTextStyle.ITALIC -> "italic_style" + CraftDTextStyle.NORMAL -> "normal_style" + } + assertEquals("bold_style", result) + } + + @Test + fun `given ITALIC enum when used in when expression then returns correct branch`() { + val result = when (CraftDTextStyle.ITALIC) { + CraftDTextStyle.BOLD -> "bold_style" + CraftDTextStyle.ITALIC -> "italic_style" + CraftDTextStyle.NORMAL -> "normal_style" + } + assertEquals("italic_style", result) + } + + @Test + fun `given NORMAL enum when used in when expression then returns correct branch`() { + val result = when (CraftDTextStyle.NORMAL) { + CraftDTextStyle.BOLD -> "bold_style" + CraftDTextStyle.ITALIC -> "italic_style" + CraftDTextStyle.NORMAL -> "normal_style" + } + assertEquals("normal_style", result) + } +} \ No newline at end of file diff --git a/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/extensions/ContextExtesionTest.kt b/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/extensions/ContextExtesionTest.kt new file mode 100644 index 0000000..82664a5 --- /dev/null +++ b/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/extensions/ContextExtesionTest.kt @@ -0,0 +1,260 @@ +package com.github.codandotv.craftd.androidcore.extensions + +import android.content.Context +import android.content.res.AssetManager +import android.util.TypedValue +import io.mockk.every +import io.mockk.mockk +import io.mockk.mockkStatic +import io.mockk.unmockkAll +import io.mockk.verify +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import java.io.BufferedReader +import java.io.InputStream +import kotlin.test.assertEquals +import kotlin.test.assertNotNull + +@RunWith(JUnit4::class) +class ContextExtensionTest { + + private lateinit var mockContext: Context + private lateinit var mockAssetManager: AssetManager + private lateinit var mockInputStream: InputStream + private lateinit var mockBufferedReader: BufferedReader + + @Before + fun setup() { + mockContext = mockk(relaxed = true) + mockAssetManager = mockk(relaxed = true) + mockInputStream = mockk(relaxed = true) + mockBufferedReader = mockk(relaxed = true) + } + + @After + fun tearDown() { + unmockkAll() + } + + @Test + fun `given valid json file when loadJSONFromAsset then returns file content`() { + val fileName = "test_config" + val expectedContent = """{"key": "value", "number": 123}""" + + every { mockContext.assets } returns mockAssetManager + every { mockAssetManager.open("$fileName.json") } returns mockInputStream + every { mockInputStream.bufferedReader() } returns mockBufferedReader + every { mockBufferedReader.readText() } returns expectedContent + every { mockBufferedReader.close() } returns Unit + + val result = mockContext.loadJSONFromAsset(fileName) + + assertEquals(expectedContent, result) + verify { mockAssetManager.open("$fileName.json") } + verify { mockBufferedReader.readText() } + } + + @Test + fun `given empty file name when loadJSONFromAsset then opens with json extension`() { + val fileName = "" + val expectedContent = "{}" + + every { mockContext.assets } returns mockAssetManager + every { mockAssetManager.open(".json") } returns mockInputStream + every { mockInputStream.bufferedReader() } returns mockBufferedReader + every { mockBufferedReader.readText() } returns expectedContent + every { mockBufferedReader.close() } returns Unit + + val result = mockContext.loadJSONFromAsset(fileName) + + assertEquals(expectedContent, result) + verify { mockAssetManager.open(".json") } + } + + @Test + fun `given file with special characters when loadJSONFromAsset then constructs correct path`() { + val fileName = "test-config_v2" + val expectedContent = """{"data": [1, 2, 3]}""" + + every { mockContext.assets } returns mockAssetManager + every { mockAssetManager.open("$fileName.json") } returns mockInputStream + every { mockInputStream.bufferedReader() } returns mockBufferedReader + every { mockBufferedReader.readText() } returns expectedContent + every { mockBufferedReader.close() } returns Unit + + val result = mockContext.loadJSONFromAsset(fileName) + + assertEquals(expectedContent, result) + verify { mockAssetManager.open("test-config_v2.json") } + } + + @Test + fun `given multiline json file when loadJSONFromAsset then returns complete content`() { + val fileName = "multiline" + val expectedContent = """{ + "name": "Test", + "items": [1, 2, 3], + "nested": { + "key": "value" + } + }""" + + every { mockContext.assets } returns mockAssetManager + every { mockAssetManager.open("$fileName.json") } returns mockInputStream + every { mockInputStream.bufferedReader() } returns mockBufferedReader + every { mockBufferedReader.readText() } returns expectedContent + every { mockBufferedReader.close() } returns Unit + + val result = mockContext.loadJSONFromAsset(fileName) + + assertEquals(expectedContent, result) + } + + @Test + fun `given valid attribute when getAttrColorRes then returns color data`() { + val attributeRes = android.R.attr.textColorPrimary + val expectedColorData = 0xFFFFFFFF.toInt() + val mockTheme = mockk(relaxed = true) + val typedValue = TypedValue() + typedValue.data = expectedColorData + + every { mockContext.theme } returns mockTheme + every { mockTheme.resolveAttribute(attributeRes, any(), false) } answers { + val value = secondArg() + value.data = expectedColorData + true + } + + val result = mockContext.getAttrColorRes(attributeRes) + + assertEquals(expectedColorData, result) + verify { mockTheme.resolveAttribute(attributeRes, any(), false) } + } + + @Test + fun `given non existent attribute when getAttrColorRes then returns zero`() { + val attributeRes = 99999 + val mockTheme = mockk(relaxed = true) + + every { mockContext.theme } returns mockTheme + every { mockTheme.resolveAttribute(attributeRes, any(), false) } returns false + + val result = mockContext.getAttrColorRes(attributeRes) + + assertEquals(0, result) + verify { mockTheme.resolveAttribute(attributeRes, any(), false) } + } + + @Test + fun `given multiple different attributes when getAttrColorRes then returns correct color for each`() { + val attr1 = android.R.attr.textColorPrimary + val attr2 = android.R.attr.textColorSecondary + val color1 = 0xFF000000.toInt() + val color2 = 0xFF808080.toInt() + val mockTheme = mockk(relaxed = true) + + every { mockContext.theme } returns mockTheme + every { mockTheme.resolveAttribute(attr1, any(), false) } answers { + val value = secondArg() + value.data = color1 + true + } + every { mockTheme.resolveAttribute(attr2, any(), false) } answers { + val value = secondArg() + value.data = color2 + true + } + + val result1 = mockContext.getAttrColorRes(attr1) + val result2 = mockContext.getAttrColorRes(attr2) + + assertEquals(color1, result1) + assertEquals(color2, result2) + } + + @Test + fun `given attribute with zero data when getAttrColorRes then returns zero`() { + val attributeRes = android.R.attr.textColorPrimary + val mockTheme = mockk(relaxed = true) + + every { mockContext.theme } returns mockTheme + every { mockTheme.resolveAttribute(attributeRes, any(), false) } answers { + val value = secondArg() + value.data = 0 + true + } + + val result = mockContext.getAttrColorRes(attributeRes) + + assertEquals(0, result) + } + + @Test + fun `given negative attribute id when getAttrColorRes then resolves attribute`() { + val attributeRes = -1 + val expectedColorData = 0xFF123456.toInt() + val mockTheme = mockk(relaxed = true) + + every { mockContext.theme } returns mockTheme + every { mockTheme.resolveAttribute(attributeRes, any(), false) } answers { + val value = secondArg() + value.data = expectedColorData + true + } + + val result = mockContext.getAttrColorRes(attributeRes) + + assertEquals(expectedColorData, result) + } + + @Test + fun `given theme resolution fails when getAttrColorRes then returns zero`() { + val attributeRes = android.R.attr.textColorPrimary + val mockTheme = mockk(relaxed = true) + + every { mockContext.theme } returns mockTheme + every { mockTheme.resolveAttribute(attributeRes, any(), false) } returns false + + val result = mockContext.getAttrColorRes(attributeRes) + + assertEquals(0, result) + verify(exactly = 1) { mockTheme.resolveAttribute(attributeRes, any(), false) } + } + + @Test + fun `given large color value when getAttrColorRes then returns correct value`() { + val attributeRes = android.R.attr.textColorPrimary + val largeColorValue = 0xFFFFFFFF.toInt() + val mockTheme = mockk(relaxed = true) + + every { mockContext.theme } returns mockTheme + every { mockTheme.resolveAttribute(attributeRes, any(), false) } answers { + val value = secondArg() + value.data = largeColorValue + true + } + + val result = mockContext.getAttrColorRes(attributeRes) + + assertEquals(largeColorValue, result) + } + + @Test + fun `given buffered reader when loadJSONFromAsset then closes resource automatically`() { + val fileName = "test" + val content = "{}" + + every { mockContext.assets } returns mockAssetManager + every { mockAssetManager.open("$fileName.json") } returns mockInputStream + every { mockInputStream.bufferedReader() } returns mockBufferedReader + every { mockBufferedReader.readText() } returns content + every { mockBufferedReader.close() } returns Unit + + mockContext.loadJSONFromAsset(fileName) + + verify { mockBufferedReader.close() } + } +} \ No newline at end of file diff --git a/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/extensions/StringExtensionsTest.kt b/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/extensions/StringExtensionsTest.kt new file mode 100644 index 0000000..d0b727f --- /dev/null +++ b/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/extensions/StringExtensionsTest.kt @@ -0,0 +1,80 @@ +package com.github.codandotv.craftd.androidcore.extensions + +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +@RunWith(JUnit4::class) +class StringExtensionsTest { + + @Test + fun `given String Companion when empty then returns empty string`() { + val result = String.empty() + assertEquals("", result) + assertTrue(result.isEmpty()) + } + + @Test + fun `given String Companion when empty then returns exact empty string instance`() { + val result1 = String.empty() + val result2 = String.empty() + assertEquals(result1, result2) + } + + @Test + fun `given String Companion when empty then returned string has length zero`() { + val result = String.empty() + assertEquals(0, result.length) + } + + @Test + fun `given String Companion when empty then returned string is not null`() { + val result = String.empty() + assert(result != null) + } + + @Test + fun `given String Companion when empty multiple times then all results are equal`() { + val result1 = String.empty() + val result2 = String.empty() + val result3 = String.empty() + assertEquals(result1, result2) + assertEquals(result2, result3) + } + + @Test + fun `given String Companion when empty then returned string can be used in comparisons`() { + val emptyStr = String.empty() + assertTrue(emptyStr == "") + } + + @Test + fun `given String Companion when empty then returned string behaves like empty string literal`() { + val result = String.empty() + val literal = "" + assertEquals(result, literal) + assertEquals(result.hashCode(), literal.hashCode()) + } + + @Test + fun `given String Companion when empty then concatenation produces expected result`() { + val emptyStr = String.empty() + val concatenated = emptyStr + "test" + assertEquals("test", concatenated) + } + + @Test + fun `given String Companion when empty then string operations work correctly`() { + val result = String.empty() + assertTrue(result.isBlank()) + assertTrue(result.all { false }) + } + + @Test + fun `given String Companion when empty then string representation is consistent`() { + val result = String.empty() + assertEquals("", result.toString()) + } +} \ No newline at end of file diff --git a/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/presentation/CraftDComponentKeyTest.kt b/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/presentation/CraftDComponentKeyTest.kt new file mode 100644 index 0000000..7ff8d8f --- /dev/null +++ b/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/presentation/CraftDComponentKeyTest.kt @@ -0,0 +1,157 @@ +package com.github.codandotv.craftd.androidcore.presentation + +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import kotlin.test.assertEquals +import kotlin.test.assertNotNull + +@RunWith(JUnit4::class) +class CraftDComponentKeyTest { + + @Test + fun `given TEXT_VIEW_COMPONENT enum when accessing key then returns correct value`() { + val component = CraftDComponentKey.TEXT_VIEW_COMPONENT + assertEquals("CraftDTextView", component.key) + } + + @Test + fun `given BUTTON_COMPONENT enum when accessing key then returns correct value`() { + val component = CraftDComponentKey.BUTTON_COMPONENT + assertEquals("CraftDButton", component.key) + } + + @Test + fun `given CHECK_BOX_COMPONENT enum when accessing key then returns correct value`() { + val component = CraftDComponentKey.CHECK_BOX_COMPONENT + assertEquals("CraftDCheckBox", component.key) + } + + @Test + fun `given TEXT_VIEW_COMPONENT enum when accessing name then returns TEXT_VIEW_COMPONENT`() { + val component = CraftDComponentKey.TEXT_VIEW_COMPONENT + assertEquals("TEXT_VIEW_COMPONENT", component.name) + } + + @Test + fun `given BUTTON_COMPONENT enum when accessing name then returns BUTTON_COMPONENT`() { + val component = CraftDComponentKey.BUTTON_COMPONENT + assertEquals("BUTTON_COMPONENT", component.name) + } + + @Test + fun `given CHECK_BOX_COMPONENT enum when accessing name then returns CHECK_BOX_COMPONENT`() { + val component = CraftDComponentKey.CHECK_BOX_COMPONENT + assertEquals("CHECK_BOX_COMPONENT", component.name) + } + + @Test + fun `given valid enum name when using enumValueOf then returns correct enum`() { + val component = enumValueOf("TEXT_VIEW_COMPONENT") + assertEquals(CraftDComponentKey.TEXT_VIEW_COMPONENT, component) + } + + @Test + fun `given valid enum name BUTTON_COMPONENT when using enumValueOf then returns correct enum`() { + val component = enumValueOf("BUTTON_COMPONENT") + assertEquals(CraftDComponentKey.BUTTON_COMPONENT, component) + } + + @Test + fun `given valid enum name CHECK_BOX_COMPONENT when using enumValueOf then returns correct enum`() { + val component = enumValueOf("CHECK_BOX_COMPONENT") + assertEquals(CraftDComponentKey.CHECK_BOX_COMPONENT, component) + } + + @Test + fun `given all enum constants when iterating then all constants are present`() { + val constants = CraftDComponentKey.values() + assertEquals(3, constants.size) + assertEquals(true, constants.contains(CraftDComponentKey.TEXT_VIEW_COMPONENT)) + assertEquals(true, constants.contains(CraftDComponentKey.BUTTON_COMPONENT)) + assertEquals(true, constants.contains(CraftDComponentKey.CHECK_BOX_COMPONENT)) + } + + @Test + fun `given TEXT_VIEW_COMPONENT enum when comparing equality then same instance returns true`() { + val component1 = CraftDComponentKey.TEXT_VIEW_COMPONENT + val component2 = CraftDComponentKey.TEXT_VIEW_COMPONENT + assertEquals(component1, component2) + } + + @Test + fun `given different enum constants when comparing equality then returns false`() { + val component1 = CraftDComponentKey.TEXT_VIEW_COMPONENT + val component2 = CraftDComponentKey.BUTTON_COMPONENT + assertEquals(false, component1 == component2) + } + + @Test + fun `given enum constant when calling hashCode then returns consistent value`() { + val component1 = CraftDComponentKey.TEXT_VIEW_COMPONENT + val component2 = CraftDComponentKey.TEXT_VIEW_COMPONENT + assertEquals(component1.hashCode(), component2.hashCode()) + } + + @Test + fun `given different enum constants when comparing hashCode then returns different values`() { + val component1 = CraftDComponentKey.TEXT_VIEW_COMPONENT + val component2 = CraftDComponentKey.BUTTON_COMPONENT + assertEquals(false, component1.hashCode() == component2.hashCode()) + } + + @Test + fun `given CRAFT_D constant when accessed then returns CraftD`() { + assertEquals("CraftD", CRAFT_D) + } + + @Test + fun `given TEXT_VIEW_COMPONENT when checking prefix then key starts with CRAFT_D`() { + val component = CraftDComponentKey.TEXT_VIEW_COMPONENT + assertEquals(true, component.key.startsWith(CRAFT_D)) + } + + @Test + fun `given BUTTON_COMPONENT when checking prefix then key starts with CRAFT_D`() { + val component = CraftDComponentKey.BUTTON_COMPONENT + assertEquals(true, component.key.startsWith(CRAFT_D)) + } + + @Test + fun `given CHECK_BOX_COMPONENT when checking prefix then key starts with CRAFT_D`() { + val component = CraftDComponentKey.CHECK_BOX_COMPONENT + assertEquals(true, component.key.startsWith(CRAFT_D)) + } + + @Test + fun `given enum when calling toString then returns proper format`() { + val component = CraftDComponentKey.TEXT_VIEW_COMPONENT + assertNotNull(component.toString()) + assertEquals(true, component.toString().contains("TEXT_VIEW_COMPONENT")) + } + + @Test + fun `given all components when checking keys are unique then no duplicates exist`() { + val keys = CraftDComponentKey.values().map { it.key }.toSet() + assertEquals(3, keys.size) + } + + @Test + fun `given TEXT_VIEW_COMPONENT when comparing ordinal then returns zero`() { + val component = CraftDComponentKey.TEXT_VIEW_COMPONENT + assertEquals(0, component.ordinal) + } + + @Test + fun `given BUTTON_COMPONENT when comparing ordinal then returns one`() { + val component = CraftDComponentKey.BUTTON_COMPONENT + assertEquals(1, component.ordinal) + } + + @Test + fun `given CHECK_BOX_COMPONENT when comparing ordinal then returns two`() { + val component = CraftDComponentKey.CHECK_BOX_COMPONENT + assertEquals(2, component.ordinal) + } + +} \ No newline at end of file diff --git a/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/presentation/CraftDViewListenerTest.kt b/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/presentation/CraftDViewListenerTest.kt new file mode 100644 index 0000000..b54c4cc --- /dev/null +++ b/android_kmp/craftd-core/src/test/java/com/github/codandotv/craftd/androidcore/presentation/CraftDViewListenerTest.kt @@ -0,0 +1,169 @@ +```kotlin +package com.github.codandotv.craftd.androidcore.presentation + +import com.github.codandotv.craftd.androidcore.data.model.action.ActionProperties +import io.mockk.mockk +import io.mockk.verify +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@RunWith(JUnit4::class) +class CraftDViewListenerTest { + + @Test + fun `given valid ActionProperties when invoking CraftDViewListener then executes lambda`() { + val mockActionProperties = mockk() + var invoked = false + var capturedProperties: ActionProperties? = null + + val listener: CraftDViewListener = { properties -> + invoked = true + capturedProperties = properties + } + + listener.invoke(mockActionProperties) + + assert(invoked) + assert(capturedProperties == mockActionProperties) + } + + @Test + fun `given CraftDViewListener when assigning lambda then lambda is callable`() { + val listener: CraftDViewListener = { _ -> + // no-op + } + + assert(listener != null) + } + + @Test + fun `given multiple ActionProperties when invoking CraftDViewListener sequentially then all invocations execute`() { + val mockActionProperties1 = mockk() + val mockActionProperties2 = mockk() + val invokedProperties = mutableListOf() + + val listener: CraftDViewListener = { properties -> + invokedProperties.add(properties) + } + + listener.invoke(mockActionProperties1) + listener.invoke(mockActionProperties2) + + assert(invokedProperties.size == 2) + assert(invokedProperties[0] == mockActionProperties1) + assert(invokedProperties[1] == mockActionProperties2) + } + + @Test + fun `given CraftDViewListener with side effects when invoked then side effects are applied`() { + var sideEffectCounter = 0 + + val listener: CraftDViewListener = { _ -> + sideEffectCounter++ + } + + listener.invoke(mockk()) + listener.invoke(mockk()) + listener.invoke(mockk()) + + assert(sideEffectCounter == 3) + } + + @Test + fun `given CraftDViewListener type alias when creating instance then type is function with ActionProperties parameter`() { + val mockActionProperties = mockk() + val listener: CraftDViewListener = { capturedProps -> + assert(capturedProps is ActionProperties) + } + + listener.invoke(mockActionProperties) + } + + @Test + fun `given CraftDViewListener when listener returns Unit then no return value expected`() { + val listener: CraftDViewListener = { _ -> + Unit + } + + val result = listener.invoke(mockk()) + + assert(result == Unit) + } + + @Test + fun `given CraftDViewListener created from lambda when invoking with ActionProperties then properties are accessible in lambda scope`() { + val mockActionProperties = mockk() + var accessibleInScope = false + + val listener: CraftDViewListener = { properties -> + accessibleInScope = properties != null + } + + listener.invoke(mockActionProperties) + + assert(accessibleInScope) + } + + @Test + fun `given CraftDViewListener type alias then it is function type with single ActionProperties parameter`() { + val listener: CraftDViewListener = { _ -> } + val isFunction = listener is Function1<*, *> + + assert(isFunction) + } + + @Test + fun `given CraftDViewListener with exception in lambda when invoked then exception propagates`() { + val testException = IllegalStateException("Test exception") + val listener: CraftDViewListener = { _ -> + throw testException + } + + try { + listener.invoke(mockk()) + assert(false) { "Expected exception to be thrown" } + } catch (e: IllegalStateException) { + assert(e == testException) + } + } + + @Test + fun `given CraftDViewListener assigned to variable when calling through variable then listener executes`() { + val mockActionProperties = mockk() + var executed = false + + val listener: CraftDViewListener = { _ -> + executed = true + } + + val listenerVariable = listener + listenerVariable.invoke(mockActionProperties) + + assert(executed) + } + + @Test + fun `given two CraftDViewListener instances with same implementation when comparing then they are different instances`() { + val listener1: CraftDViewListener = { _ -> } + val listener2: CraftDViewListener = { _ -> } + + assert(listener1 !== listener2) + } + + @Test + fun `given CraftDViewListener when invoked multiple times with same ActionProperties instance then all invocations use same instance`() { + val mockActionProperties = mockk() + val capturedInstances = mutableListOf() + + val listener: CraftDViewListener = { properties -> + capturedInstances.add(properties) + } + + listener.invoke(mockActionProperties) + listener.invoke(mockActionProperties) + + assert(capturedInstances[0] === capturedInstances[1]) + } +} +``` \ No newline at end of file