From 520a0c0a84c34897aae10564ed48ec3efc508fbf Mon Sep 17 00:00:00 2001 From: rsgowman Date: Tue, 15 May 2018 17:02:08 -0400 Subject: [De]serialize empty Document instances (#1262) * [De]serialize empty Document instances Still TODO: - non-empty - NoDocument - ErrorHandling --- .../firebase/firestore/remote/serializer_test.cc | 115 ++++++++++++++++++++- 1 file changed, 114 insertions(+), 1 deletion(-) (limited to 'Firestore/core/test') diff --git a/Firestore/core/test/firebase/firestore/remote/serializer_test.cc b/Firestore/core/test/firebase/firestore/remote/serializer_test.cc index 38277e2..a147309 100644 --- a/Firestore/core/test/firebase/firestore/remote/serializer_test.cc +++ b/Firestore/core/test/firebase/firestore/remote/serializer_test.cc @@ -33,9 +33,11 @@ #include #include "Firestore/Protos/cpp/google/firestore/v1beta1/document.pb.h" +#include "Firestore/Protos/cpp/google/firestore/v1beta1/firestore.pb.h" #include "Firestore/core/include/firebase/firestore/firestore_errors.h" #include "Firestore/core/include/firebase/firestore/timestamp.h" #include "Firestore/core/src/firebase/firestore/model/field_value.h" +#include "Firestore/core/src/firebase/firestore/model/snapshot_version.h" #include "Firestore/core/src/firebase/firestore/timestamp_internal.h" #include "Firestore/core/src/firebase/firestore/util/status.h" #include "Firestore/core/src/firebase/firestore/util/statusor.h" @@ -48,8 +50,13 @@ using firebase::Timestamp; using firebase::TimestampInternal; using firebase::firestore::FirestoreErrorCode; using firebase::firestore::model::DatabaseId; +using firebase::firestore::model::Document; +using firebase::firestore::model::DocumentKey; using firebase::firestore::model::FieldValue; +using firebase::firestore::model::MaybeDocument; +using firebase::firestore::model::NoDocument; using firebase::firestore::model::ObjectValue; +using firebase::firestore::model::SnapshotVersion; using firebase::firestore::remote::Serializer; using firebase::firestore::testutil::Key; using firebase::firestore::util::Status; @@ -73,6 +80,7 @@ TEST(Serializer, CanLinkToNanopb) { class SerializerTest : public ::testing::Test { public: SerializerTest() : serializer(kDatabaseId) { + msg_diff.ReportDifferencesToString(&message_differences); } const DatabaseId kDatabaseId{"p", "d"}; @@ -92,6 +100,15 @@ class SerializerTest : public ::testing::Test { ExpectDeserializationRoundTrip(model, proto, type); } + void ExpectRoundTrip( + const DocumentKey& key, + const FieldValue& value, + const SnapshotVersion& update_time, + const google::firestore::v1beta1::BatchGetDocumentsResponse& proto) { + ExpectSerializationRoundTrip(key, value, update_time, proto); + ExpectDeserializationRoundTrip(key, value, update_time, proto); + } + /** * Checks the status. Don't use directly; use one of the relevant macros * instead. eg: @@ -145,6 +162,16 @@ class SerializerTest : public ::testing::Test { return bytes; } + std::vector EncodeDocument(Serializer* serializer, + const DocumentKey& key, + const FieldValue& value) { + std::vector bytes; + Status status = + serializer->EncodeDocument(key, value.object_value(), &bytes); + EXPECT_OK(status); + return bytes; + } + void Mutate(uint8_t* byte, uint8_t expected_initial_value, uint8_t new_value) { @@ -202,7 +229,7 @@ class SerializerTest : public ::testing::Test { google::firestore::v1beta1::Value actual_proto; bool ok = actual_proto.ParseFromArray(bytes.data(), bytes.size()); EXPECT_TRUE(ok); - EXPECT_TRUE(MessageDifferencer::Equals(proto, actual_proto)); + EXPECT_TRUE(msg_diff.Compare(proto, actual_proto)) << message_differences; } void ExpectDeserializationRoundTrip( @@ -220,6 +247,65 @@ class SerializerTest : public ::testing::Test { EXPECT_EQ(type, actual_model.type()); EXPECT_EQ(model, actual_model); } + + void ExpectSerializationRoundTrip( + const DocumentKey& key, + const FieldValue& value, + const SnapshotVersion& update_time, + const google::firestore::v1beta1::BatchGetDocumentsResponse& proto) { + std::vector bytes = EncodeDocument(&serializer, key, value); + google::firestore::v1beta1::Document actual_proto; + bool ok = actual_proto.ParseFromArray(bytes.data(), bytes.size()); + EXPECT_TRUE(ok); + + // TODO(rsgowman): Right now, we only support Document (and don't support + // NoDocument). That should change in the next PR or so. + EXPECT_TRUE(proto.has_found()); + + // Slight weirdness: When we *encode* a document for sending it to the + // backend, we don't encode the update_time (or create_time). But when we + // *decode* a document, we *do* decode the update_time (though we still + // ignore the create_time). Therefore, we'll verify the update_time + // independently, and then strip it out before comparing the rest. + EXPECT_FALSE(actual_proto.has_create_time()); + EXPECT_EQ(update_time.timestamp().seconds(), + proto.found().update_time().seconds()); + EXPECT_EQ(update_time.timestamp().nanoseconds(), + proto.found().update_time().nanos()); + google::firestore::v1beta1::BatchGetDocumentsResponse proto_copy{proto}; + proto_copy.mutable_found()->clear_update_time(); + proto_copy.mutable_found()->clear_create_time(); + + EXPECT_TRUE(msg_diff.Compare(proto_copy.found(), actual_proto)) + << message_differences; + } + + void ExpectDeserializationRoundTrip( + const DocumentKey& key, + const FieldValue& value, + const SnapshotVersion& update_time, + const google::firestore::v1beta1::BatchGetDocumentsResponse& proto) { + size_t size = proto.ByteSizeLong(); + std::vector bytes(size); + bool status = proto.SerializeToArray(bytes.data(), size); + EXPECT_TRUE(status); + StatusOr> actual_model_status = + serializer.DecodeMaybeDocument(bytes); + EXPECT_OK(actual_model_status); + std::unique_ptr actual_model = + std::move(actual_model_status).ValueOrDie(); + + // TODO(rsgowman): Right now, we only support Document (and don't support + // NoDocument). That should change in the next PR or so. + EXPECT_EQ(MaybeDocument::Type::Document, actual_model->type()); + Document* actual_doc_model = static_cast(actual_model.get()); + EXPECT_EQ(key, actual_model->key()); + EXPECT_EQ(value, actual_doc_model->data()); + EXPECT_EQ(update_time, actual_model->version()); + } + + std::string message_differences; + MessageDifferencer msg_diff; }; TEST_F(SerializerTest, EncodesNull) { @@ -517,5 +603,32 @@ TEST_F(SerializerTest, BadKey) { } } +TEST_F(SerializerTest, EncodesEmptyDocument) { + DocumentKey key = DocumentKey::FromPathString("path/to/the/doc"); + FieldValue empty_value = FieldValue::ObjectValueFromMap({}); + SnapshotVersion update_time = SnapshotVersion{{1234, 5678}}; + + google::firestore::v1beta1::BatchGetDocumentsResponse proto; + google::firestore::v1beta1::Document* doc_proto = proto.mutable_found(); + doc_proto->set_name(serializer.EncodeKey(key)); + doc_proto->mutable_fields(); + + google::protobuf::Timestamp* update_time_proto = + doc_proto->mutable_update_time(); + update_time_proto->set_seconds(1234); + update_time_proto->set_nanos(5678); + + // Note that we ignore create time in our serializer. We never set it, and + // never read it (other than to throw it away). But the server could (and + // probably does) set it, so we need to be able to discard it properly. The + // ExpectRoundTrip deals with this asymmetry. + google::protobuf::Timestamp* create_time_proto = + doc_proto->mutable_create_time(); + create_time_proto->set_seconds(8765); + create_time_proto->set_nanos(4321); + + ExpectRoundTrip(key, empty_value, update_time, proto); +} + // TODO(rsgowman): Test [en|de]coding multiple protos into the same output // vector. -- cgit v1.2.3