Skip to content

Commit e0a73e6

Browse files
committed
Error Handling Tests
1 parent 5e2192e commit e0a73e6

File tree

1 file changed

+166
-0
lines changed

1 file changed

+166
-0
lines changed
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.firebase.firestore.pipeline
16+
17+
import com.google.common.truth.Truth.assertThat
18+
import com.google.firebase.firestore.RealtimePipelineSource
19+
import com.google.firebase.firestore.TestUtil
20+
import com.google.firebase.firestore.pipeline.Expr.Companion.and
21+
import com.google.firebase.firestore.pipeline.Expr.Companion.constant
22+
import com.google.firebase.firestore.pipeline.Expr.Companion.divide
23+
import com.google.firebase.firestore.pipeline.Expr.Companion.eq
24+
import com.google.firebase.firestore.pipeline.Expr.Companion.field
25+
import com.google.firebase.firestore.pipeline.Expr.Companion.or
26+
import com.google.firebase.firestore.pipeline.Expr.Companion.xor
27+
import com.google.firebase.firestore.runPipeline
28+
import com.google.firebase.firestore.testutil.TestUtilKtx.doc
29+
import kotlinx.coroutines.flow.flowOf
30+
import kotlinx.coroutines.flow.toList
31+
import kotlinx.coroutines.runBlocking
32+
import org.junit.Test
33+
import org.junit.runner.RunWith
34+
import org.robolectric.RobolectricTestRunner
35+
36+
@RunWith(RobolectricTestRunner::class)
37+
internal class ErrorHandlingTests {
38+
39+
private val db = TestUtil.firestore()
40+
41+
@Test
42+
fun `where partial error or`(): Unit = runBlocking {
43+
val doc1 = doc("k/1", 1000, mapOf("a" to "true", "b" to true, "c" to false))
44+
val doc2 = doc("k/2", 1000, mapOf("a" to true, "b" to "true", "c" to false))
45+
val doc3 = doc("k/3", 1000, mapOf("a" to true, "b" to false, "c" to "true"))
46+
val doc4 = doc("k/4", 1000, mapOf("a" to "true", "b" to "true", "c" to true))
47+
val doc5 = doc("k/5", 1000, mapOf("a" to "true", "b" to true, "c" to "true"))
48+
val doc6 = doc("k/6", 1000, mapOf("a" to true, "b" to "true", "c" to "true"))
49+
val documents = listOf(doc1, doc2, doc3, doc4, doc5, doc6)
50+
51+
val pipeline =
52+
RealtimePipelineSource(db)
53+
.collection("k")
54+
.where(
55+
or(
56+
eq(field("a"), constant(true)),
57+
eq(field("b"), constant(true)),
58+
eq(field("c"), constant(true))
59+
)
60+
)
61+
62+
val result = runPipeline(db, pipeline, flowOf(*documents.toTypedArray())).toList()
63+
// In Firestore, comparisons between different types are generally false.
64+
// The OR evaluates to true if *any* of the fields 'a', 'b', or 'c' is the
65+
// boolean value `true`. All documents have at least one field that is boolean
66+
// `true`.
67+
assertThat(result).containsExactlyElementsIn(listOf(doc1, doc2, doc3, doc4, doc5, doc6))
68+
}
69+
70+
@Test
71+
fun `where partial error and`(): Unit = runBlocking {
72+
val doc1 = doc("k/1", 1000, mapOf("a" to "true", "b" to true, "c" to false))
73+
val doc2 = doc("k/2", 1000, mapOf("a" to true, "b" to "true", "c" to false))
74+
val doc3 = doc("k/3", 1000, mapOf("a" to true, "b" to false, "c" to "true"))
75+
val doc4 = doc("k/4", 1000, mapOf("a" to "true", "b" to "true", "c" to true))
76+
val doc5 = doc("k/5", 1000, mapOf("a" to "true", "b" to true, "c" to "true"))
77+
val doc6 = doc("k/6", 1000, mapOf("a" to true, "b" to "true", "c" to "true"))
78+
val doc7 = doc("k/7", 1000, mapOf("a" to true, "b" to true, "c" to true))
79+
val documents = listOf(doc1, doc2, doc3, doc4, doc5, doc6, doc7)
80+
81+
val pipeline =
82+
RealtimePipelineSource(db)
83+
.collection("k")
84+
.where(
85+
and(
86+
eq(field("a"), constant(true)),
87+
eq(field("b"), constant(true)),
88+
eq(field("c"), constant(true))
89+
)
90+
)
91+
92+
val result = runPipeline(db, pipeline, flowOf(*documents.toTypedArray())).toList()
93+
// AND requires all conditions to be true. Type mismatches evaluate EqExpr to
94+
// false. Only doc7 has a=true, b=true, AND c=true.
95+
assertThat(result).containsExactly(doc7)
96+
}
97+
98+
@Test
99+
fun `where partial error xor`(): Unit = runBlocking {
100+
val doc1 = doc("k/1", 1000, mapOf("a" to "true", "b" to true, "c" to false))
101+
val doc2 = doc("k/2", 1000, mapOf("a" to true, "b" to "true", "c" to false))
102+
val doc3 = doc("k/3", 1000, mapOf("a" to true, "b" to false, "c" to "true"))
103+
val doc4 = doc("k/4", 1000, mapOf("a" to "true", "b" to "true", "c" to true))
104+
val doc5 = doc("k/5", 1000, mapOf("a" to "true", "b" to true, "c" to "true"))
105+
val doc6 = doc("k/6", 1000, mapOf("a" to true, "b" to "true", "c" to "true"))
106+
val doc7 = doc("k/7", 1000, mapOf("a" to true, "b" to true, "c" to true))
107+
val documents = listOf(doc1, doc2, doc3, doc4, doc5, doc6, doc7)
108+
109+
val pipeline =
110+
RealtimePipelineSource(db)
111+
.collection("k")
112+
.where(
113+
xor(
114+
eq(field("a"), constant(true)),
115+
eq(field("b"), constant(true)),
116+
eq(field("c"), constant(true))
117+
)
118+
)
119+
120+
val result = runPipeline(db, pipeline, flowOf(*documents.toTypedArray())).toList()
121+
// XOR is true if an odd number of inputs are true.
122+
// Assuming type mismatches evaluate EqExpr to false:
123+
// doc1: F xor T xor F = T
124+
// doc2: T xor F xor F = T
125+
// doc3: T xor F xor F = T
126+
// doc4: F xor F xor T = T
127+
// doc5: F xor T xor F = T
128+
// doc6: T xor F xor F = T
129+
// doc7: T xor T xor T = T
130+
assertThat(result).containsExactlyElementsIn(listOf(doc1, doc2, doc3, doc4, doc5, doc6, doc7))
131+
}
132+
133+
@Test
134+
fun `where not error`(): Unit = runBlocking {
135+
val doc1 = doc("k/1", 1000, mapOf("a" to false))
136+
val doc2 = doc("k/2", 1000, mapOf("a" to "true"))
137+
val doc3 = doc("k/3", 1000, mapOf("b" to true))
138+
val documents = listOf(doc1, doc2, doc3)
139+
140+
// This test case in C++ was adjusted to match a TS behavior,
141+
// resulting in a condition `field("a") == false`.
142+
val pipeline = RealtimePipelineSource(db).collection("k").where(eq(field("a"), constant(false)))
143+
144+
val result = runPipeline(db, pipeline, flowOf(*documents.toTypedArray())).toList()
145+
// Only doc1 has a == false.
146+
assertThat(result).containsExactly(doc1)
147+
}
148+
149+
@Test
150+
fun `where error producing function returns empty`(): Unit = runBlocking {
151+
val doc1 = doc("users/a", 1000, mapOf("name" to "alice", "age" to true))
152+
val doc2 = doc("users/b", 1000, mapOf("name" to "bob", "age" to "42"))
153+
val doc3 = doc("users/c", 1000, mapOf("name" to "charlie", "age" to 0))
154+
val documents = listOf(doc1, doc2, doc3)
155+
156+
val pipeline =
157+
RealtimePipelineSource(db)
158+
.collection("k")
159+
.where(eq(divide(constant("100"), constant("50")), constant(2L)))
160+
161+
val result = runPipeline(db, pipeline, flowOf(*documents.toTypedArray())).toList()
162+
// Division of string constants should cause an evaluation error,
163+
// leading to no documents matching.
164+
assertThat(result).isEmpty()
165+
}
166+
}

0 commit comments

Comments
 (0)