def test_add_document_with_different_read_times(self): bundle = FirestoreBundle("test") doc = _test_helpers.build_document_snapshot( client=_test_helpers.make_client(), data={"version": 1}, read_time=_test_helpers.build_test_timestamp(second=1), ) # Create another reference to the same document, but with new # data and a more recent `read_time` doc_refreshed = _test_helpers.build_document_snapshot( client=_test_helpers.make_client(), data={"version": 2}, read_time=_test_helpers.build_test_timestamp(second=2), ) bundle.add_document(doc) self.assertEqual( bundle.documents[self.doc_key].snapshot._data, {"version": 1}, ) bundle.add_document(doc_refreshed) self.assertEqual( bundle.documents[self.doc_key].snapshot._data, {"version": 2}, )
def test_chunkify(self): client = _test_helpers.make_client() col = client.collection("my-collection") client._firestore_api_internal = mock.Mock(spec=["run_query"]) results = [] for index in range(10): results.append( RunQueryResponse(document=Document( name= f"projects/project-project/databases/(default)/documents/my-collection/{index}", ), ), ) chunks = [ results[:3], results[3:6], results[6:9], results[9:], ] def _get_chunk(*args, **kwargs): return iter(chunks.pop(0)) client._firestore_api_internal.run_query.side_effect = _get_chunk counter = 0 expected_lengths = [3, 3, 3, 1] for chunk in col._chunkify(3): msg = f"Expected chunk of length {expected_lengths[counter]} at index {counter}. Saw {len(chunk)}." self.assertEqual(len(chunk), expected_lengths[counter], msg) counter += 1
def test_invalid_json(self, fnc): from google.cloud.firestore_v1 import _helpers client = _test_helpers.make_client() fnc.return_value = iter([{}]) with pytest.raises(ValueError): _helpers.deserialize_bundle("does not matter", client)
def test_not_metadata_first(self, fnc): from google.cloud.firestore_v1 import _helpers client = _test_helpers.make_client() fnc.return_value = iter([{"document": {}}]) with pytest.raises(ValueError): _helpers.deserialize_bundle("does not matter", client)
def _list_documents_helper(self, page_size=None, retry=None, timeout=None): from google.cloud.firestore_v1 import _helpers as _fs_v1_helpers from google.api_core.page_iterator import Iterator from google.api_core.page_iterator import Page from google.cloud.firestore_v1.document import DocumentReference from google.cloud.firestore_v1.services.firestore.client import FirestoreClient from google.cloud.firestore_v1.types.document import Document class _Iterator(Iterator): def __init__(self, pages): super(_Iterator, self).__init__(client=None) self._pages = pages def _next_page(self): if self._pages: page, self._pages = self._pages[0], self._pages[1:] return Page(self, page, self.item_to_value) client = _test_helpers.make_client() template = client._database_string + "/documents/{}" document_ids = ["doc-1", "doc-2"] documents = [ Document(name=template.format(document_id)) for document_id in document_ids ] iterator = _Iterator(pages=[documents]) api_client = mock.create_autospec(FirestoreClient) api_client.list_documents.return_value = iterator client._firestore_api_internal = api_client collection = self._make_one("collection", client=client) kwargs = _fs_v1_helpers.make_retry_timeout_kwargs(retry, timeout) if page_size is not None: documents = list( collection.list_documents(page_size=page_size, **kwargs)) else: documents = list(collection.list_documents(**kwargs)) # Verify the response and the mocks. self.assertEqual(len(documents), len(document_ids)) for document, document_id in zip(documents, document_ids): self.assertIsInstance(document, DocumentReference) self.assertEqual(document.parent, collection) self.assertEqual(document.id, document_id) parent, _ = collection._parent_info() api_client.list_documents.assert_called_once_with( request={ "parent": parent, "collection_id": collection.id, "page_size": page_size, "show_missing": True, "mask": { "field_paths": None }, }, metadata=client._rpc_metadata, **kwargs, )
def test_add_document(self): from google.cloud.firestore_bundle import FirestoreBundle bundle = FirestoreBundle("test") doc = _test_helpers.build_document_snapshot( client=_test_helpers.make_client()) bundle.add_document(doc) assert bundle.documents[self.doc_key].snapshot == doc
def test_not_actually_a_bundle_at_all(self): client = _test_helpers.make_client() self.assertRaises( ValueError, _helpers.deserialize_bundle, "{}", client, )
def test_invalid_bundle_start(self, fnc, _): from google.cloud.firestore_v1 import _helpers client = _test_helpers.make_client() # invalid bc first element must be of key `metadata` fnc.return_value = [{"document": {}}] with pytest.raises(ValueError): _helpers.deserialize_bundle("does not matter", client)
def test_invalid_bundle_element_type(self, fnc, _): from google.cloud.firestore_v1 import _helpers client = _test_helpers.make_client() # invalid bc `wtfisthis?` is obviously invalid fnc.return_value = [{"metadata": {"id": "asdf"}}, {"wtfisthis?": {}}] with pytest.raises(ValueError): _helpers.deserialize_bundle("does not matter", client)
def test_invalid_bundle(self, fnc, _): from google.cloud.firestore_v1 import _helpers client = _test_helpers.make_client() # invalid bc `document` must follow `document_metadata` fnc.return_value = [{"metadata": {"id": "asdf"}}, {"document": {}}] with pytest.raises(ValueError): _helpers.deserialize_bundle("does not matter", client)
def test_add_auto_assigned(): from google.cloud.firestore_v1.types import document from google.cloud.firestore_v1.document import DocumentReference from google.cloud.firestore_v1 import SERVER_TIMESTAMP from google.cloud.firestore_v1._helpers import pbs_for_create from tests.unit.v1 import _test_helpers # Create a minimal fake GAPIC add attach it to a real client. firestore_api = mock.Mock(spec=["create_document", "commit"]) write_result = mock.Mock( update_time=mock.sentinel.update_time, spec=["update_time"] ) commit_response = mock.Mock( write_results=[write_result], spec=["write_results", "commit_time"], commit_time=mock.sentinel.commit_time, ) firestore_api.commit.return_value = commit_response create_doc_response = document.Document() firestore_api.create_document.return_value = create_doc_response client = _test_helpers.make_client() client._firestore_api_internal = firestore_api # Actually make a collection. collection = _make_collection_reference( "grand-parent", "parent", "child", client=client ) # Actually call add() on our collection; include a transform to make # sure transforms during adds work. document_data = {"been": "here", "now": SERVER_TIMESTAMP} patch = mock.patch("google.cloud.firestore_v1.base_collection._auto_id") random_doc_id = "DEADBEEF" with patch as patched: patched.return_value = random_doc_id update_time, document_ref = collection.add(document_data) # Verify the response and the mocks. assert update_time is mock.sentinel.update_time assert isinstance(document_ref, DocumentReference) assert document_ref._client is client expected_path = collection._path + (random_doc_id,) assert document_ref._path == expected_path write_pbs = pbs_for_create(document_ref._document_path, document_data) firestore_api.commit.assert_called_once_with( request={ "database": client._database_string, "writes": write_pbs, "transaction": None, }, metadata=client._rpc_metadata, ) # Since we generate the ID locally, we don't call 'create_document'. firestore_api.create_document.assert_not_called()
def test_not_metadata_first(self, fnc): client = _test_helpers.make_client() fnc.return_value = iter([{"document": {}}]) self.assertRaises( ValueError, _helpers.deserialize_bundle, "does not matter", client, )
def test_invalid_json(self, fnc): client = _test_helpers.make_client() fnc.return_value = iter([{}]) self.assertRaises( ValueError, _helpers.deserialize_bundle, "does not matter", client, )
def test_add_invalid_bundle_element_type(self): from google.cloud.firestore_bundle import FirestoreBundle from google.cloud.firestore_bundle import BundleElement client = _test_helpers.make_client() bundle = FirestoreBundle("asdf") with pytest.raises(ValueError): bundle._add_bundle_element(BundleElement(), client=client, type="asdf")
def test_invalid_bundle_element_type(self, fnc, _): client = _test_helpers.make_client() # invalid bc `wtfisthis?` is obviously invalid fnc.return_value = [{"metadata": {"id": "asdf"}}, {"wtfisthis?": {}}] self.assertRaises( ValueError, _helpers.deserialize_bundle, "does not matter", client, )
def test_add_invalid_bundle_element_type(self): client = _test_helpers.make_client() bundle = FirestoreBundle("asdf") self.assertRaises( ValueError, bundle._add_bundle_element, BundleElement(), client=client, type="asdf", )
def test_invalid_bundle(self, fnc, _): client = _test_helpers.make_client() # invalid bc `document` must follow `document_metadata` fnc.return_value = [{"metadata": {"id": "asdf"}}, {"document": {}}] self.assertRaises( ValueError, _helpers.deserialize_bundle, "does not matter", client, )
def test_invalid_bundle_start(self, fnc, _): client = _test_helpers.make_client() # invalid bc first element must be of key `metadata` fnc.return_value = [{"document": {}}] self.assertRaises( ValueError, _helpers.deserialize_bundle, "does not matter", client, )
def test_add_older_document(self): bundle = FirestoreBundle("test") new_doc = _test_helpers.build_document_snapshot( data={"version": 2}, client=_test_helpers.make_client(), read_time=Timestamp(seconds=1, nanos=2), ) bundle.add_document(new_doc) self.assertEqual( bundle.documents[self.doc_key].snapshot._data["version"], 2) # Builds the same ID by default old_doc = _test_helpers.build_document_snapshot( data={"version": 1}, client=_test_helpers.make_client(), read_time=Timestamp(seconds=1, nanos=1), ) bundle.add_document(old_doc) self.assertEqual( bundle.documents[self.doc_key].snapshot._data["version"], 2)
def test_add_older_document(self): from google.protobuf.timestamp_pb2 import Timestamp # type: ignore from google.cloud.firestore_bundle import FirestoreBundle bundle = FirestoreBundle("test") new_doc = _test_helpers.build_document_snapshot( data={"version": 2}, client=_test_helpers.make_client(), read_time=Timestamp(seconds=1, nanos=2), ) bundle.add_document(new_doc) assert bundle.documents[self.doc_key].snapshot._data["version"] == 2 # Builds the same ID by default old_doc = _test_helpers.build_document_snapshot( data={"version": 1}, client=_test_helpers.make_client(), read_time=Timestamp(seconds=1, nanos=1), ) bundle.add_document(old_doc) assert bundle.documents[self.doc_key].snapshot._data["version"] == 2
def test_unexpected_termination(self, fnc, _): client = _test_helpers.make_client() # invalid bc `document_metadata` must be followed by a `document` fnc.return_value = [{ "metadata": { "id": "asdf" } }, { "documentMetadata": {} }] self.assertRaises( ValueError, _helpers.deserialize_bundle, "does not matter", client, )
def test_valid_passes(self, fnc, _): client = _test_helpers.make_client() fnc.return_value = [ { "metadata": { "id": "asdf" } }, { "documentMetadata": {} }, { "document": {} }, ] _helpers.deserialize_bundle("does not matter", client)
def _add_helper(retry=None, timeout=None): from google.cloud.firestore_v1.document import DocumentReference from google.cloud.firestore_v1 import _helpers as _fs_v1_helpers from tests.unit.v1 import _test_helpers # Create a minimal fake GAPIC with a dummy response. firestore_api = mock.Mock(spec=["commit"]) write_result = mock.Mock( update_time=mock.sentinel.update_time, spec=["update_time"] ) commit_response = mock.Mock( write_results=[write_result], spec=["write_results", "commit_time"], commit_time=mock.sentinel.commit_time, ) firestore_api.commit.return_value = commit_response # Attach the fake GAPIC to a real client. client = _test_helpers.make_client() client._firestore_api_internal = firestore_api # Actually make a collection and call add(). collection = _make_collection_reference("parent", client=client) document_data = {"zorp": 208.75, "i-did-not": b"know that"} doc_id = "child" kwargs = _fs_v1_helpers.make_retry_timeout_kwargs(retry, timeout) update_time, document_ref = collection.add( document_data, document_id=doc_id, **kwargs ) # Verify the response and the mocks. assert update_time is mock.sentinel.update_time assert isinstance(document_ref, DocumentReference) assert document_ref._client is client assert document_ref._path == (collection.id, doc_id) write_pb = _write_pb_for_create(document_ref._document_path, document_data) firestore_api.commit.assert_called_once_with( request={ "database": client._database_string, "writes": [write_pb], "transaction": None, }, metadata=client._rpc_metadata, **kwargs, )
def test_valid_passes(self, fnc, _): from google.cloud.firestore_v1 import _helpers client = _test_helpers.make_client() fnc.return_value = [ { "metadata": { "id": "asdf" } }, { "documentMetadata": {} }, { "document": {} }, ] _helpers.deserialize_bundle("does not matter", client)
def test_deserialize_from_seconds_nanos(self): """Some SDKs (Node) serialize Timestamp values to '{"seconds": 123, "nanos": 456}', instead of an ISO-formatted string. This tests deserialization from that format.""" client = _test_helpers.make_client(project_name="fir-bundles-test") _serialized: str = ( '139{"metadata":{"id":"test-bundle","createTime":' + '{"seconds":"1616434660","nanos":913764000},"version":1,"totalDocuments"' + ':1,"totalBytes":"829"}}224{"namedQuery":{"name":"self","bundledQuery":' + '{"parent":"projects/fir-bundles-test/databases/(default)/documents",' + '"structuredQuery":{"from":[{"collectionId":"bundles"}]}},"readTime":' + '{"seconds":"1616434660","nanos":913764000}}}194{"documentMetadata":' + '{"name":"projects/fir-bundles-test/databases/(default)/documents/' + 'bundles/test-bundle","readTime":{"seconds":"1616434660","nanos":' + '913764000},"exists":true,"queries":["self"]}}402{"document":{"name":' + '"projects/fir-bundles-test/databases/(default)/documents/bundles/' + 'test-bundle","fields":{"clientCache":{"stringValue":"1200"},' + '"serverCache":{"stringValue":"600"},"queries":{"mapValue":{"fields":' + '{"self":{"mapValue":{"fields":{"collection":{"stringValue":"bundles"' + '}}}}}}}},"createTime":{"seconds":"1615488796","nanos":163327000},' + '"updateTime":{"seconds":"1615492486","nanos":34157000}}}') self.assertRaises( ValueError, _helpers.deserialize_bundle, _serialized, client=client, )
def test_chunkify(): from google.cloud.firestore_v1.types.document import Document from google.cloud.firestore_v1.types.firestore import RunQueryResponse from tests.unit.v1 import _test_helpers client = _test_helpers.make_client() col = client.collection("my-collection") client._firestore_api_internal = mock.Mock(spec=["run_query"]) results = [] for index in range(10): results.append( RunQueryResponse( document=Document( name=f"projects/project-project/databases/(default)/documents/my-collection/{index}", ), ), ) chunks = [ results[:3], results[3:6], results[6:9], results[9:], ] def _get_chunk(*args, **kwargs): return iter(chunks.pop(0)) client._firestore_api_internal.run_query.side_effect = _get_chunk counter = 0 expected_lengths = [3, 3, 3, 1] for chunk in col._chunkify(3): assert len(chunk) == expected_lengths[counter] counter += 1
def get_client(): return _test_helpers.make_client()
def test_not_actually_a_bundle_at_all(self): from google.cloud.firestore_v1 import _helpers client = _test_helpers.make_client() with pytest.raises(ValueError): _helpers.deserialize_bundle("{}", client)
def test_add_document(self): bundle = FirestoreBundle("test") doc = _test_helpers.build_document_snapshot( client=_test_helpers.make_client()) bundle.add_document(doc) self.assertEqual(bundle.documents[self.doc_key].snapshot, doc)