Skip to content

Commit d59843f

Browse files
committed
Port the missing tests from Android.
1 parent c1519bc commit d59843f

File tree

4 files changed

+307
-2
lines changed

4 files changed

+307
-2
lines changed

Firestore/Swift/Tests/Integration/TypeTest.swift

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,21 @@ class TypeTest: FSTIntegrationTestCase {
228228
func testCanReadAndWriteDecimal128Fields() async throws {
229229
_ = try await expectRoundtrip(
230230
coll: collectionRef(),
231-
data: ["decimal128": Decimal128Value("-1.234e-5")]
231+
data: ["map": [
232+
"decimalSciPositive": Decimal128Value("1.2e3"),
233+
"decimalSciNegative": Decimal128Value("-1.2e3"),
234+
"decimalSciNegativeExponent": Decimal128Value("1.2e-3"),
235+
"decimalSciNegativeValueAndExponent": Decimal128Value("-1.2e-3"),
236+
"decimalSciExplicitPositiveExponent": Decimal128Value("1.2e+3"),
237+
"decimalFloatPositive": Decimal128Value("1.1"),
238+
"decimalIntNegative": Decimal128Value("-1"),
239+
"decimalZeroNegative": Decimal128Value("-0"),
240+
"decimalZeroInt": Decimal128Value("0"),
241+
"decimalZeroFloat": Decimal128Value("0.0"),
242+
"decimalNaN": Decimal128Value("NaN"),
243+
"decimalInfinityPositive": Decimal128Value("Infinity"),
244+
"decimalInfinityNegative": Decimal128Value("-Infinity"),
245+
]]
232246
)
233247
}
234248

@@ -280,7 +294,7 @@ class TypeTest: FSTIntegrationTestCase {
280294
func testCanReadAndWriteBsonFieldsInAnObject() async throws {
281295
_ = try await expectRoundtrip(
282296
coll: collectionRef(),
283-
data: ["array": [
297+
data: ["map": [
284298
"binary": BSONBinaryData(subtype: 1, data: Data([1, 2, 3])),
285299
"objectId": BSONObjectId("507f191e810c19729de860ea"),
286300
"bsonTimestamp": BSONTimestamp(seconds: 123, increment: 456),
@@ -330,6 +344,50 @@ class TypeTest: FSTIntegrationTestCase {
330344
}
331345
}
332346

347+
func testInvalidDecimal128ValuesGetsRejected() async throws {
348+
let docRef = collectionRef().document("test-doc")
349+
var errorMessage: String?
350+
351+
do {
352+
try await docRef.setData(["key": Decimal128Value("")])
353+
XCTFail("Expected error for invalid Decimal128Value")
354+
} catch {
355+
errorMessage = (error as NSError).userInfo[NSLocalizedDescriptionKey] as? String
356+
XCTAssertNotNil(errorMessage)
357+
XCTAssertTrue(errorMessage!.contains("Invalid decimal128 string"))
358+
}
359+
360+
errorMessage = nil
361+
do {
362+
try await docRef.setData(["key": Decimal128Value("abc")])
363+
XCTFail("Expected error for invalid Decimal128Value")
364+
} catch {
365+
errorMessage = (error as NSError).userInfo[NSLocalizedDescriptionKey] as? String
366+
XCTAssertNotNil(errorMessage)
367+
XCTAssertTrue(errorMessage!.contains("Invalid decimal128 string"))
368+
}
369+
370+
errorMessage = nil
371+
do {
372+
try await docRef.setData(["key": Decimal128Value("1 23.45")])
373+
XCTFail("Expected error for invalid Decimal128Value")
374+
} catch {
375+
errorMessage = (error as NSError).userInfo[NSLocalizedDescriptionKey] as? String
376+
XCTAssertNotNil(errorMessage)
377+
XCTAssertTrue(errorMessage!.contains("Invalid decimal128 string"))
378+
}
379+
380+
errorMessage = nil
381+
do {
382+
try await docRef.setData(["key": Decimal128Value("1e1234567890")])
383+
XCTFail("Expected error for invalid Decimal128Value")
384+
} catch {
385+
errorMessage = (error as NSError).userInfo[NSLocalizedDescriptionKey] as? String
386+
XCTAssertNotNil(errorMessage)
387+
XCTAssertTrue(errorMessage!.contains("Invalid decimal128 string"))
388+
}
389+
}
390+
333391
func testCanOrderValuesOfDifferentTypeOrderTogether() async throws {
334392
let collection = collectionRef()
335393
let testDocs: [String: [String: Any?]] = [

Firestore/core/test/unit/index/index_value_writer_test.cc

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,92 @@ TEST(IndexValueWriterTest, writeIndexValueSupportsDecimal128) {
319319
EXPECT_EQ(actual_bytes, expected_bytes);
320320
}
321321

322+
TEST(IndexValueWriterTest, writeIndexValueSupportsNegativeDecimal128) {
323+
// Value
324+
auto value = Decimal128("-1.2e3");
325+
326+
// Actual
327+
IndexEncodingBuffer encoder;
328+
WriteIndexValue(*value, encoder.ForKind(model::Segment::Kind::kAscending));
329+
auto& actual_bytes = encoder.GetEncodedBytes();
330+
331+
// Expected
332+
IndexEncodingBuffer expected_encoder;
333+
DirectionalIndexByteEncoder* index_byte_encoder =
334+
expected_encoder.ForKind(model::Segment::Kind::kAscending);
335+
index_byte_encoder->WriteLong(IndexType::kNumber);
336+
// We currently store a 64-bit double representation in the client-side index.
337+
index_byte_encoder->WriteDouble(-1200.0);
338+
index_byte_encoder->WriteInfinity();
339+
auto& expected_bytes = expected_encoder.GetEncodedBytes();
340+
341+
EXPECT_EQ(actual_bytes, expected_bytes);
342+
}
343+
344+
TEST(IndexValueWriterTest, writeIndexValueSupportsNaNDecimal128) {
345+
// Value
346+
auto value = Decimal128("NaN");
347+
348+
// Actual
349+
IndexEncodingBuffer encoder;
350+
WriteIndexValue(*value, encoder.ForKind(model::Segment::Kind::kAscending));
351+
auto& actual_bytes = encoder.GetEncodedBytes();
352+
353+
// Expected
354+
IndexEncodingBuffer expected_encoder;
355+
DirectionalIndexByteEncoder* index_byte_encoder =
356+
expected_encoder.ForKind(model::Segment::Kind::kAscending);
357+
index_byte_encoder->WriteLong(IndexType::kNan);
358+
index_byte_encoder->WriteInfinity();
359+
auto& expected_bytes = expected_encoder.GetEncodedBytes();
360+
361+
EXPECT_EQ(actual_bytes, expected_bytes);
362+
}
363+
364+
TEST(IndexValueWriterTest, writeIndexValueSupportsDecimal128Infinity) {
365+
// Value
366+
auto value = Decimal128("Infinity");
367+
368+
// Actual
369+
IndexEncodingBuffer encoder;
370+
WriteIndexValue(*value, encoder.ForKind(model::Segment::Kind::kAscending));
371+
auto& actual_bytes = encoder.GetEncodedBytes();
372+
373+
// Expected
374+
IndexEncodingBuffer expected_encoder;
375+
DirectionalIndexByteEncoder* index_byte_encoder =
376+
expected_encoder.ForKind(model::Segment::Kind::kAscending);
377+
index_byte_encoder->WriteLong(IndexType::kNumber);
378+
// We currently store a 64-bit double representation in the client-side index.
379+
index_byte_encoder->WriteDouble(std::stod("Infinity"));
380+
index_byte_encoder->WriteInfinity();
381+
auto& expected_bytes = expected_encoder.GetEncodedBytes();
382+
383+
EXPECT_EQ(actual_bytes, expected_bytes);
384+
}
385+
386+
TEST(IndexValueWriterTest, writeIndexValueSupportsDecimal128NegativeInfinity) {
387+
// Value
388+
auto value = Decimal128("-Infinity");
389+
390+
// Actual
391+
IndexEncodingBuffer encoder;
392+
WriteIndexValue(*value, encoder.ForKind(model::Segment::Kind::kAscending));
393+
auto& actual_bytes = encoder.GetEncodedBytes();
394+
395+
// Expected
396+
IndexEncodingBuffer expected_encoder;
397+
DirectionalIndexByteEncoder* index_byte_encoder =
398+
expected_encoder.ForKind(model::Segment::Kind::kAscending);
399+
index_byte_encoder->WriteLong(IndexType::kNumber);
400+
// We currently store a 64-bit double representation in the client-side index.
401+
index_byte_encoder->WriteDouble(std::stod("-Infinity"));
402+
index_byte_encoder->WriteInfinity();
403+
auto& expected_bytes = expected_encoder.GetEncodedBytes();
404+
405+
EXPECT_EQ(actual_bytes, expected_bytes);
406+
}
407+
322408
TEST(IndexValueWriterTest, writeIndexValueSupportsSmallestInt32) {
323409
// Value
324410
auto value = Int32(-2147483648);

Firestore/core/test/unit/local/leveldb_index_manager_test.cc

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1354,6 +1354,77 @@ TEST_F(LevelDbIndexManagerTest, IndexDecimal128Fields) {
13541354
});
13551355
}
13561356

1357+
TEST_F(LevelDbIndexManagerTest, IndexDecimal128FieldsWithPrecisionLoss) {
1358+
persistence_->Run("TestIndexDecimal128Fields", [&]() {
1359+
index_manager_->Start();
1360+
index_manager_->AddFieldIndex(
1361+
MakeFieldIndex("coll", "key", model::Segment::kAscending));
1362+
1363+
AddDoc("coll/doc1",
1364+
Map("key",
1365+
Decimal128("-0.1234567890123456789"))); // will be rounded to
1366+
// -0.12345678901234568
1367+
AddDoc("coll/doc2", Map("key", Decimal128("0")));
1368+
AddDoc("coll/doc3",
1369+
Map("key",
1370+
Decimal128("0.1234567890123456789"))); // will be rounded to
1371+
// 0.12345678901234568
1372+
1373+
auto base_query = Query("coll").AddingOrderBy(OrderBy("key"));
1374+
1375+
{
1376+
SCOPED_TRACE("Query Decimal128 with EqualTo filter");
1377+
auto query = base_query.AddingFilter(
1378+
Filter("key", "==", Decimal128("0.1234567890123456789")));
1379+
VerifyResults(query, {"coll/doc3"});
1380+
}
1381+
{
1382+
SCOPED_TRACE("Query Decimal128 with EqualTo filter with rounding error");
1383+
// Mismatch behaviour caused by rounding error. Firestore fetches the doc3
1384+
// from LevelDb as doc3 rounds to the same number, even though the actual
1385+
// number in doc3 is different.
1386+
auto query = base_query.AddingFilter(
1387+
Filter("key", "==", Decimal128("0.12345678901234568")));
1388+
VerifyResults(query, {"coll/doc3"});
1389+
}
1390+
1391+
// Operations that doesn't go up to 17 decimal digits of precision wouldn't
1392+
// be affected by this rounding errors.
1393+
{
1394+
SCOPED_TRACE("Query Decimal128 with NotEqualTo filter");
1395+
auto query =
1396+
base_query.AddingFilter(Filter("key", "!=", Decimal128("0.0")));
1397+
VerifyResults(query, {"coll/doc1", "coll/doc3"});
1398+
}
1399+
{
1400+
SCOPED_TRACE("Query Decimal128 with GreaterThanOrEqualTo filter");
1401+
auto query =
1402+
base_query.AddingFilter(Filter("key", ">=", Decimal128("1.23e-1")));
1403+
VerifyResults(query, {"coll/doc3"});
1404+
}
1405+
{
1406+
SCOPED_TRACE("Query Decimal128 with LessThanOrEqualTo filter");
1407+
auto query =
1408+
base_query.AddingFilter(Filter("key", "<=", Decimal128("-1.23e-1")));
1409+
VerifyResults(query, {"coll/doc1"});
1410+
}
1411+
{
1412+
SCOPED_TRACE(
1413+
"Query Decimal128 with GreaterThan filter and empty result set");
1414+
auto query =
1415+
base_query.AddingFilter(Filter("key", ">", Decimal128("1.2e3")));
1416+
VerifyResults(query, {});
1417+
}
1418+
{
1419+
SCOPED_TRACE(
1420+
"Query Decimal128 with LessThan filter and empty result set");
1421+
auto query =
1422+
base_query.AddingFilter(Filter("key", "<", Decimal128("-1.2e3")));
1423+
VerifyResults(query, {});
1424+
}
1425+
});
1426+
}
1427+
13571428
TEST_F(LevelDbIndexManagerTest, IndexRegexFields) {
13581429
persistence_->Run("TestIndexRegexFields", [&]() {
13591430
index_manager_->Start();

Firestore/core/test/unit/local/leveldb_local_store_test.cc

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -807,6 +807,96 @@ TEST_F(LevelDbLocalStoreTest, IndexesDecimal128) {
807807
FSTAssertQueryReturned("coll/doc4", "coll/doc5");
808808
}
809809

810+
TEST_F(LevelDbLocalStoreTest, IndexesDecimal128WithPrecisionLoss) {
811+
FieldIndex index = MakeFieldIndex("coll", 0, FieldIndex::InitialState(),
812+
"key", model::Segment::Kind::kAscending);
813+
ConfigureFieldIndexes({index});
814+
815+
WriteMutation(SetMutation(
816+
"coll/doc1",
817+
Map("key",
818+
Decimal128("-0.1234567890123456789")))); // will be rounded to
819+
// -0.12345678901234568
820+
WriteMutation(SetMutation("coll/doc2", Map("key", Decimal128("0"))));
821+
WriteMutation(SetMutation(
822+
"coll/doc3",
823+
Map("key", Decimal128("0.1234567890123456789")))); // will be rounded to
824+
// 0.12345678901234568
825+
826+
BackfillIndexes();
827+
828+
core::Query query =
829+
testutil::Query("coll").AddingOrderBy(OrderBy("key", "asc"));
830+
ExecuteQuery(query);
831+
FSTAssertOverlaysRead(/* byKey= */ 3, /* byCollection= */ 0);
832+
FSTAssertOverlayTypes(
833+
OverlayTypeMap({{Key("coll/doc1"), model::Mutation::Type::Set},
834+
{Key("coll/doc2"), model::Mutation::Type::Set},
835+
{Key("coll/doc3"), model::Mutation::Type::Set}}));
836+
FSTAssertQueryReturned("coll/doc1", "coll/doc2", "coll/doc3");
837+
838+
query = testutil::Query("coll").AddingFilter(
839+
Filter("key", "==", Decimal128("0.1234567890123456789")));
840+
ExecuteQuery(query);
841+
FSTAssertOverlaysRead(/* byKey= */ 1, /* byCollection= */ 0);
842+
FSTAssertOverlayTypes(
843+
OverlayTypeMap({{Key("coll/doc3"), model::Mutation::Type::Set}}));
844+
FSTAssertQueryReturned("coll/doc3");
845+
846+
// Mismatch behaviour caused by rounding error. Firestore fetches the doc3
847+
// from LevelDB as doc3 rounds to the same number, but, it is not presented in
848+
// the final query result.
849+
query = testutil::Query("coll").AddingFilter(
850+
Filter("key", "==", Decimal128("0.12345678901234568")));
851+
ExecuteQuery(query);
852+
FSTAssertOverlaysRead(/* byKey= */ 1, /* byCollection= */ 0);
853+
FSTAssertOverlayTypes(
854+
OverlayTypeMap({{Key("coll/doc3"), model::Mutation::Type::Set}}));
855+
FSTAssertQueryReturned();
856+
857+
// Operations that doesn't go up to 17 decimal digits of precision wouldn't be
858+
// affected by rounding errors.
859+
860+
query = testutil::Query("coll").AddingFilter(
861+
Filter("key", "!=", Decimal128("0")));
862+
ExecuteQuery(query);
863+
FSTAssertOverlaysRead(/* byKey= */ 2, /* byCollection= */ 0);
864+
FSTAssertOverlayTypes(
865+
OverlayTypeMap({{Key("coll/doc1"), model::Mutation::Type::Set},
866+
{Key("coll/doc3"), model::Mutation::Type::Set}}));
867+
FSTAssertQueryReturned("coll/doc1", "coll/doc3");
868+
869+
query = testutil::Query("coll").AddingFilter(
870+
Filter("key", ">=", Decimal128("1.23e-1")));
871+
ExecuteQuery(query);
872+
FSTAssertOverlaysRead(/* byKey= */ 1, /* byCollection= */ 0);
873+
FSTAssertOverlayTypes(
874+
OverlayTypeMap({{Key("coll/doc3"), model::Mutation::Type::Set}}));
875+
FSTAssertQueryReturned("coll/doc3");
876+
877+
query = testutil::Query("coll").AddingFilter(
878+
Filter("key", "<=", Decimal128("-1.23e-1")));
879+
ExecuteQuery(query);
880+
FSTAssertOverlaysRead(/* byKey= */ 1, /* byCollection= */ 0);
881+
FSTAssertOverlayTypes(
882+
OverlayTypeMap({{Key("coll/doc1"), model::Mutation::Type::Set}}));
883+
FSTAssertQueryReturned("coll/doc1");
884+
885+
query = testutil::Query("coll").AddingFilter(
886+
Filter("key", ">", Decimal128("1.2e3")));
887+
ExecuteQuery(query);
888+
FSTAssertOverlaysRead(/* byKey= */ 0, /* byCollection= */ 0);
889+
FSTAssertOverlayTypes(OverlayTypeMap());
890+
FSTAssertQueryReturned();
891+
892+
query = testutil::Query("coll").AddingFilter(
893+
Filter("key", "<", Decimal128("-1.2e3")));
894+
ExecuteQuery(query);
895+
FSTAssertOverlaysRead(/* byKey= */ 0, /* byCollection= */ 0);
896+
FSTAssertOverlayTypes(OverlayTypeMap());
897+
FSTAssertQueryReturned();
898+
}
899+
810900
TEST_F(LevelDbLocalStoreTest, IndexesMinKey) {
811901
FieldIndex index = MakeFieldIndex("coll", 0, FieldIndex::InitialState(),
812902
"key", model::Segment::Kind::kAscending);

0 commit comments

Comments
 (0)