diff --git a/firebase-dataconnect/connectors/src/androidTest/kotlin/com/google/firebase/dataconnect/connectors/demo/EnumIntegrationTest.kt b/firebase-dataconnect/connectors/src/androidTest/kotlin/com/google/firebase/dataconnect/connectors/demo/EnumIntegrationTest.kt index 73e373d6eb1..966f96f1231 100644 --- a/firebase-dataconnect/connectors/src/androidTest/kotlin/com/google/firebase/dataconnect/connectors/demo/EnumIntegrationTest.kt +++ b/firebase-dataconnect/connectors/src/androidTest/kotlin/com/google/firebase/dataconnect/connectors/demo/EnumIntegrationTest.kt @@ -25,13 +25,15 @@ import com.google.firebase.dataconnect.connectors.demo.testutil.DemoConnectorInt import com.google.firebase.dataconnect.testutil.property.arbitrary.dataConnect import com.google.firebase.dataconnect.testutil.property.arbitrary.threeValues import com.google.firebase.dataconnect.testutil.property.arbitrary.twoValues +import io.kotest.assertions.asClue import io.kotest.assertions.assertSoftly -import io.kotest.assertions.withClue import io.kotest.matchers.collections.shouldBeEmpty import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder import io.kotest.matchers.nulls.shouldBeNull +import io.kotest.matchers.nulls.shouldNotBeNull import io.kotest.matchers.shouldBe import io.kotest.property.Arb +import io.kotest.property.Exhaustive import io.kotest.property.arbitrary.bind import io.kotest.property.arbitrary.constant import io.kotest.property.arbitrary.enum @@ -39,6 +41,7 @@ import io.kotest.property.arbitrary.list import io.kotest.property.arbitrary.next import io.kotest.property.arbitrary.orNull import io.kotest.property.checkAll +import io.kotest.property.exhaustive.enum import java.util.UUID import kotlinx.coroutines.test.runTest import org.junit.Ignore @@ -52,36 +55,44 @@ class EnumIntegrationTest : DemoConnectorIntegrationTestBase() { @Test fun insertNonNullableNonNullKnownEnumValue() = runTest { - N5ekmae3jn.entries.forEach { enumValue -> + checkAll(Exhaustive.enum()) { enumValue -> val key = connector.enumNonNullableInsert.execute(enumValue).data.key - val queryResult = connector.enumNonNullableGetByKey.execute(key).data - withClue(queryResult) { queryResult.item?.value shouldBe Known(enumValue) } + val queryResult = connector.enumNonNullableGetByKey.execute(key) + queryResult.asClue { + val item = it.data.item.shouldNotBeNull() + item.value shouldBe Known(enumValue) + } } } @Test fun updateNonNullableEnumValue() = runTest { - checkAll(NUM_ITERATIONS, Arb.twoValues(Arb.enum())) { values -> - val (value1, value2) = values + checkAll(NUM_ITERATIONS, Arb.twoValues(Arb.enum())) { (value1, value2) -> val key = connector.enumNonNullableInsert.execute(value1).data.key val updateResult = connector.enumNonNullableUpdateByKey.execute(key) { value = value2 } - withClue(updateResult) { updateResult.data.key shouldBe key } - val queryResult = connector.enumNonNullableGetByKey.execute(key).data - withClue(queryResult) { queryResult.item?.value shouldBe Known(value2) } + updateResult.asClue { it.data.key shouldBe key } + val queryResult = connector.enumNonNullableGetByKey.execute(key) + queryResult.asClue { + val item = it.data.item.shouldNotBeNull() + item.value shouldBe Known(value2) + } } } @Test fun queryNonNullableNonNullUnknownEnumValue() = runTest { - N5ekmae3jn.entries.forEach { enumValue -> + checkAll(Exhaustive.enum()) { enumValue -> val key = connector.enumNonNullableInsert.execute(enumValue).data.key val queryRef = connector.enumNonNullableGetByKey .ref(key) .withDataDeserializer(EnumSubsetGetByKeyQuery.dataDeserializer) - val queryResult = queryRef.execute().data + val queryResult = queryRef.execute() val expectedEnumValue: EnumValue = enumValue.toN5ekmae3jnSubsetEnumValue() - withClue(queryResult) { queryResult.item?.value shouldBe expectedEnumValue } + queryResult.asClue { + val item = it.data.item.shouldNotBeNull() + item.value shouldBe expectedEnumValue + } } } @@ -91,35 +102,35 @@ class EnumIntegrationTest : DemoConnectorIntegrationTestBase() { val (tag, insertValue1, insertValue2, insertValue3, queryValue) = testData val insertResult = connector.enumNonNullableInsert3.execute(tag, insertValue1, insertValue2, insertValue3).data - val queryResult = connector.enumNonNullableGetAllByTagAndValue.execute(tag, queryValue).data + val queryResult = connector.enumNonNullableGetAllByTagAndValue.execute(tag, queryValue) val matchingIds = insertResult.idsForMatchingValues(testData) - withClue(queryResult) { - queryResult.items.map { it.id } shouldContainExactlyInAnyOrder matchingIds + queryResult.asClue { + it.data.items.map { item -> item.id } shouldContainExactlyInAnyOrder matchingIds } } } @Test fun queryNonNullableByUndefinedEnumValue() = runTest { - checkAll(NUM_ITERATIONS, Arb.insert3TestData(Arb.enum())) { testData -> - val (tag, insertValue1, insertValue2, insertValue3) = testData + checkAll(NUM_ITERATIONS, Arb.insert3TestData(Arb.enum())) { + (tag, insertValue1, insertValue2, insertValue3) -> val insertResult = connector.enumNonNullableInsert3.execute(tag, insertValue1, insertValue2, insertValue3).data - val queryResult = connector.enumNonNullableGetAllByTagAndMaybeValue.execute(tag).data - withClue(queryResult) { - queryResult.items.map { it.id } shouldContainExactlyInAnyOrder insertResult.ids + val queryResult = connector.enumNonNullableGetAllByTagAndMaybeValue.execute(tag) + queryResult.asClue { + it.data.items.map { item -> item.id } shouldContainExactlyInAnyOrder insertResult.ids } } } @Test fun queryNonNullableByNullEnumValue() = runTest { - checkAll(NUM_ITERATIONS, Arb.insert3TestData(Arb.enum())) { testData -> - val (tag, insertValue1, insertValue2, insertValue3) = testData + checkAll(NUM_ITERATIONS, Arb.insert3TestData(Arb.enum())) { + (tag, insertValue1, insertValue2, insertValue3) -> connector.enumNonNullableInsert3.execute(tag, insertValue1, insertValue2, insertValue3) val queryResult = - connector.enumNonNullableGetAllByTagAndMaybeValue.execute(tag) { value = null }.data - withClue(queryResult) { queryResult.items.shouldBeEmpty() } + connector.enumNonNullableGetAllByTagAndMaybeValue.execute(tag) { value = null } + queryResult.asClue { it.data.items.shouldBeEmpty() } } } @@ -131,10 +142,10 @@ class EnumIntegrationTest : DemoConnectorIntegrationTestBase() { val (tag, insertValue1, insertValue2, insertValue3) = testData val insertResult = connector.enumNonNullableInsert3.execute(tag, insertValue1, insertValue2, insertValue3).data - val queryResult = connector.enumNonNullableGetAllByTagAndDefaultValue.execute(tag).data + val queryResult = connector.enumNonNullableGetAllByTagAndDefaultValue.execute(tag) val matchingIds = insertResult.idsForMatchingValues(testData) - withClue(queryResult) { - queryResult.items.map { it.id } shouldContainExactlyInAnyOrder matchingIds + queryResult.asClue { + it.data.items.map { item -> item.id } shouldContainExactlyInAnyOrder matchingIds } } } @@ -145,33 +156,39 @@ class EnumIntegrationTest : DemoConnectorIntegrationTestBase() { @Test fun insertNullableNonNullEnumValue() = runTest { - N5ekmae3jn.entries.forEach { enumValue -> + checkAll(Exhaustive.enum()) { enumValue -> val key = connector.enumNullableInsert.execute { value = enumValue }.data.key - val queryResult = connector.enumNullableGetByKey.execute(key).data - withClue(queryResult) { queryResult.item?.value shouldBe Known(enumValue) } + val queryResult = connector.enumNullableGetByKey.execute(key) + queryResult.asClue { + val item = it.data.item.shouldNotBeNull() + item.value shouldBe Known(enumValue) + } } } @Test fun insertNullableNullEnumValue() = runTest { val key = connector.enumNullableInsert.execute { value = null }.data.key - val queryResult = connector.enumNullableGetByKey.execute(key).data - withClue(queryResult) { queryResult.item?.value.shouldBeNull() } + val queryResult = connector.enumNullableGetByKey.execute(key) + queryResult.asClue { + val item = it.data.item.shouldNotBeNull() + item.value.shouldBeNull() + } } @Test fun updateNullableEnumValue() = runTest { val enumArb = Arb.enum().orNull(nullProbability = 0.5) - checkAll(NUM_ITERATIONS, Arb.twoValues(enumArb)) { values -> - val (value1, value2) = values + checkAll(NUM_ITERATIONS, Arb.twoValues(enumArb)) { (value1, value2) -> val key = connector.enumNullableInsert.execute { value = value1 }.data.key connector.enumNullableUpdateByKey.execute(key) { value = value2 } - val queryResult = connector.enumNullableGetByKey.execute(key).data - withClue(queryResult) { + val queryResult = connector.enumNullableGetByKey.execute(key) + queryResult.asClue { + val item = it.data.item.shouldNotBeNull() if (value2 === null) { - queryResult.item?.value.shouldBeNull() + item.value.shouldBeNull() } else { - queryResult.item?.value shouldBe Known(value2) + item.value shouldBe Known(value2) } } } @@ -179,15 +196,18 @@ class EnumIntegrationTest : DemoConnectorIntegrationTestBase() { @Test fun queryNullableNonNullUnknownEnumValue() = runTest { - N5ekmae3jn.entries.forEach { enumValue -> + checkAll(Exhaustive.enum()) { enumValue -> val key = connector.enumNullableInsert.execute { value = enumValue }.data.key val queryRef = connector.enumNullableGetByKey .ref(key) .withDataDeserializer(EnumSubsetGetByKeyQuery.dataDeserializer) - val queryResult = queryRef.execute().data + val queryResult = queryRef.execute() val expectedEnumValue: EnumValue = enumValue.toN5ekmae3jnSubsetEnumValue() - withClue(queryResult) { queryResult.item?.value shouldBe expectedEnumValue } + queryResult.asClue { + val item = it.data.item.shouldNotBeNull() + item.value shouldBe expectedEnumValue + } } } @@ -205,10 +225,10 @@ class EnumIntegrationTest : DemoConnectorIntegrationTestBase() { } .data val queryResult = - connector.enumNullableGetAllByTagAndValue.execute(tag) { value = queryValue }.data + connector.enumNullableGetAllByTagAndValue.execute(tag) { value = queryValue } val matchingIds = insertResult.idsForMatchingValues(testData) - withClue(queryResult) { - queryResult.items.map { it.id } shouldContainExactlyInAnyOrder matchingIds + queryResult.asClue { + it.data.items.map { item -> item.id } shouldContainExactlyInAnyOrder matchingIds } } } @@ -216,8 +236,8 @@ class EnumIntegrationTest : DemoConnectorIntegrationTestBase() { @Test fun queryNullableByUndefinedEnumValue() = runTest { val enumArb = Arb.enum().orNull(nullProbability = 0.5) - checkAll(NUM_ITERATIONS, Arb.insert3TestData(enumArb)) { testData -> - val (tag, insertValue1, insertValue2, insertValue3) = testData + checkAll(NUM_ITERATIONS, Arb.insert3TestData(enumArb)) { + (tag, insertValue1, insertValue2, insertValue3) -> val insertResult = connector.enumNullableInsert3 .execute(tag) { @@ -226,9 +246,9 @@ class EnumIntegrationTest : DemoConnectorIntegrationTestBase() { value3 = insertValue3 } .data - val queryResult = connector.enumNullableGetAllByTagAndValue.execute(tag).data - withClue(queryResult) { - queryResult.items.map { it.id } shouldContainExactlyInAnyOrder insertResult.ids + val queryResult = connector.enumNullableGetAllByTagAndValue.execute(tag) + queryResult.asClue { + it.data.items.map { item -> item.id } shouldContainExactlyInAnyOrder insertResult.ids } } } @@ -247,10 +267,10 @@ class EnumIntegrationTest : DemoConnectorIntegrationTestBase() { value3 = insertValue3 } .data - val queryResult = connector.enumNullableGetAllByTagAndDefaultValue.execute(tag).data + val queryResult = connector.enumNullableGetAllByTagAndDefaultValue.execute(tag) val matchingIds = insertResult.idsForMatchingValues(testData) - withClue(queryResult) { - queryResult.items.map { it.id } shouldContainExactlyInAnyOrder matchingIds + queryResult.asClue { + it.data.items.map { item -> item.id } shouldContainExactlyInAnyOrder matchingIds } } } @@ -262,8 +282,11 @@ class EnumIntegrationTest : DemoConnectorIntegrationTestBase() { @Test fun insertEnumNonNullableTableDefault() = runTest { val key = connector.enumNonNullableTableDefaultInsert.execute().data.key - val queryResult = connector.enumNonNullableTableDefaultGetByKey.execute(key).data - withClue(queryResult) { queryResult.item?.value shouldBe Known(N5ekmae3jn.RGTB44C2M8) } + val queryResult = connector.enumNonNullableTableDefaultGetByKey.execute(key) + queryResult.asClue { + val item = it.data.item.shouldNotBeNull() + item.value shouldBe Known(N5ekmae3jn.RGTB44C2M8) + } } ////////////////////////////////////////////////////////////////////////////////////////////////// @@ -273,8 +296,11 @@ class EnumIntegrationTest : DemoConnectorIntegrationTestBase() { @Test fun insertEnumNullableTableDefault() = runTest { val key = connector.enumNullableTableDefaultInsert.execute().data.key - val queryResult = connector.enumNullableTableDefaultGetByKey.execute(key).data - withClue(queryResult) { queryResult.item?.value shouldBe Known(N5ekmae3jn.ZE6Z5778RV) } + val queryResult = connector.enumNullableTableDefaultGetByKey.execute(key) + queryResult.asClue { + val item = it.data.item.shouldNotBeNull() + item.value shouldBe Known(N5ekmae3jn.ZE6Z5778RV) + } } ////////////////////////////////////////////////////////////////////////////////////////////////// @@ -283,18 +309,19 @@ class EnumIntegrationTest : DemoConnectorIntegrationTestBase() { @Test fun insertNonNullableListOfNonNullable() = runTest { - val enumArb = Arb.enum() - checkAll(NUM_ITERATIONS, Arb.list(enumArb, 0..5)) { values -> + checkAll(NUM_ITERATIONS, Arb.list(Arb.enum(), 0..5)) { values -> val key = connector.enumNonNullableListOfNonNullableInsert.execute(values).data.key - val queryResult = connector.enumNonNullableListOfNonNullableGetByKey.execute(key).data - withClue(queryResult) { queryResult.item?.value shouldBe values.map(::Known) } + val queryResult = connector.enumNonNullableListOfNonNullableGetByKey.execute(key) + queryResult.asClue { + val item = it.data.item.shouldNotBeNull() + item.value shouldBe values.map(::Known) + } } } @Test fun queryNonNullableListOfNonNullableUnknownEnumValues() = runTest { - val enumArb = Arb.enum() - checkAll(NUM_ITERATIONS, Arb.list(enumArb, 10..20)) { values -> + checkAll(NUM_ITERATIONS, Arb.list(Arb.enum(), 10..20)) { values -> val key = connector.enumNonNullableListOfNonNullableInsert.execute(values).data.key val queryRef = connector.enumNonNullableListOfNonNullableGetByKey @@ -302,8 +329,11 @@ class EnumIntegrationTest : DemoConnectorIntegrationTestBase() { .withDataDeserializer(EnumSubsetListGetByKeyQuery.dataDeserializer) val expectedEnumValues: List> = values.map { it.toN5ekmae3jnSubsetEnumValue() } - val queryResult = queryRef.execute().data - withClue(queryResult) { queryResult.item?.value shouldBe expectedEnumValues } + val queryResult = queryRef.execute() + queryResult.asClue { + val item = it.data.item.shouldNotBeNull() + item.value shouldBe expectedEnumValues + } } } @@ -313,18 +343,19 @@ class EnumIntegrationTest : DemoConnectorIntegrationTestBase() { @Test fun insertNonNullableListOfNullable() = runTest { - val enumArb = Arb.enum() - checkAll(NUM_ITERATIONS, Arb.list(enumArb, 0..5)) { values -> + checkAll(NUM_ITERATIONS, Arb.list(Arb.enum(), 0..5)) { values -> val key = connector.enumNonNullableListOfNullableInsert.execute(values).data.key - val queryResult = connector.enumNonNullableListOfNullableGetByKey.execute(key).data - withClue(queryResult) { queryResult.item?.value shouldBe values.map(::Known) } + val queryResult = connector.enumNonNullableListOfNullableGetByKey.execute(key) + queryResult.asClue { + val item = it.data.item.shouldNotBeNull() + item.value shouldBe values.map(::Known) + } } } @Test fun queryNonNullableListOfNullableUnknownEnumValues() = runTest { - val enumArb = Arb.enum() - checkAll(NUM_ITERATIONS, Arb.list(enumArb, 10..20)) { values -> + checkAll(NUM_ITERATIONS, Arb.list(Arb.enum(), 10..20)) { values -> val key = connector.enumNonNullableListOfNullableInsert.execute(values).data.key val queryRef = connector.enumNonNullableListOfNullableGetByKey @@ -332,8 +363,11 @@ class EnumIntegrationTest : DemoConnectorIntegrationTestBase() { .withDataDeserializer(EnumSubsetListGetByKeyQuery.dataDeserializer) val expectedEnumValues: List> = values.map { it.toN5ekmae3jnSubsetEnumValue() } - val queryResult = queryRef.execute().data - withClue(queryResult) { queryResult.item?.value shouldBe expectedEnumValues } + val queryResult = queryRef.execute() + queryResult.asClue { + val item = it.data.item.shouldNotBeNull() + item.value shouldBe expectedEnumValues + } } } @@ -343,25 +377,29 @@ class EnumIntegrationTest : DemoConnectorIntegrationTestBase() { @Test fun insertNullableListOfNonNullable_NonNullList() = runTest { - val enumArb = Arb.enum() - checkAll(NUM_ITERATIONS, Arb.list(enumArb, 0..5)) { values -> + checkAll(NUM_ITERATIONS, Arb.list(Arb.enum(), 0..5)) { values -> val key = connector.enumNullableListOfNonNullableInsert.execute { value = values }.data.key - val queryResult = connector.enumNullableListOfNonNullableGetByKey.execute(key).data - withClue(queryResult) { queryResult.item?.value shouldBe values.map(::Known) } + val queryResult = connector.enumNullableListOfNonNullableGetByKey.execute(key) + queryResult.asClue { + val item = it.data.item.shouldNotBeNull() + item.value shouldBe values.map(::Known) + } } } @Test fun insertNullableListOfNonNullable_NullList() = runTest { val key = connector.enumNullableListOfNonNullableInsert.execute { value = null }.data.key - val queryResult = connector.enumNullableListOfNonNullableGetByKey.execute(key).data - withClue(queryResult) { queryResult.item?.value.shouldBeNull() } + val queryResult = connector.enumNullableListOfNonNullableGetByKey.execute(key) + queryResult.asClue { + val item = it.data.item.shouldNotBeNull() + item.value.shouldBeNull() + } } @Test fun queryNullableListOfNonNullableUnknownEnumValues() = runTest { - val enumArb = Arb.enum() - checkAll(NUM_ITERATIONS, Arb.list(enumArb, 10..20)) { values -> + checkAll(NUM_ITERATIONS, Arb.list(Arb.enum(), 10..20)) { values -> val key = connector.enumNullableListOfNonNullableInsert.execute { value = values }.data.key val queryRef = connector.enumNullableListOfNonNullableGetByKey @@ -369,8 +407,11 @@ class EnumIntegrationTest : DemoConnectorIntegrationTestBase() { .withDataDeserializer(EnumSubsetListGetByKeyQuery.dataDeserializer) val expectedEnumValues: List> = values.map { it.toN5ekmae3jnSubsetEnumValue() } - val queryResult = queryRef.execute().data - withClue(queryResult) { queryResult.item?.value shouldBe expectedEnumValues } + val queryResult = queryRef.execute() + queryResult.asClue { + val item = it.data.item.shouldNotBeNull() + item.value shouldBe expectedEnumValues + } } } @@ -380,25 +421,29 @@ class EnumIntegrationTest : DemoConnectorIntegrationTestBase() { @Test fun insertNullableListOfNullable_NonNullList() = runTest { - val enumArb = Arb.enum() - checkAll(NUM_ITERATIONS, Arb.list(enumArb, 0..5)) { values -> + checkAll(NUM_ITERATIONS, Arb.list(Arb.enum(), 0..5)) { values -> val key = connector.enumNullableListOfNullableInsert.execute { value = values }.data.key - val queryResult = connector.enumNullableListOfNullableGetByKey.execute(key).data - withClue(queryResult) { queryResult.item?.value shouldBe values.map(::Known) } + val queryResult = connector.enumNullableListOfNullableGetByKey.execute(key) + queryResult.asClue { + val item = it.data.item.shouldNotBeNull() + item.value shouldBe values.map(::Known) + } } } @Test fun insertNullableListOfNullable_NullList() = runTest { val key = connector.enumNullableListOfNullableInsert.execute { value = null }.data.key - val queryResult = connector.enumNullableListOfNullableGetByKey.execute(key).data - withClue(queryResult) { queryResult.item?.value.shouldBeNull() } + val queryResult = connector.enumNullableListOfNullableGetByKey.execute(key) + queryResult.asClue { + val item = it.data.item.shouldNotBeNull() + item.value.shouldBeNull() + } } @Test fun queryNullableListOfNullableUnknownEnumValues() = runTest { - val enumArb = Arb.enum() - checkAll(NUM_ITERATIONS, Arb.list(enumArb, 10..20)) { values -> + checkAll(NUM_ITERATIONS, Arb.list(Arb.enum(), 10..20)) { values -> val key = connector.enumNullableListOfNullableInsert.execute { value = values }.data.key val queryRef = connector.enumNullableListOfNullableGetByKey @@ -406,8 +451,11 @@ class EnumIntegrationTest : DemoConnectorIntegrationTestBase() { .withDataDeserializer(EnumSubsetListGetByKeyQuery.dataDeserializer) val expectedEnumValues: List> = values.map { it.toN5ekmae3jnSubsetEnumValue() } - val queryResult = queryRef.execute().data - withClue(queryResult) { queryResult.item?.value shouldBe expectedEnumValues } + val queryResult = queryRef.execute() + queryResult.asClue { + val item = it.data.item.shouldNotBeNull() + item.value shouldBe expectedEnumValues + } } } @@ -419,8 +467,11 @@ class EnumIntegrationTest : DemoConnectorIntegrationTestBase() { fun enumKotlinKeywords() = runTest { Break.entries.forEach { enumValue -> val key = connector.enumKotlinKeywordsInsert.execute(enumValue).data.key - val queryResult = connector.enumKotlinKeywordsGetByKey.execute(key).data - withClue(queryResult) { queryResult.item?.value shouldBe Known(enumValue) } + val queryResult = connector.enumKotlinKeywordsGetByKey.execute(key) + queryResult.asClue { + val item = it.data.item.shouldNotBeNull() + item.value shouldBe Known(enumValue) + } } } @@ -430,12 +481,15 @@ class EnumIntegrationTest : DemoConnectorIntegrationTestBase() { @Test fun enumAsPrimaryKey() = runTest { - N5ekmae3jn.entries.forEach { enumValue -> - val tagValue = Arb.dataConnect.tag().next(rs) + checkAll(Exhaustive.enum()) { enumValue -> + val tagValue = Arb.dataConnect.tag().next(randomSource()) val key = connector.enumKeyInsert.execute(enumValue) { tag = tagValue }.data.key - withClue(key) { key.enumValue shouldBe enumValue } - val queryResult = connector.enumKeyGetByKey.execute(key).data - withClue(queryResult) { queryResult.item?.tag shouldBe tagValue } + key.asClue { it.enumValue shouldBe enumValue } + val queryResult = connector.enumKeyGetByKey.execute(key) + queryResult.asClue { + val item = it.data.item.shouldNotBeNull() + item.tag shouldBe tagValue + } } } @@ -452,11 +506,12 @@ class EnumIntegrationTest : DemoConnectorIntegrationTestBase() { fun multipleEnumColumns() = runTest { checkAll(NUM_ITERATIONS, Arb.enum(), Arb.enum()) { enum1, enum2 -> val key = connector.multipleEnumColumnsInsert.execute(enum1, enum2).data.key - val queryResult = connector.multipleEnumColumnsGetByKey.execute(key).data - withClue(queryResult) { + val queryResult = connector.multipleEnumColumnsGetByKey.execute(key) + queryResult.asClue { + val item = it.data.item.shouldNotBeNull() assertSoftly { - queryResult.item?.enum1 shouldBe Known(enum1) - queryResult.item?.enum2 shouldBe Known(enum2) + item.enum1 shouldBe Known(enum1) + item.enum2 shouldBe Known(enum2) } } } @@ -479,6 +534,7 @@ class EnumIntegrationTest : DemoConnectorIntegrationTestBase() { /** The default number of iterations to use in property-based tests. */ const val NUM_ITERATIONS = 10 + @Suppress("SpellCheckingInspection") fun N5ekmae3jn.toN5ekmae3jnSubsetOrNull(): N5ekmae3jnSubset? = when (this) { N5ekmae3jn.DPSKD6HR3A -> N5ekmae3jnSubset.DPSKD6HR3A @@ -487,6 +543,7 @@ class EnumIntegrationTest : DemoConnectorIntegrationTestBase() { else -> null } + @Suppress("SpellCheckingInspection") fun N5ekmae3jn.toN5ekmae3jnSubsetEnumValue(): EnumValue { return Known(toN5ekmae3jnSubsetOrNull() ?: return Unknown(name)) } diff --git a/firebase-dataconnect/src/androidTest/kotlin/com/google/firebase/dataconnect/EnumIntegrationTest.kt b/firebase-dataconnect/src/androidTest/kotlin/com/google/firebase/dataconnect/EnumIntegrationTest.kt index 31ffa642033..dfa0676f8c1 100644 --- a/firebase-dataconnect/src/androidTest/kotlin/com/google/firebase/dataconnect/EnumIntegrationTest.kt +++ b/firebase-dataconnect/src/androidTest/kotlin/com/google/firebase/dataconnect/EnumIntegrationTest.kt @@ -18,20 +18,26 @@ package com.google.firebase.dataconnect import com.google.firebase.dataconnect.testutil.DataConnectIntegrationTestBase import com.google.firebase.dataconnect.testutil.property.arbitrary.dataConnect -import com.google.firebase.dataconnect.testutil.withNullAppended +import com.google.firebase.dataconnect.testutil.property.arbitrary.enumWithNull +import com.google.firebase.dataconnect.testutil.property.arbitrary.fourValues +import com.google.firebase.dataconnect.testutil.property.arbitrary.listContainingNull +import com.google.firebase.dataconnect.testutil.property.arbitrary.threeValues +import com.google.firebase.dataconnect.testutil.property.arbitrary.twoValues +import io.kotest.assertions.asClue import io.kotest.assertions.assertSoftly import io.kotest.assertions.throwables.shouldThrow -import io.kotest.assertions.withClue import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder import io.kotest.matchers.nulls.shouldBeNull +import io.kotest.matchers.nulls.shouldNotBeNull import io.kotest.matchers.shouldBe import io.kotest.property.Arb +import io.kotest.property.Exhaustive import io.kotest.property.arbitrary.enum -import io.kotest.property.arbitrary.filter import io.kotest.property.arbitrary.list import io.kotest.property.arbitrary.next import io.kotest.property.arbitrary.orNull import io.kotest.property.checkAll +import io.kotest.property.exhaustive.enum import kotlinx.coroutines.test.runTest import kotlinx.serialization.Serializable import kotlinx.serialization.serializer @@ -50,12 +56,15 @@ class EnumIntegrationTest : DataConnectIntegrationTestBase() { @Test fun insertNonNullableNonNullEnumValue() = runTest { - N5ekmae3jn.entries.forEach { enumValue -> + checkAll(Exhaustive.enum()) { enumValue -> val insertVariables = InsertNonNullableVariables(enumValue) val key = dataConnect.mutation(insertVariables).execute().data.key val queryVariables = GetNonNullableByKeyVariables(key) - val queryResult = dataConnect.query(queryVariables).execute().data - withClue(queryResult) { queryResult?.item?.value shouldBe enumValue } + val queryResult = dataConnect.query(queryVariables).execute() + queryResult.asClue { + val data = it.data.shouldNotBeNull() + data.item.value shouldBe enumValue + } } } @@ -68,86 +77,77 @@ class EnumIntegrationTest : DataConnectIntegrationTestBase() { @Test fun updateNonNullableEnumValue() = runTest { - val enumArb = Arb.enum() - checkAll(NUM_ITERATIONS, enumArb, enumArb) { value1, value2 -> + checkAll(NUM_ITERATIONS, Arb.twoValues(Arb.enum())) { (value1, value2) -> val insertVariables = InsertNonNullableVariables(value1) val key = dataConnect.mutation(insertVariables).execute().data.key val updateVariables = UpdateNonNullableVariables(key, value2) dataConnect.mutation(updateVariables).execute() val queryVariables = GetNonNullableByKeyVariables(key) - val queryResult = dataConnect.query(queryVariables).execute().data - withClue(queryResult) { queryResult?.item?.value shouldBe value2 } + val queryResult = dataConnect.query(queryVariables).execute() + queryResult.asClue { it.data.item.value shouldBe value2 } } } @Test fun queryNonNullableEnumValue() = runTest { - N5ekmae3jn.entries.forEach { enumValue -> + checkAll(Exhaustive.enum()) { enumValue -> val insertVariables = InsertNonNullableVariables(enumValue) val key = dataConnect.mutation(insertVariables).execute().data.key val queryVariables = GetNonNullableByKeyVariables(key) val queryRef = dataConnect.query(queryVariables) - val queryResult = queryRef.execute().data - withClue(queryResult) { queryResult?.item?.value shouldBe enumValue } + val queryResult = queryRef.execute() + queryResult.asClue { it.data.item.value shouldBe enumValue } } } @Test fun queryNonNullableByNonNullEnumValue() = runTest { - val enumArb = Arb.enum() - checkAll(NUM_ITERATIONS, enumArb, enumArb, enumArb, enumArb, Arb.dataConnect.tag()) { - value1, - value2, - value3, - value4, + checkAll(NUM_ITERATIONS, Arb.fourValues(Arb.enum()), Arb.dataConnect.tag()) { + values, tag -> + val (value1, value2, value3, value4) = values val insertVariables = Insert3NonNullableVariables(tag, value1, value2, value3) val insertResult = dataConnect.mutation(insertVariables).execute().data val queryVariables = GetNonNullableByTagAndValueVariables(tag, value4) - val queryResult = dataConnect.query(queryVariables).execute().data + val queryResult = dataConnect.query(queryVariables).execute() val matchingKeys = insertResult.keysForMatchingValues(value4, insertVariables) - withClue(queryResult) { queryResult.items shouldContainExactlyInAnyOrder matchingKeys } + queryResult.asClue { it.data.items shouldContainExactlyInAnyOrder matchingKeys } } } @Test fun queryNonNullableByUndefinedEnumValue() = runTest { - val enumArb = Arb.enum() - checkAll(1, enumArb, enumArb, enumArb, Arb.dataConnect.tag()) { value1, value2, value3, tag -> - val insertVariables = Insert3NonNullableVariables(tag, value1, value2, value3) - dataConnect.mutation(insertVariables).execute().data - val queryVariables = GetNonNullableByTagAndValueVariables(tag, OptionalVariable.Undefined) - val queryRef = dataConnect.query(queryVariables) - shouldThrow { queryRef.execute() } - } + val tag = Arb.dataConnect.tag().next(rs) + val (value1, value2, value3) = Arb.threeValues(Arb.enum()).next(rs) + val insertVariables = Insert3NonNullableVariables(tag, value1, value2, value3) + dataConnect.mutation(insertVariables).execute().data + val queryVariables = GetNonNullableByTagAndValueVariables(tag, OptionalVariable.Undefined) + val queryRef = dataConnect.query(queryVariables) + shouldThrow { queryRef.execute() } } @Test fun queryNonNullableByNullEnumValue() = runTest { - val enumArb = Arb.enum() - checkAll(1, enumArb, enumArb, enumArb, Arb.dataConnect.tag()) { value1, value2, value3, tag -> - val insertVariables = Insert3NonNullableVariables(tag, value1, value2, value3) - dataConnect.mutation(insertVariables).execute() - val queryVariables = GetNonNullableByTagAndValueVariables(tag, OptionalVariable.Value(null)) - val queryRef = dataConnect.query(queryVariables) - shouldThrow { queryRef.execute() } - } + val tag = Arb.dataConnect.tag().next(rs) + val (value1, value2, value3) = Arb.threeValues(Arb.enum()).next(rs) + val insertVariables = Insert3NonNullableVariables(tag, value1, value2, value3) + dataConnect.mutation(insertVariables).execute() + val queryVariables = GetNonNullableByTagAndValueVariables(tag, OptionalVariable.Value(null)) + val queryRef = dataConnect.query(queryVariables) + shouldThrow { queryRef.execute() } } @Test fun queryNonNullableByDefaultEnumValue() = runTest { - val enumArb = Arb.enum() - checkAll(NUM_ITERATIONS, enumArb, enumArb, enumArb, Arb.dataConnect.tag()) { - value1, - value2, - value3, + checkAll(NUM_ITERATIONS, Arb.threeValues(Arb.enum()), Arb.dataConnect.tag()) { + (value1, value2, value3), tag -> val insertVariables = Insert3NonNullableVariables(tag, value1, value2, value3) val insertResult = dataConnect.mutation(insertVariables).execute().data val queryVariables = GetNonNullableByTagAndDefaultValueVariables(tag) - val queryResult = dataConnect.query(queryVariables).execute().data + val queryResult = dataConnect.query(queryVariables).execute() val matchingKeys = insertResult.keysForMatchingValues(N5ekmae3jn.XGWGVMYTHJ, insertVariables) - withClue(queryResult) { queryResult.items shouldContainExactlyInAnyOrder matchingKeys } + queryResult.asClue { it.data.items shouldContainExactlyInAnyOrder matchingKeys } } } @@ -179,7 +179,7 @@ class EnumIntegrationTest : DataConnectIntegrationTestBase() { private data class UpdateNonNullableVariables(val key: RowKey, val value: N5ekmae3jn) @Serializable - private data class GetNonNullableByKeyData(val item: Item?) { + private data class GetNonNullableByKeyData(val item: Item) { @Serializable data class Item(val value: N5ekmae3jn) } @@ -189,12 +189,12 @@ class EnumIntegrationTest : DataConnectIntegrationTestBase() { @Test fun insertNullableNonNullEnumValue() = runTest { - N5ekmae3jn.entries.forEach { enumValue -> + checkAll(Exhaustive.enum()) { enumValue -> val insertVariables = InsertNullableVariables(enumValue) val key = dataConnect.mutation(insertVariables).execute().data.key val queryVariables = GetNullableByKeyVariables(key) - val queryResult = dataConnect.query(queryVariables).execute().data - withClue(queryResult) { queryResult?.item?.value shouldBe enumValue } + val queryResult = dataConnect.query(queryVariables).execute() + queryResult.asClue { it.data.item.value shouldBe enumValue } } } @@ -203,84 +203,77 @@ class EnumIntegrationTest : DataConnectIntegrationTestBase() { val insertVariables = InsertNullableVariables(null) val key = dataConnect.mutation(insertVariables).execute().data.key val queryVariables = GetNullableByKeyVariables(key) - val queryResult = dataConnect.query(queryVariables).execute().data - withClue(queryResult) { queryResult?.item?.value.shouldBeNull() } + val queryResult = dataConnect.query(queryVariables).execute() + queryResult.asClue { it.data.item.value.shouldBeNull() } } @Test fun updateNullableEnumValue() = runTest { val enumArb = Arb.enum().orNull(nullProbability = 0.5) - checkAll(NUM_ITERATIONS, enumArb, enumArb) { value1, value2 -> + checkAll(NUM_ITERATIONS, Arb.twoValues(enumArb)) { (value1, value2) -> val insertVariables = InsertNullableVariables(value1) val key = dataConnect.mutation(insertVariables).execute().data.key val updateVariables = UpdateNullableVariables(key, value2) dataConnect.mutation(updateVariables).execute() val queryVariables = GetNullableByKeyVariables(key) - val queryResult = dataConnect.query(queryVariables).execute().data - withClue(queryResult) { queryResult?.item?.value shouldBe value2 } + val queryResult = dataConnect.query(queryVariables).execute() + queryResult.asClue { it.data.item.value shouldBe value2 } } } @Test fun queryNullableEnumValue() = runTest { - N5ekmae3jn.entries.withNullAppended().forEach { enumValue -> + checkAll(Exhaustive.enumWithNull()) { enumValue -> val insertVariables = InsertNullableVariables(enumValue) val key = dataConnect.mutation(insertVariables).execute().data.key val queryVariables = GetNullableByKeyVariables(key) val queryRef = dataConnect.query(queryVariables) - val queryResult = queryRef.execute().data - withClue(queryResult) { queryResult?.item?.value shouldBe enumValue } + val queryResult = queryRef.execute() + queryResult.asClue { it.data.item.value shouldBe enumValue } } } @Test fun queryNullableByEnumValue() = runTest { val enumArb = Arb.enum().orNull(nullProbability = 0.33) - checkAll(NUM_ITERATIONS, enumArb, enumArb, enumArb, enumArb, Arb.dataConnect.tag()) { - value1, - value2, - value3, - value4, + checkAll(NUM_ITERATIONS, Arb.fourValues(enumArb), Arb.dataConnect.tag()) { + (value1, value2, value3, value4), tag -> val insertVariables = Insert3NullableVariables(tag, value1, value2, value3) val insertResult = dataConnect.mutation(insertVariables).execute().data val queryVariables = GetNullableByTagAndValueVariables(tag, value4) - val queryResult = dataConnect.query(queryVariables).execute().data + val queryResult = dataConnect.query(queryVariables).execute() val matchingKeys = insertResult.keysForMatchingValues(value4, insertVariables) - withClue(queryResult) { queryResult.items shouldContainExactlyInAnyOrder matchingKeys } + queryResult.asClue { it.data.items shouldContainExactlyInAnyOrder matchingKeys } } } @Test fun queryNullableByUndefinedEnumValue() = runTest { val enumArb = Arb.enum().orNull(nullProbability = 0.5) - checkAll(NUM_ITERATIONS, enumArb, enumArb, enumArb, Arb.dataConnect.tag()) { - value1, - value2, - value3, + checkAll(NUM_ITERATIONS, Arb.threeValues(enumArb), Arb.dataConnect.tag()) { + (value1, value2, value3), tag -> val insertVariables = Insert3NullableVariables(tag, value1, value2, value3) val insertResult = dataConnect.mutation(insertVariables).execute().data val queryVariables = GetNullableByTagAndValueVariables(tag, OptionalVariable.Undefined) - val queryResult = dataConnect.query(queryVariables).execute().data - withClue(queryResult) { queryResult.items shouldContainExactlyInAnyOrder insertResult.keys } + val queryResult = dataConnect.query(queryVariables).execute() + queryResult.asClue { it.data.items shouldContainExactlyInAnyOrder insertResult.keys } } } @Test fun queryNullableByDefaultEnumValue() = runTest { val enumArb = Arb.enum().orNull(nullProbability = 0.33) - checkAll(NUM_ITERATIONS, enumArb, enumArb, enumArb, Arb.dataConnect.tag()) { - value1, - value2, - value3, + checkAll(NUM_ITERATIONS, Arb.threeValues(enumArb), Arb.dataConnect.tag()) { + (value1, value2, value3), tag -> val insertVariables = Insert3NullableVariables(tag, value1, value2, value3) val insertResult = dataConnect.mutation(insertVariables).execute().data val queryVariables = GetNullableByTagAndDefaultValueVariables(tag) - val queryResult = dataConnect.query(queryVariables).execute().data + val queryResult = dataConnect.query(queryVariables).execute() val matchingKeys = insertResult.keysForMatchingValues(N5ekmae3jn.QJX7C7RD5T, insertVariables) - withClue(queryResult) { queryResult.items shouldContainExactlyInAnyOrder matchingKeys } + queryResult.asClue { it.data.items shouldContainExactlyInAnyOrder matchingKeys } } } @@ -309,7 +302,7 @@ class EnumIntegrationTest : DataConnectIntegrationTestBase() { @Serializable private data class UpdateNullableVariables(val key: RowKey, val value: N5ekmae3jn?) @Serializable - private data class GetNullableByKeyData(val item: Item?) { + private data class GetNullableByKeyData(val item: Item) { @Serializable data class Item(val value: N5ekmae3jn?) } @@ -319,11 +312,10 @@ class EnumIntegrationTest : DataConnectIntegrationTestBase() { @Test fun insertEnumNonNullableTableDefault() = runTest { - val insertVariables = InsertNonNullableTableDefaultVariables - val key = dataConnect.mutation(insertVariables).execute().data.key + val key = dataConnect.mutation(InsertNonNullableTableDefaultVariables).execute().data.key val queryVariables = GetNonNullableTableDefaultByKeyVariables(key) - val queryResult = dataConnect.query(queryVariables).execute().data - withClue(queryResult) { queryResult?.item?.value shouldBe N5ekmae3jn.RGTB44C2M8 } + val queryResult = dataConnect.query(queryVariables).execute() + queryResult.asClue { it.data.item.value shouldBe N5ekmae3jn.RGTB44C2M8 } } @Serializable private object InsertNonNullableTableDefaultVariables @@ -336,11 +328,10 @@ class EnumIntegrationTest : DataConnectIntegrationTestBase() { @Test fun insertEnumNullableTableDefault() = runTest { - val insertVariables = InsertNullableTableDefaultVariables - val key = dataConnect.mutation(insertVariables).execute().data.key + val key = dataConnect.mutation(InsertNullableTableDefaultVariables).execute().data.key val queryVariables = GetNullableTableDefaultByKeyVariables(key) - val queryResult = dataConnect.query(queryVariables).execute().data - withClue(queryResult) { queryResult?.item?.value shouldBe N5ekmae3jn.ZE6Z5778RV } + val queryResult = dataConnect.query(queryVariables).execute() + queryResult.asClue { it.data.item.value shouldBe N5ekmae3jn.ZE6Z5778RV } } @Serializable private object InsertNullableTableDefaultVariables @@ -353,13 +344,12 @@ class EnumIntegrationTest : DataConnectIntegrationTestBase() { @Test fun insertNonNullableListOfNonNullable_ListContainingNonNullValues() = runTest { - val enumArb = Arb.enum() - checkAll(NUM_ITERATIONS, Arb.list(enumArb, 0..5)) { value -> - val insertVariables = InsertNonNullableListOfNonNullableVariables(value) + checkAll(NUM_ITERATIONS, Arb.list(Arb.enum(), 0..5)) { values -> + val insertVariables = InsertNonNullableListOfNonNullableVariables(values) val key = dataConnect.mutation(insertVariables).execute().data.key val queryVariables = GetNonNullableListOfNonNullableByKeyVariables(key) - val queryResult = dataConnect.query(queryVariables).execute().data - withClue(queryResult) { queryResult?.item?.value shouldBe value } + val queryResult = dataConnect.query(queryVariables).execute() + queryResult.asClue { it.data.item.value shouldBe values } } } @@ -372,26 +362,24 @@ class EnumIntegrationTest : DataConnectIntegrationTestBase() { @Test fun insertNonNullableListOfNonNullable_ListContainingNull() = runTest { - val enumArb = Arb.enum().orNull(nullProbability = 0.5) - checkAll(NUM_ITERATIONS, Arb.list(enumArb, 0..9).filter { it.contains(null) }) { value -> - val insertVariables = InsertNonNullableListOfNonNullableVariables(value) + checkAll(NUM_ITERATIONS, Arb.listContainingNull(Arb.enum(), 1..9)) { values -> + val insertVariables = InsertNonNullableListOfNonNullableVariables(values) val key = dataConnect.mutation(insertVariables).execute().data.key val queryVariables = GetNonNullableListOfNonNullableByKeyVariables(key) - val queryResult = dataConnect.query(queryVariables).execute().data - withClue(queryResult) { queryResult?.item?.value shouldBe value } + val queryResult = dataConnect.query(queryVariables).execute() + queryResult.asClue { it.data.item.value shouldBe values } } } @Test fun queryNonNullableListOfNonNullableEnumValues() = runTest { - val enumArb = Arb.enum() - checkAll(NUM_ITERATIONS, Arb.list(enumArb, 10..20)) { values -> + checkAll(NUM_ITERATIONS, Arb.list(Arb.enum(), 10..20)) { values -> val insertVariables = InsertNonNullableListOfNonNullableVariables(values) val key = dataConnect.mutation(insertVariables).execute().data.key val queryVariables = GetNonNullableListOfNonNullableByKeyVariables(key) val queryRef = dataConnect.query(queryVariables) - val queryResult = queryRef.execute().data - withClue(queryResult) { queryResult?.item?.value shouldBe values } + val queryResult = queryRef.execute() + queryResult.asClue { it.data.item.value shouldBe values } } } @@ -405,7 +393,7 @@ class EnumIntegrationTest : DataConnectIntegrationTestBase() { @Serializable private data class GetNonNullableListOfNonNullableByKeyVariables(val key: RowKey) @Serializable - private data class GetNonNullableListOfNonNullableByKeyData(val item: Item?) { + private data class GetNonNullableListOfNonNullableByKeyData(val item: Item) { @Serializable data class Item(val value: List) } @@ -415,25 +403,23 @@ class EnumIntegrationTest : DataConnectIntegrationTestBase() { @Test fun insertNonNullableListOfNullable_ListNotContainingNull() = runTest { - val enumArb = Arb.enum() - checkAll(NUM_ITERATIONS, Arb.list(enumArb, 0..5)) { value -> - val insertVariables = InsertNonNullableListOfNullableVariables(value) + checkAll(NUM_ITERATIONS, Arb.list(Arb.enum(), 0..5)) { values -> + val insertVariables = InsertNonNullableListOfNullableVariables(values) val key = dataConnect.mutation(insertVariables).execute().data.key val queryVariables = GetNonNullableListOfNullableByKeyVariables(key) - val queryResult = dataConnect.query(queryVariables).execute().data - withClue(queryResult) { queryResult?.item?.value shouldBe value } + val queryResult = dataConnect.query(queryVariables).execute() + queryResult.asClue { it.data.item.value shouldBe values } } } @Test fun insertNonNullableListOfNullable_ListContainingNull() = runTest { - val enumArb = Arb.enum().orNull(nullProbability = 0.5) - checkAll(NUM_ITERATIONS, Arb.list(enumArb, 0..9).filter { it.contains(null) }) { value -> - val insertVariables = InsertNonNullableListOfNullableVariables(value) + checkAll(NUM_ITERATIONS, Arb.listContainingNull(Arb.enum(), 1..5)) { values -> + val insertVariables = InsertNonNullableListOfNullableVariables(values) val key = dataConnect.mutation(insertVariables).execute().data.key val queryVariables = GetNonNullableListOfNullableByKeyVariables(key) - val queryResult = dataConnect.query(queryVariables).execute().data - withClue(queryResult) { queryResult?.item?.value shouldBe value } + val queryResult = dataConnect.query(queryVariables).execute() + queryResult.asClue { it.data.item.value shouldBe values } } } @@ -452,8 +438,8 @@ class EnumIntegrationTest : DataConnectIntegrationTestBase() { val key = dataConnect.mutation(insertVariables).execute().data.key val queryVariables = GetNonNullableListOfNullableByKeyVariables(key) val queryRef = dataConnect.query(queryVariables) - val queryResult = queryRef.execute().data - withClue(queryResult) { queryResult?.item?.value shouldBe values } + val queryResult = queryRef.execute() + queryResult.asClue { it.data.item.value shouldBe values } } } @@ -467,7 +453,7 @@ class EnumIntegrationTest : DataConnectIntegrationTestBase() { @Serializable private data class GetNonNullableListOfNullableByKeyVariables(val key: RowKey) @Serializable - private data class GetNonNullableListOfNullableByKeyData(val item: Item?) { + private data class GetNonNullableListOfNullableByKeyData(val item: Item) { @Serializable data class Item(val value: List) } @@ -477,13 +463,12 @@ class EnumIntegrationTest : DataConnectIntegrationTestBase() { @Test fun insertNullableListOfNonNullable_ListContainingNonNullValues() = runTest { - val enumArb = Arb.enum() - checkAll(NUM_ITERATIONS, Arb.list(enumArb, 0..5)) { value -> - val insertVariables = InsertNullableListOfNonNullableVariables(value) + checkAll(NUM_ITERATIONS, Arb.list(Arb.enum(), 0..5)) { values -> + val insertVariables = InsertNullableListOfNonNullableVariables(values) val key = dataConnect.mutation(insertVariables).execute().data.key val queryVariables = GetNullableListOfNonNullableByKeyVariables(key) - val queryResult = dataConnect.query(queryVariables).execute().data - withClue(queryResult) { queryResult?.item?.value shouldBe value } + val queryResult = dataConnect.query(queryVariables).execute() + queryResult.asClue { it.data.item.value shouldBe values } } } @@ -492,19 +477,18 @@ class EnumIntegrationTest : DataConnectIntegrationTestBase() { val insertVariables = InsertNullableListOfNonNullableVariables(null) val key = dataConnect.mutation(insertVariables).execute().data.key val queryVariables = GetNullableListOfNonNullableByKeyVariables(key) - val queryResult = dataConnect.query(queryVariables).execute().data - withClue(queryResult) { queryResult?.item?.value.shouldBeNull() } + val queryResult = dataConnect.query(queryVariables).execute() + queryResult.asClue { it.data.item.value.shouldBeNull() } } @Test fun insertNullableListOfNonNullable_ListContainingNull() = runTest { - val enumArb = Arb.enum().orNull(nullProbability = 0.5) - checkAll(NUM_ITERATIONS, Arb.list(enumArb, 0..9).filter { it.contains(null) }) { value -> - val insertVariables = InsertNullableListOfNonNullableVariables(value) + checkAll(NUM_ITERATIONS, Arb.listContainingNull(Arb.enum(), 1..9)) { values -> + val insertVariables = InsertNullableListOfNonNullableVariables(values) val key = dataConnect.mutation(insertVariables).execute().data.key val queryVariables = GetNullableListOfNonNullableByKeyVariables(key) - val queryResult = dataConnect.query(queryVariables).execute().data - withClue(queryResult) { queryResult?.item?.value shouldBe value } + val queryResult = dataConnect.query(queryVariables).execute() + queryResult.asClue { it.data.item.value shouldBe values } } } @@ -516,8 +500,8 @@ class EnumIntegrationTest : DataConnectIntegrationTestBase() { val key = dataConnect.mutation(insertVariables).execute().data.key val queryVariables = GetNullableListOfNonNullableByKeyVariables(key) val queryRef = dataConnect.query(queryVariables) - val queryResult = queryRef.execute().data - withClue(queryResult) { queryResult?.item?.value shouldBe values } + val queryResult = queryRef.execute() + queryResult.asClue { it.data.item.value shouldBe values } } } @@ -531,7 +515,7 @@ class EnumIntegrationTest : DataConnectIntegrationTestBase() { @Serializable private data class GetNullableListOfNonNullableByKeyVariables(val key: RowKey) @Serializable - private data class GetNullableListOfNonNullableByKeyData(val item: Item?) { + private data class GetNullableListOfNonNullableByKeyData(val item: Item) { @Serializable data class Item(val value: List?) } @@ -541,13 +525,12 @@ class EnumIntegrationTest : DataConnectIntegrationTestBase() { @Test fun insertNullableListOfNullable_ListContainingNonNullValues() = runTest { - val enumArb = Arb.enum() - checkAll(NUM_ITERATIONS, Arb.list(enumArb, 0..5)) { value -> - val insertVariables = InsertNullableListOfNullableVariables(value) + checkAll(NUM_ITERATIONS, Arb.list(Arb.enum(), 0..5)) { values -> + val insertVariables = InsertNullableListOfNullableVariables(values) val key = dataConnect.mutation(insertVariables).execute().data.key val queryVariables = GetNullableListOfNullableByKeyVariables(key) - val queryResult = dataConnect.query(queryVariables).execute().data - withClue(queryResult) { queryResult?.item?.value shouldBe value } + val queryResult = dataConnect.query(queryVariables).execute() + queryResult.asClue { it.data.item.value shouldBe values } } } @@ -556,19 +539,18 @@ class EnumIntegrationTest : DataConnectIntegrationTestBase() { val insertVariables = InsertNullableListOfNullableVariables(null) val key = dataConnect.mutation(insertVariables).execute().data.key val queryVariables = GetNullableListOfNullableByKeyVariables(key) - val queryResult = dataConnect.query(queryVariables).execute().data - withClue(queryResult) { queryResult?.item?.value.shouldBeNull() } + val queryResult = dataConnect.query(queryVariables).execute() + queryResult.asClue { it.data.item.value.shouldBeNull() } } @Test fun insertNullableListOfNullable_ListContainingNull() = runTest { - val enumArb = Arb.enum().orNull(nullProbability = 0.5) - checkAll(NUM_ITERATIONS, Arb.list(enumArb, 0..9).filter { it.contains(null) }) { value -> - val insertVariables = InsertNullableListOfNullableVariables(value) + checkAll(NUM_ITERATIONS, Arb.listContainingNull(Arb.enum(), 1..5)) { values -> + val insertVariables = InsertNullableListOfNullableVariables(values) val key = dataConnect.mutation(insertVariables).execute().data.key val queryVariables = GetNullableListOfNullableByKeyVariables(key) - val queryResult = dataConnect.query(queryVariables).execute().data - withClue(queryResult) { queryResult?.item?.value shouldBe value } + val queryResult = dataConnect.query(queryVariables).execute() + queryResult.asClue { it.data.item.value shouldBe values } } } @@ -580,8 +562,8 @@ class EnumIntegrationTest : DataConnectIntegrationTestBase() { val key = dataConnect.mutation(insertVariables).execute().data.key val queryVariables = GetNullableListOfNullableByKeyVariables(key) val queryRef = dataConnect.query(queryVariables) - val queryResult = queryRef.execute().data - withClue(queryResult) { queryResult?.item?.value shouldBe values } + val queryResult = queryRef.execute() + queryResult.asClue { it.data.item.value shouldBe values } } } @@ -595,7 +577,7 @@ class EnumIntegrationTest : DataConnectIntegrationTestBase() { @Serializable private data class GetNullableListOfNullableByKeyVariables(val key: RowKey) @Serializable - private data class GetNullableListOfNullableByKeyData(val item: Item?) { + private data class GetNullableListOfNullableByKeyData(val item: Item) { @Serializable data class Item(val value: List?) } @@ -605,14 +587,14 @@ class EnumIntegrationTest : DataConnectIntegrationTestBase() { @Test fun enumAsPrimaryKey() = runTest { - N5ekmae3jn.entries.forEach { enumValue -> - val tag = Arb.dataConnect.tag().next(rs) + checkAll(Exhaustive.enum()) { enumValue -> + val tag = Arb.dataConnect.tag().next(randomSource()) val insertVariables = InsertEnumKeyVariables(enumValue, tag) val key = dataConnect.mutation(insertVariables).execute().data.key - withClue(key) { key.enumValue shouldBe enumValue } + key.asClue { it.enumValue shouldBe enumValue } val queryVariables = GetEnumKeyByKeyVariables(key) - val queryResult = dataConnect.query(queryVariables).execute().data - withClue(queryResult) { queryResult?.item?.tag shouldBe tag } + val queryResult = dataConnect.query(queryVariables).execute() + queryResult.asClue { it.data.item.tag shouldBe tag } } } @@ -626,7 +608,7 @@ class EnumIntegrationTest : DataConnectIntegrationTestBase() { @Serializable private data class GetEnumKeyByKeyVariables(val key: EnumKeyKey) @Serializable - private data class GetEnumKeyByKeyData(val item: Item?) { + private data class GetEnumKeyByKeyData(val item: Item) { @Serializable data class Item(val tag: String) } @@ -640,11 +622,11 @@ class EnumIntegrationTest : DataConnectIntegrationTestBase() { val insertVariables = InsertMultipleEnumColumnsVariables(enum1, enum2) val key = dataConnect.mutation(insertVariables).execute().data.key val queryVariables = GetMultipleEnumColumnsByKeyVariables(key) - val queryResult = dataConnect.query(queryVariables).execute().data - withClue(queryResult) { + val queryResult = dataConnect.query(queryVariables).execute() + queryResult.asClue { assertSoftly { - queryResult?.item?.enum1 shouldBe enum1 - queryResult?.item?.enum2 shouldBe enum2 + it.data.item.enum1 shouldBe enum1 + it.data.item.enum2 shouldBe enum2 } } } @@ -659,7 +641,7 @@ class EnumIntegrationTest : DataConnectIntegrationTestBase() { @Serializable private data class GetMultipleEnumColumnsByKeyVariables(val key: RowKey) @Serializable - private data class GetMultipleEnumColumnsByKeyData(val item: Item?) { + private data class GetMultipleEnumColumnsByKeyData(val item: Item) { @Serializable data class Item(val enum1: N5ekmae3jn, val enum2: S7yayynb25) } @@ -709,7 +691,7 @@ class EnumIntegrationTest : DataConnectIntegrationTestBase() { } } - @Suppress("SpellCheckingInspection") + @Suppress("SpellCheckingInspection", "unused") private enum class N5ekmae3jn { DPSKD6HR3A, XGWGVMYTHJ, @@ -720,7 +702,7 @@ class EnumIntegrationTest : DataConnectIntegrationTestBase() { } @Suppress("SpellCheckingInspection", "unused") - enum class S7yayynb25 { + private enum class S7yayynb25 { XJ27ZAXKD3, R36KQ8PT5K, ETCV3FN9GH, @@ -794,30 +776,28 @@ class EnumIntegrationTest : DataConnectIntegrationTestBase() { fun FirebaseDataConnect.query( variables: GetNonNullableByKeyVariables - ): QueryRef = + ): QueryRef = query("EnumNonNullable_GetByKey", variables, serializer(), serializer()) fun FirebaseDataConnect.query( variables: GetNonNullableListOfNonNullableByKeyVariables ): QueryRef< - GetNonNullableListOfNonNullableByKeyData?, GetNonNullableListOfNonNullableByKeyVariables + GetNonNullableListOfNonNullableByKeyData, GetNonNullableListOfNonNullableByKeyVariables > = query("EnumNonNullableListOfNonNullable_GetByKey", variables, serializer(), serializer()) fun FirebaseDataConnect.query( variables: GetNonNullableListOfNullableByKeyVariables - ): QueryRef< - GetNonNullableListOfNullableByKeyData?, GetNonNullableListOfNullableByKeyVariables - > = query("EnumNonNullableListOfNullable_GetByKey", variables, serializer(), serializer()) + ): QueryRef = + query("EnumNonNullableListOfNullable_GetByKey", variables, serializer(), serializer()) fun FirebaseDataConnect.query( variables: GetNullableListOfNonNullableByKeyVariables - ): QueryRef< - GetNullableListOfNonNullableByKeyData?, GetNullableListOfNonNullableByKeyVariables - > = query("EnumNullableListOfNonNullable_GetByKey", variables, serializer(), serializer()) + ): QueryRef = + query("EnumNullableListOfNonNullable_GetByKey", variables, serializer(), serializer()) fun FirebaseDataConnect.query( variables: GetNullableListOfNullableByKeyVariables - ): QueryRef = + ): QueryRef = query("EnumNullableListOfNullable_GetByKey", variables, serializer(), serializer()) fun FirebaseDataConnect.query( @@ -832,12 +812,12 @@ class EnumIntegrationTest : DataConnectIntegrationTestBase() { fun FirebaseDataConnect.query( variables: GetEnumKeyByKeyVariables - ): QueryRef = + ): QueryRef = query("EnumKey_GetByKey", variables, serializer(), serializer()) fun FirebaseDataConnect.query( variables: GetMultipleEnumColumnsByKeyVariables - ): QueryRef = + ): QueryRef = query("MultipleEnumColumns_GetByKey", variables, serializer(), serializer()) fun FirebaseDataConnect.mutation( @@ -857,17 +837,17 @@ class EnumIntegrationTest : DataConnectIntegrationTestBase() { fun FirebaseDataConnect.query( variables: GetNullableByKeyVariables - ): QueryRef = + ): QueryRef = query("EnumNullable_GetByKey", variables, serializer(), serializer()) fun FirebaseDataConnect.query( variables: GetNonNullableTableDefaultByKeyVariables - ): QueryRef = + ): QueryRef = query("EnumNonNullableTableDefault_GetByKey", variables, serializer(), serializer()) fun FirebaseDataConnect.query( variables: GetNullableTableDefaultByKeyVariables - ): QueryRef = + ): QueryRef = query("EnumNullableTableDefault_GetByKey", variables, serializer(), serializer()) fun FirebaseDataConnect.query( diff --git a/firebase-dataconnect/testutil/src/main/kotlin/com/google/firebase/dataconnect/testutil/property/arbitrary/ListContainingNull.kt b/firebase-dataconnect/testutil/src/main/kotlin/com/google/firebase/dataconnect/testutil/property/arbitrary/ListContainingNull.kt new file mode 100644 index 00000000000..4fafa44a729 --- /dev/null +++ b/firebase-dataconnect/testutil/src/main/kotlin/com/google/firebase/dataconnect/testutil/property/arbitrary/ListContainingNull.kt @@ -0,0 +1,166 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.firebase.dataconnect.testutil.property.arbitrary + +import io.kotest.property.Arb +import io.kotest.property.Exhaustive +import io.kotest.property.Gen +import io.kotest.property.RandomSource +import io.kotest.property.Sample +import io.kotest.property.arbitrary.bind +import io.kotest.property.arbitrary.enum +import io.kotest.property.arbitrary.filterNot +import io.kotest.property.arbitrary.next +import io.kotest.property.asSample +import kotlin.random.nextInt + +/** + * Returns an [Arb] that generates lists containing elements produced by the given [Gen], while + * guaranteeing that at least one of the elements of the list is `null`. + * + * @param gen The [Gen] for non-null elements in the generated lists. If this [Gen] _also_ produces + * `null` then such a `null` element will satisfy the requirement that at least one element in + * generated lists is `null` even if `null` was not chosen by the given [nullProbability]. + * @param size The range of list sizes that will be produced by the returned [Arb]. The lower bound + * of the range must be at least 1 and must be a non-empty range. + * @param nullProbability The probability that any given element of generated lists will be `null` + * instead of pulling a value from [gen]. This value must be greater than `0.0` and less than or + * equal to `1.0`. + */ +fun Arb.Companion.listContainingNull( + gen: Gen, + size: IntRange = 1..100, + nullProbability: Double = 0.5 +): Arb> = ListContainingNullArb(gen, size, nullProbability) + +private class ListContainingNullArb( + gen: Gen, + private val size: IntRange, + private val nullProbability: Double +) : Arb>() { + + init { + require(size.first > 0) { + "the lower bound of the size range must be at least 1, " + + "but got: ${size.first} (size=$size)" + } + require(!size.isEmpty()) { + "the given size range is empty, but a non-empty range is required (size=$size)" + } + require(nullProbability.isFinite() && nullProbability > 0.0 && nullProbability <= 1.0) { + "invalid nullProbability: $nullProbability " + + "(must be strictly greater than 0.0 and less than or equal to 1.0)" + } + } + + private val arb: Arb = + when (gen) { + is Arb -> gen + is Exhaustive -> gen.toArb() + } + + private val edgeCaseArb: Arb = Arb.edgeCase() + + override fun sample(rs: RandomSource): Sample> = + sample(rs, listSize = randomListSize(rs)).asSample() + + private fun sample(rs: RandomSource, listSize: Int): List { + require(listSize > 0) { "invalid listSize: $listSize (must be greater than zero)" } + val guaranteedNullIndex = randomGuaranteedNullIndex(rs, listSize) + return List(listSize) { index -> + if (index == guaranteedNullIndex || rs.random.nextDouble() < nullProbability) { + null + } else { + arb.next(rs) + } + } + } + + override fun edgecase(rs: RandomSource): List { + val (listSizeCategory, nullPositions) = edgeCaseArb.next(rs) + + val listSize = + when (listSizeCategory) { + EdgeCase.ListSizeCategory.MIN -> size.first + EdgeCase.ListSizeCategory.MAX -> size.last + EdgeCase.ListSizeCategory.RANDOM -> randomListSize(rs) + } + + val guaranteedNullIndex = + when (nullPositions) { + EdgeCase.NullPositions.RANDOM -> randomGuaranteedNullIndex(rs, listSize) + EdgeCase.NullPositions.FIRST, + EdgeCase.NullPositions.LAST, + EdgeCase.NullPositions.FIRST_AND_LAST, + EdgeCase.NullPositions.ALL -> -1 + } + + val firstIndex = 0 + val lastIndex = listSize - 1 + + return List(listSize) { + val isNull = + when (nullPositions) { + EdgeCase.NullPositions.FIRST -> it == firstIndex + EdgeCase.NullPositions.LAST -> it == lastIndex + EdgeCase.NullPositions.FIRST_AND_LAST -> it == firstIndex || it == lastIndex + EdgeCase.NullPositions.ALL -> true + EdgeCase.NullPositions.RANDOM -> it == guaranteedNullIndex + } + if (isNull) null else arb.next(rs) + } + } + + private fun randomListSize(rs: RandomSource): Int = rs.random.nextInt(size) + + private fun randomGuaranteedNullIndex(rs: RandomSource, listSize: Int): Int = + rs.random.nextInt(listSize) + + private data class EdgeCase( + val listSizeCategory: ListSizeCategory, + val nullPositions: NullPositions + ) { + + enum class ListSizeCategory { + MIN, + MAX, + RANDOM, + } + + enum class NullPositions { + FIRST, + LAST, + FIRST_AND_LAST, + ALL, + RANDOM, + } + } + + private companion object { + fun Arb.Companion.edgeCase( + listSizeCategory: Arb = Arb.enum(), + nullPositions: Arb = Arb.enum() + ): Arb = + Arb.bind(listSizeCategory, nullPositions) { listSizeCategoryValue, nullPositionsValue -> + EdgeCase(listSizeCategoryValue, nullPositionsValue) + } + .filterNot { + it.listSizeCategory == EdgeCase.ListSizeCategory.RANDOM && + it.nullPositions == EdgeCase.NullPositions.RANDOM + } + } +} diff --git a/firebase-dataconnect/testutil/src/main/kotlin/com/google/firebase/dataconnect/testutil/property/arbitrary/misc.kt b/firebase-dataconnect/testutil/src/main/kotlin/com/google/firebase/dataconnect/testutil/property/arbitrary/misc.kt index 61703b784f5..a40b78cc9b5 100644 --- a/firebase-dataconnect/testutil/src/main/kotlin/com/google/firebase/dataconnect/testutil/property/arbitrary/misc.kt +++ b/firebase-dataconnect/testutil/src/main/kotlin/com/google/firebase/dataconnect/testutil/property/arbitrary/misc.kt @@ -16,12 +16,19 @@ package com.google.firebase.dataconnect.testutil.property.arbitrary +import com.google.firebase.dataconnect.testutil.withNullAppended import io.kotest.property.Arb +import io.kotest.property.EdgeConfig +import io.kotest.property.Exhaustive +import io.kotest.property.PropertyContext +import io.kotest.property.Sample import io.kotest.property.arbitrary.arbitrary import io.kotest.property.arbitrary.bind import io.kotest.property.arbitrary.filter import io.kotest.property.arbitrary.flatMap import io.kotest.property.arbitrary.map +import io.kotest.property.exhaustive.enum +import io.kotest.property.exhaustive.exhaustive import kotlin.random.nextInt /** Returns a new [Arb] that produces two _unequal_ values of this [Arb]. */ @@ -81,3 +88,14 @@ private fun pow10(n: Int): Int { repeat(n) { result *= 10 } return result } + +fun PropertyContext.sampleFromArb(arb: Arb, edgeCaseProbability: Double): Sample { + val edgeConfig = EdgeConfig(edgecasesGenerationProbability = edgeCaseProbability) + return arb.generate(randomSource(), edgeConfig).first() +} + +/** + * Creates and returns a new [Exhaustive] whose values are all of the values of [T] and also `null`. + */ +inline fun > Exhaustive.Companion.enumWithNull(): Exhaustive = + Exhaustive.enum().values.withNullAppended().exhaustive() diff --git a/firebase-dataconnect/testutil/src/test/kotlin/com/google/firebase/dataconnect/testutil/ListContainingNullUnitTest.kt b/firebase-dataconnect/testutil/src/test/kotlin/com/google/firebase/dataconnect/testutil/ListContainingNullUnitTest.kt new file mode 100644 index 00000000000..a7549f517b8 --- /dev/null +++ b/firebase-dataconnect/testutil/src/test/kotlin/com/google/firebase/dataconnect/testutil/ListContainingNullUnitTest.kt @@ -0,0 +1,90 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.firebase.dataconnect.testutil + +import com.google.firebase.dataconnect.testutil.property.arbitrary.listContainingNull +import com.google.firebase.dataconnect.testutil.property.arbitrary.sampleFromArb +import io.kotest.assertions.asClue +import io.kotest.assertions.assertSoftly +import io.kotest.assertions.withClue +import io.kotest.matchers.collections.shouldBeIn +import io.kotest.matchers.collections.shouldContain +import io.kotest.matchers.ints.shouldBeGreaterThanOrEqual +import io.kotest.matchers.ints.shouldBeLessThanOrEqual +import io.kotest.property.Arb +import io.kotest.property.Exhaustive +import io.kotest.property.arbitrary.bind +import io.kotest.property.arbitrary.int +import io.kotest.property.checkAll +import io.kotest.property.exhaustive.of +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class ListContainingNullUnitTest { + + @Test + fun `listContainingNull generates values with lengths in the given range`() = runTest { + checkAll(NUM_ITERATIONS, listLengthsArb) { lengthRange -> + val arb = Arb.listContainingNull(valuesGen, lengthRange) + val sample = sampleFromArb(arb, edgeCaseProbability = 0.5) + sample.value.asClue { + assertSoftly { + it.size shouldBeGreaterThanOrEqual lengthRange.first + it.size shouldBeLessThanOrEqual lengthRange.last + } + } + } + } + + @Test + fun `listContainingNull generates values from the given arb`() = runTest { + checkAll(NUM_ITERATIONS, listLengthsArb) { lengthRange -> + val arb = Arb.listContainingNull(valuesGen, lengthRange) + val sample = sampleFromArb(arb, edgeCaseProbability = 0.5) + sample.value.asClue { + assertSoftly { + it.forEachIndexed { index, value -> + if (value !== null) { + withClue("index=$index") { value shouldBeIn valuesGen.values } + } + } + } + } + } + } + + @Test + fun `listContainingNull generates lists that always contain null`() = runTest { + checkAll(NUM_ITERATIONS, listLengthsArb) { lengthRange -> + val arb = Arb.listContainingNull(valuesGen, lengthRange) + val sample = sampleFromArb(arb, edgeCaseProbability = 0.5) + sample.value.asClue { it shouldContain null } + } + } + + private companion object { + + const val NUM_ITERATIONS = 1000 + + val valuesGen = Exhaustive.of("foo", "bar", "baz") + + val listLengthsArb: Arb = + Arb.bind(Arb.int(1..20), Arb.int(1..20)) { lengthLowerBound, lengthExtent -> + lengthLowerBound until (lengthLowerBound + lengthExtent) + } + } +}