diff --git a/README.md b/README.md
index 842a36cb7..675f6e2e0 100644
--- a/README.md
+++ b/README.md
@@ -241,6 +241,15 @@ citiesRef.where {
}
```
+Similar methods exist for `update` methods in the Firestore module:
+
+```kotlin
+documentRef.update {
+ "field" to "value"
+ "otherField".to(IntAsStringSerializer(), 1)
+}
+```
+
In cases where it makes sense, such as Firebase Functions HTTPS Callable, operator overloading is used:
diff --git a/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/DocumentReferenceTest.kt b/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/DocumentReferenceTest.kt
index 446dbb4ab..d58024788 100644
--- a/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/DocumentReferenceTest.kt
+++ b/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/DocumentReferenceTest.kt
@@ -2,6 +2,7 @@ package dev.gitlive.firebase.firestore
import dev.gitlive.firebase.internal.decode
import dev.gitlive.firebase.runTest
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
@@ -90,10 +91,11 @@ class DocumentReferenceTest : BaseFirebaseFirestoreTest() {
}
@Test
- fun testServerTimestampFieldValue() = runTest {
- val doc = firestore
+ fun testServerTimestampFieldValue() = testDocument(
+ firestore
.collection("testServerTimestampFieldValue")
- .document("test")
+ .document("test"),
+ ) { doc ->
doc.set(
FirestoreTimeTest.serializer(),
FirestoreTimeTest("ServerTimestamp", Timestamp(123, 0)),
@@ -107,11 +109,11 @@ class DocumentReferenceTest : BaseFirebaseFirestoreTest() {
}
@Test
- fun testServerTimestampBehaviorNone() = runTest {
- val doc = firestore
+ fun testServerTimestampBehaviorNone() = testDocument(
+ firestore
.collection("testServerTimestampBehaviorNone")
- .document("test${Random.nextInt()}")
-
+ .document("test${Random.nextInt()}"),
+ ) { doc ->
val deferredPendingWritesSnapshot = async {
doc.snapshots.filter { it.exists }.first()
}
@@ -128,11 +130,11 @@ class DocumentReferenceTest : BaseFirebaseFirestoreTest() {
}
@Test
- fun testServerTimestampBehaviorEstimate() = runTest {
- val doc = firestore
+ fun testServerTimestampBehaviorEstimate() = testDocument(
+ firestore
.collection("testServerTimestampBehaviorEstimate")
- .document("test${Random.nextInt()}")
-
+ .document("test${Random.nextInt()}"),
+ ) { doc ->
val deferredPendingWritesSnapshot = async {
doc.snapshots.filter { it.exists }.first()
}
@@ -147,11 +149,11 @@ class DocumentReferenceTest : BaseFirebaseFirestoreTest() {
}
@Test
- fun testServerTimestampBehaviorPrevious() = runTest {
- val doc = firestore
+ fun testServerTimestampBehaviorPrevious() = testDocument(
+ firestore
.collection("testServerTimestampBehaviorPrevious")
- .document("test${Random.nextInt()}")
-
+ .document("test${Random.nextInt()}"),
+ ) { doc ->
val deferredPendingWritesSnapshot = async {
doc.snapshots.filter { it.exists }.first()
}
@@ -165,10 +167,11 @@ class DocumentReferenceTest : BaseFirebaseFirestoreTest() {
}
@Test
- fun testDocumentAutoId() = runTest {
- val doc = firestore
+ fun testDocumentAutoId() = testDocument(
+ firestore
.collection("testDocumentAutoId")
- .document
+ .document,
+ ) { doc ->
doc.set(FirestoreTest.serializer(), FirestoreTest("AutoId"))
@@ -182,12 +185,20 @@ class DocumentReferenceTest : BaseFirebaseFirestoreTest() {
}
@Test
- fun testUpdateValues() = runTest {
- val doc = firestore
+ fun testUpdateValues() = testDocument(
+ firestore
.collection("testFirestoreUpdateMultipleValues")
- .document("test1")
-
- doc.set(FirestoreTest.serializer(), FirestoreTest("property", count = 0, nested = NestedObject("nested"), duration = 600.milliseconds))
+ .document("test1"),
+ ) { doc ->
+ doc.set(
+ FirestoreTest.serializer(),
+ FirestoreTest(
+ "property",
+ count = 0,
+ nested = NestedObject("nested"),
+ duration = 600.milliseconds,
+ ),
+ )
val dataBefore = doc.get().data(FirestoreTest.serializer())
assertEquals(0, dataBefore.count)
assertNull(dataBefore.optional)
@@ -197,8 +208,14 @@ class DocumentReferenceTest : BaseFirebaseFirestoreTest() {
doc.update {
FirestoreTest::count.name to 5
FieldPath(FirestoreTest::optional.name) to "notNull"
- FirestoreTest::nested.name.to(NestedObject.serializer(), NestedObject("newProperty"))
- FieldPath(FirestoreTest::duration.name).to(DurationAsLongSerializer(), 700.milliseconds)
+ FirestoreTest::nested.name.to(
+ NestedObject.serializer(),
+ NestedObject("newProperty"),
+ )
+ FieldPath(FirestoreTest::duration.name).to(
+ DurationAsIntSerializer(),
+ 700.milliseconds,
+ )
}
val dataAfter = doc.get().data(FirestoreTest.serializer())
assertEquals(5, dataAfter.count)
@@ -208,11 +225,11 @@ class DocumentReferenceTest : BaseFirebaseFirestoreTest() {
}
@Test
- fun testIncrementFieldValue() = runTest {
- val doc = firestore
+ fun testIncrementFieldValue() = testDocument(
+ firestore
.collection("testFirestoreIncrementFieldValue")
- .document("test1")
-
+ .document("test1"),
+ ) { doc ->
doc.set(FirestoreTest.serializer(), FirestoreTest("increment1", count = 0))
val dataBefore = doc.get().data(FirestoreTest.serializer())
assertEquals(0, dataBefore.count)
@@ -223,11 +240,11 @@ class DocumentReferenceTest : BaseFirebaseFirestoreTest() {
}
@Test
- fun testArrayUnion() = runTest {
- val doc = firestore
+ fun testArrayUnion() = testDocument(
+ firestore
.collection("testFirestoreArrayUnion")
- .document("test1")
-
+ .document("test1"),
+ ) { doc ->
doc.set(FirestoreTest.serializer(), FirestoreTest("increment1", list = listOf("first")))
val dataBefore = doc.get().data(FirestoreTest.serializer())
assertEquals(listOf("first"), dataBefore.list)
@@ -238,11 +255,11 @@ class DocumentReferenceTest : BaseFirebaseFirestoreTest() {
}
@Test
- fun testArrayRemove() = runTest {
- val doc = firestore
+ fun testArrayRemove() = testDocument(
+ firestore
.collection("testFirestoreArrayRemove")
- .document("test1")
-
+ .document("test1"),
+ ) { doc ->
doc.set(FirestoreTest.serializer(), FirestoreTest("increment1", list = listOf("first", "second")))
val dataBefore = doc.get().data(FirestoreTest.serializer())
assertEquals(listOf("first", "second"), dataBefore.list)
@@ -253,17 +270,17 @@ class DocumentReferenceTest : BaseFirebaseFirestoreTest() {
}
@Test
- fun testLegacyDoubleTimestamp() = runTest {
+ fun testLegacyDoubleTimestamp() = testDocument(
+ firestore
+ .collection("testLegacyDoubleTimestamp")
+ .document("test${Random.nextInt()}"),
+ ) { doc ->
@Serializable
data class DoubleTimestamp(
@Serializable(with = DoubleAsTimestampSerializer::class)
val time: Double?,
)
- val doc = firestore
- .collection("testLegacyDoubleTimestamp")
- .document("test${Random.nextInt()}")
-
val deferredPendingWritesSnapshot = async {
doc.snapshots.filter { it.exists }.first()
}
@@ -278,7 +295,11 @@ class DocumentReferenceTest : BaseFirebaseFirestoreTest() {
}
@Test
- fun testLegacyDoubleTimestampWriteNewFormatRead() = runTest {
+ fun testLegacyDoubleTimestampWriteNewFormatRead() = testDocument(
+ firestore
+ .collection("testLegacyDoubleTimestampEncodeDecode")
+ .document("testLegacy"),
+ ) { doc ->
@Serializable
data class LegacyDocument(
@Serializable(with = DoubleAsTimestampSerializer::class)
@@ -290,10 +311,6 @@ class DocumentReferenceTest : BaseFirebaseFirestoreTest() {
val time: Timestamp,
)
- val doc = firestore
- .collection("testLegacyDoubleTimestampEncodeDecode")
- .document("testLegacy")
-
val ms = 12345678.0
doc.set(LegacyDocument.serializer(), LegacyDocument(time = ms))
@@ -303,25 +320,25 @@ class DocumentReferenceTest : BaseFirebaseFirestoreTest() {
}
@Test
- fun testGeoPointSerialization() = runTest {
+ fun testGeoPointSerialization() = testDocument(
+ firestore.collection("geoPointSerialization")
+ .document("geoPointSerialization"),
+ ) { doc ->
@Serializable
data class DataWithGeoPoint(val geoPoint: GeoPoint)
- fun getDocument() = firestore.collection("geoPointSerialization")
- .document("geoPointSerialization")
-
val data = DataWithGeoPoint(GeoPoint(12.34, 56.78))
// store geo point
- getDocument().set(DataWithGeoPoint.serializer(), data)
+ doc.set(DataWithGeoPoint.serializer(), data)
// restore data
- val savedData = getDocument().get().data(DataWithGeoPoint.serializer())
+ val savedData = doc.get().data(DataWithGeoPoint.serializer())
assertEquals(data.geoPoint, savedData.geoPoint)
// update data
val updatedData = DataWithGeoPoint(GeoPoint(87.65, 43.21))
- getDocument().update(FieldPath(DataWithGeoPoint::geoPoint.name) to updatedData.geoPoint)
+ doc.update(FieldPath(DataWithGeoPoint::geoPoint.name) to updatedData.geoPoint)
// verify update
- val updatedSavedData = getDocument().get().data(DataWithGeoPoint.serializer())
+ val updatedSavedData = doc.get().data(DataWithGeoPoint.serializer())
assertEquals(updatedData.geoPoint, updatedSavedData.geoPoint)
}
@@ -332,66 +349,82 @@ class DocumentReferenceTest : BaseFirebaseFirestoreTest() {
val documentReference: DocumentReference,
)
- fun getCollection() = firestore.collection("documentReferenceSerialization")
- fun getDocument() = getCollection()
+ val collection = firestore.collection("documentReferenceSerialization")
+ val document = collection
.document("documentReferenceSerialization")
- val documentRef1 = getCollection().document("refDoc1").apply {
- set(mapOf("value" to 1))
- }
- val documentRef2 = getCollection().document("refDoc2").apply {
- set(mapOf("value" to 2))
- }
-
- val data = DataWithDocumentReference(documentRef1)
- // store reference
- getDocument().set(DataWithDocumentReference.serializer(), data)
- // restore data
- val savedData = getDocument().get().data(DataWithDocumentReference.serializer())
- assertEquals(data.documentReference.path, savedData.documentReference.path)
-
- // update data
- val updatedData = DataWithDocumentReference(documentRef2)
- getDocument().update {
- FieldPath(DataWithDocumentReference::documentReference.name).to(
- DocumentReferenceSerializer,
- updatedData.documentReference,
+ val documentRef1 = collection.document("refDoc1")
+ val documentRef2 = collection.document("refDoc2")
+
+ try {
+ documentRef1.set(mapOf("value" to 1))
+ documentRef2.set(mapOf("value" to 2))
+ val data = DataWithDocumentReference(documentRef1)
+ // store reference
+ document.set(DataWithDocumentReference.serializer(), data)
+ // restore data
+ val savedData = document.get().data(DataWithDocumentReference.serializer())
+ assertEquals(data.documentReference.path, savedData.documentReference.path)
+
+ // update data
+ val updatedData = DataWithDocumentReference(documentRef2)
+ document.update {
+ FieldPath(DataWithDocumentReference::documentReference.name).to(
+ DocumentReferenceSerializer,
+ updatedData.documentReference,
+ )
+ }
+ // verify update
+ val updatedSavedData = document.get().data(DataWithDocumentReference.serializer())
+ assertEquals(
+ updatedData.documentReference.path,
+ updatedSavedData.documentReference.path,
)
+ } finally {
+ document.delete()
+ documentRef1.delete()
+ documentRef2.delete()
}
- // verify update
- val updatedSavedData = getDocument().get().data(DataWithDocumentReference.serializer())
- assertEquals(updatedData.documentReference.path, updatedSavedData.documentReference.path)
}
@Test
- fun testFieldValuesOps() = runTest {
+ fun testFieldValuesOps() = testDocument(
+ firestore.collection("fieldValuesOps")
+ .document("fieldValuesOps"),
+ ) { doc ->
@Serializable
data class TestData(val values: List)
- fun getDocument() = firestore.collection("fieldValuesOps")
- .document("fieldValuesOps")
val data = TestData(listOf(1))
// store
- getDocument().set(TestData.serializer(), data)
+ doc.set(TestData.serializer(), data)
// append & verify
- getDocument().update(FieldPath(TestData::values.name) to FieldValue.arrayUnion(2))
+ doc.update(FieldPath(TestData::values.name) to FieldValue.arrayUnion(2))
- var savedData = getDocument().get().data(TestData.serializer())
+ var savedData = doc.get().data(TestData.serializer())
assertEquals(listOf(1, 2), savedData.values)
// remove & verify
- getDocument().update(FieldPath(TestData::values.name) to FieldValue.arrayRemove(1))
- savedData = getDocument().get().data(TestData.serializer())
+ doc.update(FieldPath(TestData::values.name) to FieldValue.arrayRemove(1))
+ savedData = doc.get().data(TestData.serializer())
assertEquals(listOf(2), savedData.values)
- val list = getDocument().get().get(TestData::values.name, ListSerializer(Int.serializer()).nullable)
+ val list = doc.get().get(TestData::values.name, ListSerializer(Int.serializer()).nullable)
assertEquals(listOf(2), list)
// delete & verify
- getDocument().update(FieldPath(TestData::values.name) to FieldValue.delete)
- val deletedList = getDocument().get().get(TestData::values.name, ListSerializer(Int.serializer()).nullable)
+ doc.update(FieldPath(TestData::values.name) to FieldValue.delete)
+ val deletedList = doc.get().get(TestData::values.name, ListSerializer(Int.serializer()).nullable)
assertNull(deletedList)
}
private suspend fun nonSkippedDelay(timeout: Duration) = withContext(Dispatchers.Default) {
delay(timeout)
}
+
+ private fun testDocument(document: DocumentReference, block: suspend CoroutineScope.(DocumentReference) -> Unit) = runTest {
+ try {
+ block(document)
+ } finally {
+ document.delete()
+ }
+ }
}
diff --git a/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/FirestoreSourceTest.kt b/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/FirestoreSourceTest.kt
index ecc5b386b..f87664321 100644
--- a/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/FirestoreSourceTest.kt
+++ b/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/FirestoreSourceTest.kt
@@ -21,10 +21,6 @@ class FirestoreSourceTest {
)
}
- private suspend fun setDoc() {
- firestore.collection("testFirestoreQuerying").document("one").set(testDoc)
- }
-
private fun initializeFirebase(persistenceEnabled: Boolean = false) {
val app = Firebase.apps(context).firstOrNull() ?: Firebase.initialize(
context,
@@ -58,67 +54,75 @@ class FirestoreSourceTest {
}
@Test
- fun testGetFromServer_withPersistence() = runTest {
- initializeFirebase(persistenceEnabled = true)
- setDoc()
- val doc = firestore.collection("testFirestoreQuerying").document("one").get(Source.SERVER)
+ fun testGetFromServer_withPersistence() = testFirebaseDoc(true) {
+ val doc = get(Source.SERVER)
assertTrue(doc.exists)
assertFalse(doc.metadata.isFromCache)
}
@Test
- fun testGetFromServer_withoutPersistence() = runTest {
- initializeFirebase(persistenceEnabled = false)
- setDoc()
- val doc = firestore.collection("testFirestoreQuerying").document("one").get(Source.SERVER)
+ fun testGetFromServer_withoutPersistence() = testFirebaseDoc(false) {
+ val doc = get(Source.SERVER)
assertTrue(doc.exists)
assertFalse(doc.metadata.isFromCache)
}
@Test
- fun testGetFromCache() = runTest {
- initializeFirebase(persistenceEnabled = true)
-
+ fun testGetFromCache() = testFirebaseDoc(true) {
// Warm up cache by setting a document
- setDoc()
+ set(testDoc)
- val cachedDoc = firestore.collection("testFirestoreQuerying").document("one").get(Source.CACHE)
+ val cachedDoc = get(Source.CACHE)
assertTrue(cachedDoc.exists)
assertTrue(cachedDoc.metadata.isFromCache)
}
@Test
- fun testGetFromCache_withoutPersistence() = runTest {
- initializeFirebase(persistenceEnabled = false)
- setDoc()
+ fun testGetFromCache_withoutPersistence() = testFirebaseDoc(false) {
assertFailsWith(FirebaseFirestoreException::class) {
- firestore.collection("testFirestoreQuerying").document("one").get(Source.CACHE)
+ get(Source.CACHE)
}
}
@Test
- fun testGetDefault_withPersistence() = runTest {
- initializeFirebase(persistenceEnabled = false)
- val doc = firestore.collection("testFirestoreQuerying").document("one").get(Source.DEFAULT)
+ fun testGetDefault_withPersistence() = testFirebaseDoc(false) {
+ val doc = get(Source.DEFAULT)
assertTrue(doc.exists)
assertFalse(doc.metadata.isFromCache)
}
@Test
- fun testGet() = runTest {
- initializeFirebase(persistenceEnabled = false)
- val doc = firestore.collection("testFirestoreQuerying").document("one").get()
+ fun testGet() = testFirebaseDoc(false) {
+ val doc = get()
assertTrue(doc.exists)
assertFalse(doc.metadata.isFromCache)
}
@Test
- fun testGetDefault_withoutPersistence() = runTest {
- initializeFirebase(persistenceEnabled = true)
- setDoc()
- val doc = firestore.collection("testFirestoreQuerying").document("one").get(Source.DEFAULT)
+ fun testGetDefault_withoutPersistence() = testFirebaseDoc(true) {
+ val doc = get(Source.DEFAULT)
assertTrue(doc.exists)
// Firebase defaults to first fetching from server
assertFalse(doc.metadata.isFromCache)
}
+
+ private fun testFirebaseDoc(
+ persistenceEnabled: Boolean,
+ block: suspend DocumentReference.() -> Unit,
+ ) = runTest {
+ initializeFirebase()
+ val doc = firestore.collection("testFirestoreQuerying").document("one")
+ doc.set(testDoc)
+
+ Firebase.apps(context).forEach { it.delete() }
+
+ initializeFirebase(persistenceEnabled = persistenceEnabled)
+
+ val newDoc = firestore.collection("testFirestoreQuerying").document("one")
+ try {
+ newDoc.block()
+ } finally {
+ newDoc.delete()
+ }
+ }
}
diff --git a/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/QueryTest.kt b/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/QueryTest.kt
index 9d25c5102..665044bda 100644
--- a/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/QueryTest.kt
+++ b/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/QueryTest.kt
@@ -177,12 +177,12 @@ open class QueryTest : BaseFirebaseFirestoreTest() {
pathQuery.assertDocuments(FirestoreTest.serializer(), testOne)
val serializeFieldQuery = collection.where {
- "duration".lessThan(DurationAsLongSerializer(), testThree.duration)
+ "duration".lessThan(DurationAsIntSerializer(), testThree.duration)
}
serializeFieldQuery.assertDocuments(FirestoreTest.serializer(), testOne, testTwo)
val serializePathQuery = collection.where {
- FieldPath(FirestoreTest::duration.name).lessThan(DurationAsLongSerializer(), testTwo.duration)
+ FieldPath(FirestoreTest::duration.name).lessThan(DurationAsIntSerializer(), testTwo.duration)
}
serializePathQuery.assertDocuments(FirestoreTest.serializer(), testOne)
}
@@ -200,12 +200,12 @@ open class QueryTest : BaseFirebaseFirestoreTest() {
pathQuery.assertDocuments(FirestoreTest.serializer(), testThree)
val serializeFieldQuery = collection.where {
- "duration".greaterThan(DurationAsLongSerializer(), testOne.duration)
+ "duration".greaterThan(DurationAsIntSerializer(), testOne.duration)
}
serializeFieldQuery.assertDocuments(FirestoreTest.serializer(), testTwo, testThree)
val serializePathQuery = collection.where {
- FieldPath(FirestoreTest::duration.name).greaterThan(DurationAsLongSerializer(), testTwo.duration)
+ FieldPath(FirestoreTest::duration.name).greaterThan(DurationAsIntSerializer(), testTwo.duration)
}
serializePathQuery.assertDocuments(FirestoreTest.serializer(), testThree)
}
@@ -223,12 +223,12 @@ open class QueryTest : BaseFirebaseFirestoreTest() {
pathQuery.assertDocuments(FirestoreTest.serializer(), testOne, testTwo)
val serializeFieldQuery = collection.where {
- "duration".lessThanOrEqualTo(DurationAsLongSerializer(), testOne.duration)
+ "duration".lessThanOrEqualTo(DurationAsIntSerializer(), testOne.duration)
}
serializeFieldQuery.assertDocuments(FirestoreTest.serializer(), testOne)
val serializePathQuery = collection.where {
- FieldPath(FirestoreTest::duration.name).lessThanOrEqualTo(DurationAsLongSerializer(), testTwo.duration)
+ FieldPath(FirestoreTest::duration.name).lessThanOrEqualTo(DurationAsIntSerializer(), testTwo.duration)
}
serializePathQuery.assertDocuments(FirestoreTest.serializer(), testOne, testTwo)
}
@@ -246,12 +246,12 @@ open class QueryTest : BaseFirebaseFirestoreTest() {
pathQuery.assertDocuments(FirestoreTest.serializer(), testTwo, testThree)
val serializeFieldQuery = collection.where {
- "duration".greaterThanOrEqualTo(DurationAsLongSerializer(), testTwo.duration)
+ "duration".greaterThanOrEqualTo(DurationAsIntSerializer(), testTwo.duration)
}
serializeFieldQuery.assertDocuments(FirestoreTest.serializer(), testTwo, testThree)
val serializePathQuery = collection.where {
- FieldPath(FirestoreTest::duration.name).greaterThanOrEqualTo(DurationAsLongSerializer(), testThree.duration)
+ FieldPath(FirestoreTest::duration.name).greaterThanOrEqualTo(DurationAsIntSerializer(), testThree.duration)
}
serializePathQuery.assertDocuments(FirestoreTest.serializer(), testThree)
}
@@ -397,20 +397,25 @@ open class QueryTest : BaseFirebaseFirestoreTest() {
val pastTimestamp = Timestamp(timestamp.seconds - 60, 12345000) // note: iOS truncates 3 last digits of nanoseconds due to internal conversions
val futureTimestamp = Timestamp(timestamp.seconds + 60, 78910000)
- collection.add(DocumentWithTimestamp.serializer(), DocumentWithTimestamp(pastTimestamp))
- collection.add(DocumentWithTimestamp.serializer(), DocumentWithTimestamp(futureTimestamp))
+ val doc1 = collection.add(DocumentWithTimestamp.serializer(), DocumentWithTimestamp(pastTimestamp))
+ val doc2 = collection.add(DocumentWithTimestamp.serializer(), DocumentWithTimestamp(futureTimestamp))
- val equalityQueryResult = collection.where {
- FieldPath(DocumentWithTimestamp::time.name) equalTo pastTimestamp
- }.get().documents.map { it.data(DocumentWithTimestamp.serializer()) }.toSet()
+ try {
+ val equalityQueryResult = collection.where {
+ FieldPath(DocumentWithTimestamp::time.name) equalTo pastTimestamp
+ }.get().documents.map { it.data(DocumentWithTimestamp.serializer()) }.toSet()
- assertEquals(setOf(DocumentWithTimestamp(pastTimestamp)), equalityQueryResult)
+ assertEquals(setOf(DocumentWithTimestamp(pastTimestamp)), equalityQueryResult)
- val gtQueryResult = collection.where {
- FieldPath(DocumentWithTimestamp::time.name) greaterThan timestamp
- }.get().documents.map { it.data(DocumentWithTimestamp.serializer()) }.toSet()
+ val gtQueryResult = collection.where {
+ FieldPath(DocumentWithTimestamp::time.name) greaterThan timestamp
+ }.get().documents.map { it.data(DocumentWithTimestamp.serializer()) }.toSet()
- assertEquals(setOf(DocumentWithTimestamp(futureTimestamp)), gtQueryResult)
+ assertEquals(setOf(DocumentWithTimestamp(futureTimestamp)), gtQueryResult)
+ } finally {
+ doc1.delete()
+ doc2.delete()
+ }
}
@Test
diff --git a/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/TransactionTest.kt b/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/TransactionTest.kt
new file mode 100644
index 000000000..5a68d53a8
--- /dev/null
+++ b/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/TransactionTest.kt
@@ -0,0 +1,53 @@
+package dev.gitlive.firebase.firestore
+
+import dev.gitlive.firebase.runTest
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertTrue
+import kotlin.time.Duration.Companion.milliseconds
+
+@IgnoreForAndroidUnitTest
+class TransactionTest : BaseFirebaseFirestoreTest() {
+
+ @Test
+ fun runTransaction() = runTest {
+ val collection = firestore.collection("testServerTestTransaction")
+ val document = collection.document("doc1")
+ try {
+ document.set(
+ strategy = FirestoreTest.serializer(),
+ data = FirestoreTest(
+ prop1 = "prop1",
+ count = 0,
+ ),
+ )
+ val result = firestore.runTransaction {
+ val count = get(document).data(FirestoreTest.serializer()).count
+
+ if (count < 1) {
+ update(document) {
+ FirestoreTest::prop1.name to "newProperty"
+ FieldPath(FirestoreTest::count.name) to 5
+ FirestoreTest::duration.name.to(DurationAsIntSerializer(), 100.milliseconds)
+ FieldPath(FirestoreTest::nested.name).to(
+ NestedObject.serializer(),
+ NestedObject("nested"),
+ )
+ }
+ true
+ } else {
+ throw IllegalStateException("Invalid count")
+ }
+ }
+ assertTrue(result)
+
+ val updated = document.get().data(FirestoreTest.serializer())
+ assertEquals("newProperty", updated.prop1)
+ assertEquals(5, updated.count)
+ assertEquals(100.milliseconds, updated.duration)
+ assertEquals(NestedObject("nested"), updated.nested)
+ } finally {
+ document.delete()
+ }
+ }
+}
diff --git a/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/WriteBatchTest.kt b/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/WriteBatchTest.kt
index b06ac071c..f5e8ece3f 100644
--- a/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/WriteBatchTest.kt
+++ b/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/WriteBatchTest.kt
@@ -12,11 +12,7 @@ class WriteBatchTest : BaseFirebaseFirestoreTest() {
.collection("testServerTestSetBatch")
@Test
- fun testSetBatch() = runTest {
- val doc1 = collection
- .document("test1")
- val doc2 = collection
- .document("test2")
+ fun testSetBatch() = testBatch { doc1, doc2 ->
val batch = firestore.batch()
batch.set(
documentRef = doc1,
@@ -41,11 +37,7 @@ class WriteBatchTest : BaseFirebaseFirestoreTest() {
}
@Test
- fun testSetBatchDoesNotEncodeEmptyValues() = runTest {
- val doc1 = collection
- .document("test1")
- val doc2 = collection
- .document("test2")
+ fun testSetBatchDoesNotEncodeEmptyValues() = testBatch { doc1, doc2 ->
val batch = firestore.batch()
batch.set(
documentRef = doc1,
@@ -72,25 +64,19 @@ class WriteBatchTest : BaseFirebaseFirestoreTest() {
}
@Test
- fun testUpdateBatch() = runTest {
- val doc1 = collection
- .document("test1").apply {
- set(
- FirestoreTest(
- prop1 = "prop1",
- time = 123.0,
- ),
- )
- }
- val doc2 = collection
- .document("test2").apply {
- set(
- FirestoreTest(
- prop1 = "prop2",
- time = 456.0,
- ),
- )
- }
+ fun testUpdateBatch() = testBatch { doc1, doc2 ->
+ doc1.set(
+ FirestoreTest(
+ prop1 = "prop1",
+ time = 123.0,
+ ),
+ )
+ doc2.set(
+ FirestoreTest(
+ prop1 = "prop2",
+ time = 456.0,
+ ),
+ )
val batch = firestore.batch()
batch.update(
@@ -120,25 +106,19 @@ class WriteBatchTest : BaseFirebaseFirestoreTest() {
}
@Test
- fun testUpdateBatchDoesNotEncodeEmptyValues() = runTest {
- val doc1 = collection
- .document("test1").apply {
- set(
- FirestoreTest(
- prop1 = "prop1",
- time = 123.0,
- ),
- )
- }
- val doc2 = collection
- .document("test2").apply {
- set(
- FirestoreTest(
- prop1 = "prop2",
- time = 456.0,
- ),
- )
- }
+ fun testUpdateBatchDoesNotEncodeEmptyValues() = testBatch { doc1, doc2 ->
+ doc1.set(
+ FirestoreTest(
+ prop1 = "prop1",
+ time = 123.0,
+ ),
+ )
+ doc2.set(
+ FirestoreTest(
+ prop1 = "prop2",
+ time = 456.0,
+ ),
+ )
val batch = firestore.batch()
batch.update(
documentRef = doc1,
@@ -169,38 +149,34 @@ class WriteBatchTest : BaseFirebaseFirestoreTest() {
}
@Test
- fun testUpdateFieldValuesBatch() = runTest {
- val doc1 = collection.document("test1").apply {
- set(
- FirestoreTest(
- prop1 = "prop1",
- time = 123.0,
- duration = 800.milliseconds,
- ),
- )
- }
+ fun testUpdateFieldValuesBatch() = testBatch { doc1, doc2 ->
+ doc1.set(
+ FirestoreTest(
+ prop1 = "prop1",
+ time = 123.0,
+ duration = 800.milliseconds,
+ ),
+ )
- val doc2 = collection.document("test2").apply {
- set(
- FirestoreTest(
- prop1 = "prop2",
- time = 456.0,
- duration = 700.milliseconds,
- ),
- )
- }
+ doc2.set(
+ FirestoreTest(
+ prop1 = "prop2",
+ time = 456.0,
+ duration = 700.milliseconds,
+ ),
+ )
val batch = firestore.batch()
batch.update(doc1) {
FirestoreTest::prop1.name to "prop1-updated"
FieldPath(FirestoreTest::optional.name) to "notNull"
- FirestoreTest::duration.name.to(DurationAsLongSerializer(), 300.milliseconds)
+ FirestoreTest::duration.name.to(DurationAsIntSerializer(), 300.milliseconds)
FieldPath(FirestoreTest::nested.name).to(NestedObject.serializer(), NestedObject("nested"))
}
batch.update(doc2) {
FirestoreTest::prop1.name to "prop2-updated"
FieldPath(FirestoreTest::optional.name) to "alsoNotNull"
- FirestoreTest::duration.name.to(DurationAsLongSerializer(), 200.milliseconds)
+ FirestoreTest::duration.name.to(DurationAsIntSerializer(), 200.milliseconds)
FieldPath(FirestoreTest::nested.name).to(NestedObject.serializer(), NestedObject("alsoNested"))
}
batch.commit()
@@ -217,4 +193,18 @@ class WriteBatchTest : BaseFirebaseFirestoreTest() {
assertEquals(200.milliseconds, updatedDoc2.duration)
assertEquals(NestedObject("alsoNested"), updatedDoc2.nested)
}
+
+ private fun testBatch(block: suspend (DocumentReference, DocumentReference) -> Unit) = runTest {
+ val doc1 = collection
+ .document("test1")
+ val doc2 = collection
+ .document("test2")
+
+ try {
+ block(doc1, doc2)
+ } finally {
+ doc1.delete()
+ doc2.delete()
+ }
+ }
}
diff --git a/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/firestore.kt
index d55d5dce9..7f79a7ebc 100644
--- a/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/firestore.kt
+++ b/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/firestore.kt
@@ -45,7 +45,7 @@ abstract class BaseFirebaseFirestoreTest {
val optional: String? = null,
val nested: NestedObject? = null,
val nestedList: List = emptyList(),
- @Serializable(with = DurationAsLongSerializer::class)
+ @Serializable(with = DurationAsIntSerializer::class)
val duration: Duration = Duration.ZERO,
)
@@ -54,15 +54,16 @@ abstract class BaseFirebaseFirestoreTest {
val prop2: String,
)
- class DurationAsLongSerializer : KSerializer {
+ // Long would be better but JS does not seem to support it on the Firebase level https://stackoverflow.com/questions/31930406/storing-long-type-in-firebase
+ class DurationAsIntSerializer : KSerializer {
- override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("millisecondsSinceEpoch", PrimitiveKind.LONG)
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("millisecondsSinceEpoch", PrimitiveKind.INT)
override fun serialize(encoder: Encoder, value: Duration) {
- encoder.encodeLong(value.inWholeMilliseconds)
+ encoder.encodeInt(value.inWholeMilliseconds.toInt())
}
- override fun deserialize(decoder: Decoder): Duration = decoder.decodeLong().milliseconds
+ override fun deserialize(decoder: Decoder): Duration = decoder.decodeInt().milliseconds
}
lateinit var firebaseApp: FirebaseApp
diff --git a/firebase-firestore/src/iosTest/kotlin/dev/gitlive/firebase/firestore/ContextSwitchTest.kt b/firebase-firestore/src/iosTest/kotlin/dev/gitlive/firebase/firestore/ContextSwitchTest.kt
index e7c6fc04f..494225234 100644
--- a/firebase-firestore/src/iosTest/kotlin/dev/gitlive/firebase/firestore/ContextSwitchTest.kt
+++ b/firebase-firestore/src/iosTest/kotlin/dev/gitlive/firebase/firestore/ContextSwitchTest.kt
@@ -121,28 +121,38 @@ class ContextSwitchTest {
},
) { data ->
- fun getDocument() = firestore.collection("fieldValuesOps")
+ val doc = firestore.collection("fieldValuesOps")
.document("fieldValuesOps")
- // store
- getDocument().set(strategy = TestData.serializer(), data = TestData(data.initial), merge = false)
-
- // append & verify
- getDocument().update(data.updates[0].op)
-
- var savedData = getDocument().get().data(TestData.serializer())
- assertEquals(data.updates[0].expected, savedData.values)
-
- // remove & verify
- getDocument().update(data.updates[1].op)
- savedData = getDocument().get().data(TestData.serializer())
- assertEquals(data.updates[1].expected, savedData.values)
+ try {
+ // store
+ doc.set(
+ strategy = TestData.serializer(),
+ data = TestData(data.initial),
+ merge = false,
+ )
- val list = getDocument().get().get(TestData::values.name, ListSerializer(Int.serializer()).nullable)
- assertEquals(data.updates[1].expected, list)
- // delete & verify
- getDocument().update(data.updates[2].op)
- val deletedList = getDocument().get().get(TestData::values.name, ListSerializer(Int.serializer()).nullable)
- assertEquals(data.updates[2].expected, deletedList)
+ // append & verify
+ doc.update(data.updates[0].op)
+
+ var savedData = doc.get().data(TestData.serializer())
+ assertEquals(data.updates[0].expected, savedData.values)
+
+ // remove & verify
+ doc.update(data.updates[1].op)
+ savedData = doc.get().data(TestData.serializer())
+ assertEquals(data.updates[1].expected, savedData.values)
+
+ val list = doc.get()
+ .get(TestData::values.name, ListSerializer(Int.serializer()).nullable)
+ assertEquals(data.updates[1].expected, list)
+ // delete & verify
+ doc.update(data.updates[2].op)
+ val deletedList = doc.get()
+ .get(TestData::values.name, ListSerializer(Int.serializer()).nullable)
+ assertEquals(data.updates[2].expected, deletedList)
+ } finally {
+ doc.delete()
+ }
}
}