diff --git a/google/cloud/firestore_v1beta1/_helpers.py b/google/cloud/firestore_v1beta1/_helpers.py index a5e77f57a..85d96e318 100644 --- a/google/cloud/firestore_v1beta1/_helpers.py +++ b/google/cloud/firestore_v1beta1/_helpers.py @@ -120,8 +120,6 @@ class FieldPath(object): parts: (one or more strings) Indicating path of the key to be used. """ - simple_field_name = re.compile('^[_a-zA-Z][_a-zA-Z0-9]*$') - def __init__(self, *parts): for part in parts: if not isinstance(part, six.string_types) or not part: @@ -129,13 +127,6 @@ def __init__(self, *parts): raise ValueError(error) self.parts = tuple(parts) - def __repr__(self): - paths = "" - for part in self.parts: - paths += "'" + part + "'," - paths = paths[:-1] - return 'FieldPath({})'.format(paths) - @staticmethod def from_string(string): """ Creates a FieldPath from a unicode string representation. @@ -154,6 +145,7 @@ def from_string(string): A :class: `FieldPath` instance with the string split on "." as arguments to `FieldPath`. """ + # XXX this should just handle things with the invalid chars invalid_characters = '~*/[]' for invalid_character in invalid_characters: if invalid_character in string: @@ -161,23 +153,12 @@ def from_string(string): string = string.split('.') return FieldPath(*string) - def to_api_repr(self): - """ Returns quoted string representation of the FieldPath - - Returns: :rtype: str - Quoted string representation of the path stored - within this FieldPath conforming to the Firestore API - specification - """ - api_repr = [] + def __repr__(self): + paths = "" for part in self.parts: - match = re.match(self.simple_field_name, part) - if match and match.group(0) == part: - api_repr.append(part) - else: - replaced = part.replace('\\', '\\\\').replace('`', '\\`') - api_repr.append('`' + replaced + '`') - return '.'.join(api_repr) + paths += "'" + part + "'," + paths = paths[:-1] + return 'FieldPath({})'.format(paths) def __hash__(self): return hash(self.to_api_repr()) @@ -187,6 +168,11 @@ def __eq__(self, other): return self.parts == other.parts return NotImplemented + def __lt__(self, other): + if isinstance(other, FieldPath): + return self.parts < other.parts + return NotImplemented + def __add__(self, other): """Adds `other` field path to end of this field path. @@ -203,6 +189,19 @@ def __add__(self, other): else: return NotImplemented + def eq_or_parent(self, other): + return self.parts[:len(other.parts)] == other.parts[:len(self.parts)] + + def to_api_repr(self): + """ Returns quoted string representation of the FieldPath + + Returns: :rtype: str + Quoted string representation of the path stored + within this FieldPath conforming to the Firestore API + specification + """ + return get_field_path(self.parts) + class FieldPathHelper(object): """Helper to convert field names and paths for usage in a request. @@ -416,13 +415,6 @@ def to_field_paths(cls, field_updates): return helper.parse() -class ReadAfterWriteError(Exception): - """Raised when a read is attempted after a write. - - Raised by "read" methods that use transactions. - """ - - def verify_path(path, is_collection): """Verifies that a ``path`` has the correct form. @@ -540,6 +532,49 @@ def encode_dict(values_dict): } +def extract_field_paths(document_data): + """Extract field paths from document data + Args: + document_data (dict): The dictionary of the actual set data. + Returns: + List[~.firestore_v1beta1._helpers.FieldPath]: + A list of `FieldPath` instances from the actual data. + """ + field_paths = [] + for field_name, value in six.iteritems(document_data): + + if isinstance(value, dict): + for sub_path in extract_field_paths(value): + field_path = FieldPath(field_name, *sub_path.parts) + else: + field_path = FieldPath(field_name) + + field_paths.append(field_path) + + return field_paths + + +def filter_document_data_by_field_paths(document_data, field_paths): + flattened = {} + toplevel = {} + + for path in field_paths: + flattened[path] = get_nested_value(path, document_data) + + for path, value in six.iteritems(flattened): + filtered = toplevel + parts = parse_field_path(path) + + for part in parts: + parent, lastpart = filtered, part + filtered.setdefault(part, {}) + filtered = filtered[part] + + parent[lastpart] = value + + return toplevel + + def reference_value_to_document(reference_value, client): """Convert a reference value string to a document. @@ -673,21 +708,87 @@ def get_field_path(field_names): Returns: str: The ``.``-delimited field path. """ - return FIELD_PATH_DELIMITER.join(field_names) + simple_field_name = re.compile('^[_a-zA-Z][_a-zA-Z0-9]*$') + result = [] + + for field_name in field_names: + match = re.match(simple_field_name, field_name) + if match and match.group(0) == field_name: + result.append(field_name) + else: + replaced = field_name.replace('\\', '\\\\').replace('`', '\\`') + result.append('`' + replaced + '`') + return FIELD_PATH_DELIMITER.join(result) -def parse_field_path(field_path): + +def parse_field_path(api_repr): """Parse a **field path** from into a list of nested field names. See :func:`field_path` for more on **field paths**. Args: - field_path (str): The ``.``-delimited field path to parse. + api_repr (str): + The unique Firestore api representation which consists of + either simple or UTF-8 field names. It cannot exceed + 1500 bytes, and cannot be empty. Simple field names match + `'^[_a-zA-Z][_a-zA-Z0-9]*$'`. All other field names are + escaped with ```. Returns: List[str, ...]: The list of field names in the field path. """ - return field_path.split(FIELD_PATH_DELIMITER) + # code dredged back up from + # https://github.com/googleapis/google-cloud-python/pull/5109/files + field_names = [] + while api_repr: + field_name, api_repr = _parse_field_name(api_repr) + # non-simple field name + if field_name[0] == '`' and field_name[-1] == '`': + field_name = field_name[1:-1] + field_name = field_name.replace('\\`', '`') + field_name = field_name.replace('\\\\', '\\') + field_names.append(field_name) + return field_names + + +def _parse_field_name(api_repr): + """ + Parses the api_repr into the first field name and the rest + Args: + api_repr (str): The unique Firestore api representation. + Returns: + Tuple[str, str]: + A tuple with the first field name and the api_repr + of the rest. + """ + # XXX code dredged back up from + # https://github.com/googleapis/google-cloud-python/pull/5109/files; + # probably needs some speeding up + + if '.' not in api_repr: + return api_repr, None + + if api_repr[0] != '`': # first field name is simple + index = api_repr.index('.') + return api_repr[:index], api_repr[index+1:] # skips delimiter + + # starts with backtick: find next non-escaped backtick. + index = 1 + while index < len(api_repr): + + if api_repr[index] == '`': # end of quoted field name + break + + if api_repr[index] == '\\': # escape character, skip next + index += 2 + else: + index += 1 + + if index == len(api_repr): # no closing backtick found + raise ValueError("No closing backtick: {}".format(api_repr)) + + return api_repr[:index+1], api_repr[index+2:] def get_nested_value(field_path, data): @@ -793,7 +894,7 @@ def get_doc_id(document_pb, expected_prefix): return document_id -def process_server_timestamp(document_data, split_on_dots=True): +def process_server_timestamp(document_data, split_on_dots): """Remove all server timestamp sentinel values from data. If the data is nested, for example: @@ -852,13 +953,13 @@ def process_server_timestamp(document_data, split_on_dots=True): if split_on_dots: top_level_path = FieldPath(*field_name.split(".")) else: - top_level_path = FieldPath.from_string(field_name) + top_level_path = FieldPath(field_name) if isinstance(value, dict): if len(value) == 0: actual_data[field_name] = value continue sub_transform_paths, sub_data, sub_field_paths = ( - process_server_timestamp(value, False)) + process_server_timestamp(value, split_on_dots=False)) for sub_transform_path in sub_transform_paths: transform_path = FieldPath.from_string(field_name) transform_path.parts = ( @@ -868,7 +969,7 @@ def process_server_timestamp(document_data, split_on_dots=True): # Only add a key to ``actual_data`` if there is data. actual_data[field_name] = sub_data for sub_field_path in sub_field_paths: - field_path = FieldPath.from_string(field_name) + field_path = FieldPath(field_name) field_path.parts = field_path.parts + sub_field_path.parts field_paths.append(field_path) elif value is constants.SERVER_TIMESTAMP: @@ -930,46 +1031,240 @@ def get_transform_pb(document_path, transform_paths): ) -def pbs_for_set(document_path, document_data, merge=False, exists=None): +def pbs_for_create(document_path, document_data): + """Make ``Write`` protobufs for ``create()`` methods. + + Args: + document_path (str): A fully-qualified document path. + document_data (dict): Property names and values to use for + creating a document. + + Returns: + List[google.cloud.firestore_v1beta1.types.Write]: One or two + ``Write`` protobuf instances for ``create()``. + """ + transform_paths, actual_data, field_paths = process_server_timestamp( + document_data, split_on_dots=False) + + write_pbs = [] + + empty_document = not document_data + + if empty_document or actual_data: + + update_pb = write_pb2.Write( + update=document_pb2.Document( + name=document_path, + fields=encode_dict(actual_data), + ), + current_document=common_pb2.Precondition(exists=False), + ) + + write_pbs.append(update_pb) + + if transform_paths: + transform_pb = get_transform_pb(document_path, transform_paths) + if not actual_data: + transform_pb.current_document.CopyFrom( + common_pb2.Precondition(exists=False)) + write_pbs.append(transform_pb) + + return write_pbs + + +def pbs_for_set_no_merge(document_path, document_data): """Make ``Write`` protobufs for ``set()`` methods. Args: document_path (str): A fully-qualified document path. document_data (dict): Property names and values to use for replacing a document. - merge (bool): Whether to merge the fields or replace them - exists (bool): If set, a precondition to indicate whether the - document should exist or not. Used for create. Returns: List[google.cloud.firestore_v1beta1.types.Write]: One or two ``Write`` protobuf instances for ``set()``. """ transform_paths, actual_data, field_paths = process_server_timestamp( - document_data, False) - update_pb = write_pb2.Write( - update=document_pb2.Document( - name=document_path, - fields=encode_dict(actual_data), + document_data, split_on_dots=False) + + write_pbs = [ + write_pb2.Write( + update=document_pb2.Document( + name=document_path, + fields=encode_dict(actual_data), + ) ), + ] + + if transform_paths: + transform_pb = get_transform_pb(document_path, transform_paths) + write_pbs.append(transform_pb) + + return write_pbs + + +def all_merge_paths(document_data): + """Compute all merge paths from document data. + + Args: + document_data (dict): Property names and values to use for + replacing a document. + + Returns: + Tuple: ( + transform_paths, + actual_data, + data_merge, + transform_merge, + merge, + ) + """ + transform_paths, actual_data, field_paths = process_server_timestamp( + document_data, split_on_dots=False) + + merge = sorted(field_paths + transform_paths) + + return ( + transform_paths, actual_data, field_paths, transform_paths, merge) + + +def normalize_merge_paths(document_data, merge): + """Normalize merge paths against document data. + + Args: + document_data (dict): Property names and values to use for + replacing a document. + merge (Optional[bool] or Optional[List]): + If True, merge all fields; else, merge only the named fields. + + Returns: + Tuple: ( + transform_paths, + actual_data, + data_merge, + transform_merge, + merge, + ) + """ + transform_paths, actual_data, field_paths = process_server_timestamp( + document_data, split_on_dots=False) + + # merge is list of paths provided by enduser; convert merge + # elements into FieldPaths if they aren't already + new_merge = [] + data_merge = [] + transform_merge = [] + + for merge_field in merge: + if isinstance(merge_field, FieldPath): + merge_fp = merge_field + else: + merge_fp = FieldPath(*parse_field_path(merge_field)) + new_merge.append(merge_fp) + + if merge_fp in transform_paths: + transform_merge.append(merge_fp) + + for fp in field_paths: + if merge_fp.eq_or_parent(fp): + data_merge.append(fp) + + merge = new_merge + + # the conformance tests require that one merge path may not be the + # prefix of another, XXX quadratic is expensive, fix + for fp1 in merge: + for fp2 in merge: + if fp1 != fp2 and fp1.eq_or_parent(fp2): + raise ValueError( + 'a merge path may not be a parent of another merge ' + 'path' + ) + + # the conformance tests require that an exception be raised if any + # merge spec is not in the data, and the below happens to raise a + # keyerror XXX do this without so much expense, maybe by ensuring that + # each of the merge fieldpaths are in the union of transform_merge and + # data_merge + filter_document_data_by_field_paths( + document_data, + field_paths=[fp.to_api_repr() for fp in merge], + ) + + # XXX dont pass apireprs to filter_d_d_b_p, pass FieldPaths + actual_data = filter_document_data_by_field_paths( + document_data, + field_paths=[fp.to_api_repr() for fp in data_merge], ) - if exists is not None: - update_pb.current_document.CopyFrom( - common_pb2.Precondition(exists=exists)) - if merge: - field_paths = canonicalize_field_paths(field_paths) - mask = common_pb2.DocumentMask(field_paths=sorted(field_paths)) - update_pb.update_mask.CopyFrom(mask) + return ( + transform_paths, actual_data, data_merge, transform_merge, merge) + + +def pbs_for_set_with_merge(document_path, document_data, merge): + """Make ``Write`` protobufs for ``set()`` methods. + + Args: + document_path (str): A fully-qualified document path. + document_data (dict): Property names and values to use for + replacing a document. + merge (Optional[bool] or Optional[List]): + If True, merge all fields; else, merge only the named fields. + + Returns: + List[google.cloud.firestore_v1beta1.types.Write]: One + or two ``Write`` protobuf instances for ``set()``. + """ + create_empty = not document_data + + if merge is True: + ( + transform_paths, actual_data, data_merge, transform_merge, merge, + ) = all_merge_paths(document_data) + else: + ( + transform_paths, actual_data, data_merge, transform_merge, merge, + ) = normalize_merge_paths(document_data, merge) + + write_pbs = [] + update_pb = write_pb2.Write() + + update_paths = set(data_merge) + + # for whatever reason, the conformance tests want to see the parent + # of nested transform paths in the update mask + # (see set-st-merge-nonleaf-alone.textproto) + for transform_path in transform_paths: + if len(transform_path.parts) > 1: + parent_fp = FieldPath(*transform_path.parts[:-1]) + update_paths.add(parent_fp) + + if actual_data or create_empty or update_paths: + update = document_pb2.Document( + name=document_path, + fields=encode_dict(actual_data), + ) + update_pb.update.CopyFrom(update) + + mask_paths = [ + fp.to_api_repr() for fp in merge if fp not in transform_merge + ] + + if mask_paths or create_empty: + mask = common_pb2.DocumentMask(field_paths=mask_paths) + update_pb.update_mask.CopyFrom(mask) + + write_pbs.append(update_pb) + + new_transform_paths = [] + for merge_fp in merge: + t_merge_fps = [ + fp for fp in transform_paths if merge_fp.eq_or_parent(fp)] + new_transform_paths.extend(t_merge_fps) + transform_paths = new_transform_paths - write_pbs = [update_pb] if transform_paths: - # NOTE: We **explicitly** don't set any write option on - # the ``transform_pb``. transform_pb = get_transform_pb(document_path, transform_paths) - if not actual_data: - write_pbs = [transform_pb] - return write_pbs write_pbs.append(transform_pb) return write_pbs @@ -997,27 +1292,43 @@ def pbs_for_update(client, document_path, field_updates, option): option = client.write_option(exists=True) transform_paths, actual_updates, field_paths = ( - process_server_timestamp(field_updates)) + process_server_timestamp(field_updates, split_on_dots=True)) if not (transform_paths or actual_updates): raise ValueError('There are only ServerTimeStamp objects or is empty.') update_values, field_paths = FieldPathHelper.to_field_paths(actual_updates) - field_paths = canonicalize_field_paths(field_paths) + update_paths = field_paths[:] - update_pb = write_pb2.Write( - update=document_pb2.Document( - name=document_path, - fields=encode_dict(update_values), - ), - update_mask=common_pb2.DocumentMask(field_paths=field_paths), - ) - # Due to the default, we don't have to check if ``None``. - option.modify_write(update_pb, field_paths=field_paths) - write_pbs = [update_pb] + # for whatever reason, the conformance tests want to see the parent + # of nested transform paths in the update mask + for transform_path in transform_paths: + if len(transform_path.parts) > 1: + parent_fp = FieldPath(*transform_path.parts[:-1]) + if parent_fp not in update_paths: + update_paths.append(parent_fp) + + field_paths = canonicalize_field_paths(field_paths) + update_paths = canonicalize_field_paths(update_paths) + + write_pbs = [] + + if update_values: + update_pb = write_pb2.Write( + update=document_pb2.Document( + name=document_path, + fields=encode_dict(update_values), + ), + update_mask=common_pb2.DocumentMask(field_paths=update_paths), + ) + # Due to the default, we don't have to check if ``None``. + option.modify_write(update_pb) + write_pbs.append(update_pb) if transform_paths: - # NOTE: We **explicitly** don't set any write option on - # the ``transform_pb``. transform_pb = get_transform_pb(document_path, transform_paths) + if not update_values: + # NOTE: set the write option on the ``transform_pb`` only if there + # is no ``update_pb`` + option.modify_write(transform_pb) write_pbs.append(transform_pb) return write_pbs @@ -1043,6 +1354,13 @@ def pb_for_delete(document_path, option): return write_pb +class ReadAfterWriteError(Exception): + """Raised when a read is attempted after a write. + + Raised by "read" methods that use transactions. + """ + + def get_transaction_id(transaction, read_operation=True): """Get the transaction ID from a ``Transaction`` object. diff --git a/google/cloud/firestore_v1beta1/batch.py b/google/cloud/firestore_v1beta1/batch.py index 841bfebd2..bafa9d90a 100644 --- a/google/cloud/firestore_v1beta1/batch.py +++ b/google/cloud/firestore_v1beta1/batch.py @@ -57,8 +57,8 @@ def create(self, reference, document_data): document_data (dict): Property names and values to use for creating a document. """ - write_pbs = _helpers.pbs_for_set( - reference._document_path, document_data, merge=False, exists=False) + write_pbs = _helpers.pbs_for_create( + reference._document_path, document_data) self._add_write_pbs(write_pbs) def set(self, reference, document_data, merge=False): @@ -74,12 +74,17 @@ def set(self, reference, document_data, merge=False): A document reference that will have values set in this batch. document_data (dict): Property names and values to use for replacing a document. - merge (Optional[bool]): + merge (Optional[bool] or Optional[List]): If True, apply merging instead of overwriting the state of the document. """ - write_pbs = _helpers.pbs_for_set( - reference._document_path, document_data, merge=merge) + if merge is not False: + write_pbs = _helpers.pbs_for_set_with_merge( + reference._document_path, document_data, merge) + else: + write_pbs = _helpers.pbs_for_set_no_merge( + reference._document_path, document_data) + self._add_write_pbs(write_pbs) def update(self, reference, field_updates, option=None): @@ -98,6 +103,9 @@ def update(self, reference, field_updates, option=None): write option to make assertions / preconditions on the server state of the document before applying changes. """ + if option.__class__.__name__ == 'ExistsOption': + raise ValueError('you must not pass an explicit write option to ' + 'update.') write_pbs = _helpers.pbs_for_update( self._client, reference._document_path, field_updates, option) self._add_write_pbs(write_pbs) diff --git a/google/cloud/firestore_v1beta1/client.py b/google/cloud/firestore_v1beta1/client.py index 9eccbc13a..864dc692b 100644 --- a/google/cloud/firestore_v1beta1/client.py +++ b/google/cloud/firestore_v1beta1/client.py @@ -23,7 +23,6 @@ * a :class:`~.firestore_v1beta1.client.Client` owns a :class:`~.firestore_v1beta1.document.DocumentReference` """ - from google.cloud.client import ClientWithProject from google.cloud.firestore_v1beta1 import _helpers @@ -39,7 +38,9 @@ DEFAULT_DATABASE = '(default)' """str: The default database used in a :class:`~.firestore.client.Client`.""" _BAD_OPTION_ERR = ( - 'Exactly one of ``last_update_time`` or ``exists`` must be provided.') + 'Exactly one of ``last_update_time`` or ``exists`` ' + 'must be provided.' +) _BAD_DOC_TEMPLATE = ( 'Document {!r} appeared in response but was not present among references') _ACTIVE_TXN = 'There is already an active transaction.' diff --git a/google/cloud/firestore_v1beta1/document.py b/google/cloud/firestore_v1beta1/document.py index b702a7c4f..7b8fd6ded 100644 --- a/google/cloud/firestore_v1beta1/document.py +++ b/google/cloud/firestore_v1beta1/document.py @@ -211,9 +211,9 @@ def set(self, document_data, merge=False): Args: document_data (dict): Property names and values to use for replacing a document. - option (Optional[~.firestore_v1beta1.client.WriteOption]): A - write option to make assertions / preconditions on the server - state of the document before applying changes. + merge (Optional[bool] or Optional[List]): + If True, apply merging instead of overwriting the state + of the document. Returns: google.cloud.firestore_v1beta1.types.WriteResult: The diff --git a/tests/system.py b/tests/system.py index 4827ca5e0..62ea42c7e 100644 --- a/tests/system.py +++ b/tests/system.py @@ -202,8 +202,7 @@ def test_document_integer_field(client, cleanup): document.create(data1) data2 = {'1a.ab': '4d', '6f.7g': '9h'} - option2 = client.write_option(exists=True) - document.update(data2, option=option2) + document.update(data2) snapshot = document.get() expected = { '1a': { @@ -311,9 +310,8 @@ def test_update_document(client, cleanup): assert document_id in exc_info.value.message # 1. Try to update before the document exists (now with an option). - option1 = client.write_option(exists=True) with pytest.raises(NotFound) as exc_info: - document.update({'still': 'not-there'}, option=option1) + document.update({'still': 'not-there'}) assert exc_info.value.message.startswith(MISSING_DOCUMENT) assert document_id in exc_info.value.message @@ -327,8 +325,7 @@ def test_update_document(client, cleanup): }, 'other': True, } - option2 = client.write_option(exists=False) - write_result2 = document.update(data, option=option2) + write_result2 = document.create(data) # 3. Send an update without a field path (no option). field_updates3 = {'foo': {'quux': 800}} diff --git a/tests/unit/test__helpers.py b/tests/unit/test__helpers.py index afcc2f3e9..95dd0f6a0 100644 --- a/tests/unit/test__helpers.py +++ b/tests/unit/test__helpers.py @@ -94,44 +94,154 @@ def _get_target_class(): from google.cloud.firestore_v1beta1._helpers import FieldPath return FieldPath - def _make_one(self, *args, **kwargs): + def _make_one(self, *args): klass = self._get_target_class() - return klass(*args, **kwargs) + return klass(*args) - def test_none_fails(self): + def test_ctor_w_none_in_part(self): with self.assertRaises(ValueError): self._make_one('a', None, 'b') - def test_empty_string_in_part_fails(self): + def test_ctor_w_empty_string_in_part(self): with self.assertRaises(ValueError): self._make_one('a', '', 'b') - def test_integer_fails(self): + def test_ctor_w_integer_part(self): with self.assertRaises(ValueError): self._make_one('a', 3, 'b') - def test_iterable_fails(self): + def test_ctor_w_list(self): + parts = ['a', 'b', 'c'] with self.assertRaises(ValueError): - self._make_one('a', ['a'], 'b') + self._make_one(parts) + + def test_ctor_w_tuple(self): + parts = ('a', 'b', 'c') + with self.assertRaises(ValueError): + self._make_one(parts) - def test_invalid_chars_in_constructor(self): - parts = '~*/[].' - for part in parts: - field_path = self._make_one(part) - self.assertEqual(field_path.parts, (part, )) + def test_ctor_w_iterable_part(self): + with self.assertRaises(ValueError): + self._make_one('a', ['a'], 'b') - def test_component(self): - field_path = self._make_one('a..b') - self.assertEqual(field_path.parts, ('a..b',)) + def test_constructor_w_single_part(self): + field_path = self._make_one('a') + self.assertEqual(field_path.parts, ('a',)) - def test_constructor_iterable(self): + def test_constructor_w_multiple_parts(self): field_path = self._make_one('a', 'b', 'c') self.assertEqual(field_path.parts, ('a', 'b', 'c')) - def test_unicode(self): + def test_ctor_w_invalid_chars_in_part(self): + invalid_parts = ('~', '*', '/', '[', ']', '.') + for invalid_part in invalid_parts: + field_path = self._make_one(invalid_part) + self.assertEqual(field_path.parts, (invalid_part, )) + + def test_ctor_w_double_dots(self): + field_path = self._make_one('a..b') + self.assertEqual(field_path.parts, ('a..b',)) + + def test_ctor_w_unicode(self): field_path = self._make_one('一', '二', '三') self.assertEqual(field_path.parts, ('一', '二', '三')) + def test_from_string_w_empty_string(self): + parts = '' + with self.assertRaises(ValueError): + self._get_target_class().from_string(parts) + + def test_from_string_w_empty_field_name(self): + parts = 'a..b' + with self.assertRaises(ValueError): + self._get_target_class().from_string(parts) + + def test_from_string_w_invalid_chars(self): + invalid_parts = ('~', '*', '/', '[', ']', '.') + for invalid_part in invalid_parts: + with self.assertRaises(ValueError): + self._get_target_class().from_string(invalid_part) + + def test_from_string_w_ascii_single(self): + field_path = self._get_target_class().from_string('a') + self.assertEqual(field_path.parts, ('a',)) + + def test_from_string_w_ascii_dotted(self): + field_path = self._get_target_class().from_string('a.b.c') + self.assertEqual(field_path.parts, ('a', 'b', 'c')) + + def test_from_string_w_non_ascii_dotted(self): + field_path = self._get_target_class().from_string('a.一') + self.assertEqual(field_path.parts, ('a', '一')) + + def test___hash___w_single_part(self): + field_path = self._make_one('a') + self.assertEqual(hash(field_path), hash('a')) + + def test___hash___w_multiple_parts(self): + field_path = self._make_one('a', 'b') + self.assertEqual(hash(field_path), hash('a.b')) + + def test___hash___w_escaped_parts(self): + field_path = self._make_one('a', '3') + self.assertEqual(hash(field_path), hash('a.`3`')) + + def test___eq___w_matching_type(self): + field_path = self._make_one('a', 'b') + string_path = self._get_target_class().from_string('a.b') + self.assertEqual(field_path, string_path) + + def test___eq___w_non_matching_type(self): + field_path = self._make_one('a', 'c') + other = mock.Mock() + other.parts = 'a', 'b' + self.assertNotEqual(field_path, other) + + def test___lt___w_matching_type(self): + field_path = self._make_one('a', 'b') + string_path = self._get_target_class().from_string('a.c') + self.assertTrue(field_path < string_path) + + def test___lt___w_non_matching_type(self): + field_path = self._make_one('a', 'b') + other = object() + # Python 2 doesn't raise TypeError here, but Python3 does. + self.assertIs(field_path.__lt__(other), NotImplemented) + + def test___add__(self): + path1 = 'a123', 'b456' + path2 = 'c789', 'd012' + path3 = 'c789.d012' + field_path1 = self._make_one(*path1) + field_path1_string = self._make_one(*path1) + field_path2 = self._make_one(*path2) + field_path1 += field_path2 + field_path1_string += path3 + field_path2 = field_path2 + self._make_one(*path1) + self.assertEqual(field_path1, self._make_one(*(path1 + path2))) + self.assertEqual(field_path2, self._make_one(*(path2 + path1))) + self.assertEqual(field_path1_string, field_path1) + self.assertNotEqual(field_path1, field_path2) + with self.assertRaises(TypeError): + field_path1 + 305 + + def test_eq_or_parent_same(self): + field_path = self._make_one('a', 'b') + other = self._make_one('a', 'b') + self.assertTrue(field_path.eq_or_parent(other)) + + def test_eq_or_parent_prefix(self): + field_path = self._make_one('a', 'b') + other = self._make_one('a', 'b', 'c') + self.assertTrue(field_path.eq_or_parent(other)) + self.assertTrue(other.eq_or_parent(field_path)) + + def test_eq_or_parent_no_prefix(self): + field_path = self._make_one('a', 'b') + other = self._make_one('d', 'e', 'f') + self.assertFalse(field_path.eq_or_parent(other)) + self.assertFalse(other.eq_or_parent(field_path)) + def test_to_api_repr_a(self): parts = 'a' field_path = self._make_one(parts) @@ -192,96 +302,6 @@ def test_to_api_repr_chain(self): self.assertEqual(field_path.to_api_repr(), r'a.`\``.`\\`._3.`03`.a03.`\\\\`.a0332432.`一`') - def test_from_string(self): - field_path = self._get_target_class().from_string('a.b.c') - self.assertEqual(field_path.parts, ('a', 'b', 'c')) - self.assertEqual(field_path.to_api_repr(), 'a.b.c') - - def test_from_string_non_simple(self): - field_path = self._get_target_class().from_string('a.一') - self.assertEqual(field_path.parts, ('a', '一')) - self.assertEqual(field_path.to_api_repr(), 'a.`一`') - - def test_list_splat(self): - parts = ['a', 'b', 'c'] - field_path = self._make_one(*parts) - self.assertEqual(field_path.parts, ('a', 'b', 'c')) - - def test_tuple_splat(self): - parts = ('a', 'b', 'c') - field_path = self._make_one(*parts) - self.assertEqual(field_path.parts, ('a', 'b', 'c')) - - def test_invalid_chars_from_string_fails(self): - parts = '~*/[].' - for part in parts: - with self.assertRaises(ValueError): - self._get_target_class().from_string(part) - - def test_empty_string_fails(self): - parts = '' - with self.assertRaises(ValueError): - self._get_target_class().from_string(parts) - - def test_empty_field_name_fails(self): - parts = 'a..b' - with self.assertRaises(ValueError): - self._get_target_class().from_string(parts) - - def test_list_fails(self): - parts = ['a', 'b', 'c'] - with self.assertRaises(ValueError): - self._make_one(parts) - - def test_tuple_fails(self): - parts = ('a', 'b', 'c') - with self.assertRaises(ValueError): - self._make_one(parts) - - def test_equality(self): - field_path = self._make_one('a', 'b') - string_path = self._get_target_class().from_string('a.b') - self.assertEqual(field_path, string_path) - - def test_non_equal_types(self): - import mock - mock = mock.Mock() - mock.parts = 'a', 'b' - field_path = self._make_one('a', 'b') - self.assertNotEqual(field_path, mock) - - def test_key(self): - field_path = self._make_one('a321', 'b456') - field_path_same = self._get_target_class().from_string('a321.b456') - field_path_different = self._make_one('a321', 'b457') - keys = { - field_path: '', - field_path_same: '', - field_path_different: '' - } - for key in keys: - if key == field_path_different: - self.assertNotEqual(key, field_path) - else: - self.assertEqual(key, field_path) - - def test___add__(self): - path1 = 'a123', 'b456' - path2 = 'c789', 'd012' - path3 = 'c789.d012' - field_path1 = self._make_one(*path1) - field_path1_string = self._make_one(*path1) - field_path2 = self._make_one(*path2) - field_path1 += field_path2 - field_path1_string += path3 - field_path2 = field_path2 + self._make_one(*path1) - self.assertEqual(field_path1, self._make_one(*(path1 + path2))) - self.assertEqual(field_path2, self._make_one(*(path2 + path1))) - self.assertEqual(field_path1_string, field_path1) - self.assertNotEqual(field_path1, field_path2) - with self.assertRaises(TypeError): - field_path1 + 305 - class TestFieldPathHelper(unittest.TestCase): @@ -873,6 +893,71 @@ def test_many_types(self): self.assertEqual(encoded_dict, expected_dict) +class Test_extract_field_paths(unittest.TestCase): + + @staticmethod + def _call_fut(document): + from google.cloud.firestore_v1beta1 import _helpers + + return _helpers.extract_field_paths(document) + + @staticmethod + def _make_field_path(dotted): + from google.cloud.firestore_v1beta1 import _helpers + + return _helpers.FieldPath.from_string(dotted) + + def test_w_empty_document(self): + document = {} + expected = [] + self.assertEqual(self._call_fut(document), expected) + + def test_w_non_dict_value(self): + document = {'a': 'b'} + expected = [self._make_field_path('a')] + self.assertEqual(self._call_fut(document), expected) + + def test_w_dict_value(self): + document = {'a': {'b': 'c'}} + expected = [self._make_field_path('a.b')] + self.assertEqual(self._call_fut(document), expected) + + +class Test_filter_document_data_by_field_paths(unittest.TestCase): + + @staticmethod + def _call_fut(document_data, field_paths): + from google.cloud.firestore_v1beta1._helpers import ( + filter_document_data_by_field_paths, + ) + + return filter_document_data_by_field_paths(document_data, field_paths) + + def test_w_leaf_child(self): + document = {'a': {'b': {'c': 1, 'd': 2}}, 'x': 1} + field_paths = ['a.b.c'] + expected = {'a': {'b': {'c': 1}}} + self.assertEqual(self._call_fut(document, field_paths), expected) + + def test_w_non_leaf_child(self): + document = {'a': {'b': {'c': 1, 'd': 2}}, 'x': 1} + field_paths = ['a.b'] + expected = {'a': {'b': {'c': 1, 'd': 2}}} + self.assertEqual(self._call_fut(document, field_paths), expected) + + def test_w_root(self): + document = {'a': {'b': {'c': 1, 'd': 2}}, 'x': 1} + field_paths = ['a'] + expected = {'a': {'b': {'c': 1, 'd': 2}}} + self.assertEqual(self._call_fut(document, field_paths), expected) + + def test_w_multiple_leaves(self): + document = {'h': {'f': 5, 'g': 6}, 'e': 7} + field_paths = ['h.f', 'h.g'] + expected = {'h': {'f': 5, 'g': 6}} + self.assertEqual(self._call_fut(document, field_paths), expected) + + class Test_reference_value_to_document(unittest.TestCase): @staticmethod @@ -1161,7 +1246,25 @@ def _call_fut(field_names): return get_field_path(field_names) - def test_it(self): + def test_w_empty(self): + self.assertEqual(self._call_fut([]), '') + + def test_w_one_simple(self): + self.assertEqual(self._call_fut(['a']), 'a') + + def test_w_one_starts_w_digit(self): + self.assertEqual(self._call_fut(['0abc']), '`0abc`') + + def test_w_one_w_non_alphanum(self): + self.assertEqual(self._call_fut(['a b c']), '`a b c`') + + def test_w_one_w_backtick(self): + self.assertEqual(self._call_fut(['a`b']), '`a\\`b`') + + def test_w_one_w_backslash(self): + self.assertEqual(self._call_fut(['a\\b']), '`a\\\\b`') + + def test_multiple(self): self.assertEqual(self._call_fut(['a', 'b', 'c']), 'a.b.c') @@ -1173,15 +1276,47 @@ def _call_fut(field_path): return parse_field_path(field_path) - def test_it(self): + def test_wo_escaped_names(self): self.assertEqual(self._call_fut('a.b.c'), ['a', 'b', 'c']) - def test_api_repr(self): - from google.cloud.firestore_v1beta1._helpers import FieldPath + def test_w_escaped_backtick(self): + self.assertEqual(self._call_fut('`a\\`b`.c.d'), ['a`b', 'c', 'd']) - self.assertEqual( - self._call_fut(FieldPath('a', 'b', 'c').to_api_repr()), - ['a', 'b', 'c']) + def test_w_escaped_backslash(self): + self.assertEqual(self._call_fut('`a\\\\b`.c.d'), ['a\\b', 'c', 'd']) + + +class Test__parse_field_name(unittest.TestCase): + + @staticmethod + def _call_fut(field_path): + from google.cloud.firestore_v1beta1._helpers import _parse_field_name + + return _parse_field_name(field_path) + + def test_w_no_dots(self): + name, rest = self._call_fut('a') + self.assertEqual(name, 'a') + self.assertIsNone(rest) + + def test_w_first_name_simple(self): + name, rest = self._call_fut('a.b.c') + self.assertEqual(name, 'a') + self.assertEqual(rest, 'b.c') + + def test_w_first_name_escaped_no_escapse(self): + name, rest = self._call_fut('`3`.b.c') + self.assertEqual(name, '`3`') + self.assertEqual(rest, 'b.c') + + def test_w_first_name_escaped_w_escaped_backtick(self): + name, rest = self._call_fut('`a\\`b`.c.d') + self.assertEqual(name, '`a\\`b`') + self.assertEqual(rest, 'c.d') + + def test_w_first_name_escaped_wo_closing_backtick(self): + with self.assertRaises(ValueError): + self._call_fut('`a\\`b.c.d') class Test_get_nested_value(unittest.TestCase): @@ -1293,13 +1428,33 @@ def test_failure(self): class Test_process_server_timestamp(unittest.TestCase): @staticmethod - def _call_fut(document_data): - from google.cloud.firestore_v1beta1._helpers import ( - process_server_timestamp) + def _call_fut(document_data, split_on_dots): + from google.cloud.firestore_v1beta1 import _helpers - return process_server_timestamp(document_data) + return _helpers.process_server_timestamp( + document_data, split_on_dots=split_on_dots) + + def test_no_fields_w_split_on_dots(self): + import collections + from google.cloud.firestore_v1beta1 import _helpers + + data = collections.OrderedDict(( + ('one', 1), + ('two', 2.25), + ('three', [False, True, True]), + )) + expected_field_paths = [ + _helpers.FieldPath('one'), + _helpers.FieldPath('two'), + _helpers.FieldPath('three') + ] + transform_paths, actual_data, field_paths = self._call_fut( + data, split_on_dots=True) + self.assertEqual(transform_paths, []) + self.assertEqual(field_paths, expected_field_paths) + self.assertIs(actual_data, data) - def test_no_fields(self): + def test_no_fields_wo_split_on_dots(self): import collections from google.cloud.firestore_v1beta1 import _helpers @@ -1313,12 +1468,55 @@ def test_no_fields(self): _helpers.FieldPath('two'), _helpers.FieldPath('three') ] - transform_paths, actual_data, field_paths = self._call_fut(data) + transform_paths, actual_data, field_paths = self._call_fut( + data, split_on_dots=False) self.assertEqual(transform_paths, []) self.assertEqual(field_paths, expected_field_paths) self.assertIs(actual_data, data) - def test_simple_fields(self): + def test_simple_fields_w_split_on_dots(self): + import collections + from google.cloud.firestore_v1beta1 import _helpers + from google.cloud.firestore_v1beta1.constants import SERVER_TIMESTAMP + + # "Cheat" and use OrderedDict-s so that iteritems() is deterministic. + nested1 = collections.OrderedDict(( + ('bottom2', SERVER_TIMESTAMP), + ('bottom3', 1.5), + )) + nested2 = collections.OrderedDict(( + ('bottom7', SERVER_TIMESTAMP), + )) + data = collections.OrderedDict(( + ('top1', nested1), + ('top4', SERVER_TIMESTAMP), + ('top5', 200), + ('top6', nested2), + )) + expected_transform_paths = [ + _helpers.FieldPath('top1', 'bottom2'), + _helpers.FieldPath('top4'), + _helpers.FieldPath('top6', 'bottom7') + ] + expected_field_paths = [ + _helpers.FieldPath('top1', 'bottom3'), + _helpers.FieldPath('top5')] + expected_data = { + 'top1': { + 'bottom3': data['top1']['bottom3'], + }, + 'top5': data['top5'], + } + transform_paths, actual_data, field_paths = self._call_fut( + data, split_on_dots=True) + self.assertEqual( + transform_paths, + expected_transform_paths + ) + self.assertEqual(field_paths, expected_field_paths) + self.assertEqual(actual_data, expected_data) + + def test_simple_fields_wo_split_on_dots(self): import collections from google.cloud.firestore_v1beta1 import _helpers from google.cloud.firestore_v1beta1.constants import SERVER_TIMESTAMP @@ -1351,7 +1549,8 @@ def test_simple_fields(self): }, 'top5': data['top5'], } - transform_paths, actual_data, field_paths = self._call_fut(data) + transform_paths, actual_data, field_paths = self._call_fut( + data, split_on_dots=False) self.assertEqual( transform_paths, expected_transform_paths @@ -1359,7 +1558,7 @@ def test_simple_fields(self): self.assertEqual(field_paths, expected_field_paths) self.assertEqual(actual_data, expected_data) - def test_field_updates(self): + def test_field_updates_w_split_on_dots(self): import collections from google.cloud.firestore_v1beta1 import _helpers from google.cloud.firestore_v1beta1.constants import SERVER_TIMESTAMP @@ -1370,14 +1569,34 @@ def test_field_updates(self): ('c.d', {'e': SERVER_TIMESTAMP}), ('f.g', SERVER_TIMESTAMP), )) - transform_paths, actual_data, field_paths = self._call_fut(data) + transform_paths, actual_data, field_paths = self._call_fut( + data, split_on_dots=True) self.assertEqual(transform_paths, [_helpers.FieldPath('c', 'd', 'e'), _helpers.FieldPath('f', 'g')]) expected_data = {'a': {'b': data['a']['b']}} self.assertEqual(actual_data, expected_data) - def test_field_updates_w_empty_value(self): + def test_field_updates_wo_split_on_dots(self): + import collections + from google.cloud.firestore_v1beta1 import _helpers + from google.cloud.firestore_v1beta1.constants import SERVER_TIMESTAMP + + # "Cheat" and use OrderedDict-s so that iteritems() is deterministic. + data = collections.OrderedDict(( + ('a', {'b': 10}), + ('c.d', {'e': SERVER_TIMESTAMP}), + ('f.g', SERVER_TIMESTAMP), + )) + transform_paths, actual_data, field_paths = self._call_fut( + data, split_on_dots=False) + self.assertEqual(transform_paths, [_helpers.FieldPath('c', 'd', 'e'), + _helpers.FieldPath('f.g')]) + + expected_data = {'a': {'b': data['a']['b']}} + self.assertEqual(actual_data, expected_data) + + def test_field_updates_w_empty_value_w_split_on_dots(self): import collections from google.cloud.firestore_v1beta1 import _helpers from google.cloud.firestore_v1beta1.constants import SERVER_TIMESTAMP @@ -1389,7 +1608,8 @@ def test_field_updates_w_empty_value(self): ('f.g', SERVER_TIMESTAMP), ('h', {}), )) - transform_paths, actual_data, field_paths = self._call_fut(data) + transform_paths, actual_data, field_paths = self._call_fut( + data, split_on_dots=True) self.assertEqual( transform_paths, [_helpers.FieldPath('c', 'd', 'e'), @@ -1398,15 +1618,37 @@ def test_field_updates_w_empty_value(self): expected_data = {'a': {'b': data['a']['b']}, 'h': {}} self.assertEqual(actual_data, expected_data) - -class Test_canonicalize_field_paths(unittest.TestCase): - - @staticmethod - def _call_fut(field_paths): + def test_field_updates_w_empty_value_wo_split_on_dots(self): + import collections from google.cloud.firestore_v1beta1 import _helpers + from google.cloud.firestore_v1beta1.constants import SERVER_TIMESTAMP + + # "Cheat" and use OrderedDict-s so that iteritems() is deterministic. + data = collections.OrderedDict(( + ('a', {'b': 10}), + ('c.d', {'e': SERVER_TIMESTAMP}), + ('f.g', SERVER_TIMESTAMP), + ('h', {}), + )) + transform_paths, actual_data, field_paths = self._call_fut( + data, split_on_dots=False) + self.assertEqual( + transform_paths, + [_helpers.FieldPath('c', 'd', 'e'), + _helpers.FieldPath('f.g')]) + + expected_data = {'a': {'b': data['a']['b']}, 'h': {}} + self.assertEqual(actual_data, expected_data) + + +class Test_canonicalize_field_paths(unittest.TestCase): + + @staticmethod + def _call_fut(field_paths): + from google.cloud.firestore_v1beta1 import _helpers + + return _helpers.canonicalize_field_paths(field_paths) - return _helpers.canonicalize_field_paths(field_paths) - def _test_helper(self, to_convert): from google.cloud.firestore_v1beta1 import _helpers @@ -1478,26 +1720,27 @@ def test_it(self): self.assertEqual(transform_pb, expected_pb) -class Test_pbs_for_set(unittest.TestCase): +class Test_pbs_for_create(unittest.TestCase): @staticmethod - def _call_fut(document_path, document_data, merge=False, exists=None): - from google.cloud.firestore_v1beta1._helpers import pbs_for_set + def _call_fut(document_path, document_data): + from google.cloud.firestore_v1beta1._helpers import pbs_for_create - return pbs_for_set( - document_path, document_data, merge=merge, exists=exists) + return pbs_for_create(document_path, document_data) @staticmethod def _make_write_w_document(document_path, **data): from google.cloud.firestore_v1beta1.proto import document_pb2 from google.cloud.firestore_v1beta1.proto import write_pb2 from google.cloud.firestore_v1beta1._helpers import encode_dict + from google.cloud.firestore_v1beta1.proto import common_pb2 return write_pb2.Write( update=document_pb2.Document( name=document_path, fields=encode_dict(data), ), + current_document=common_pb2.Precondition(exists=False), ) @staticmethod @@ -1519,10 +1762,8 @@ def _make_write_w_transform(document_path, fields): ), ) - def _helper(self, merge=False, do_transform=False, exists=None, - empty_val=False): + def _helper(self, do_transform=False, empty_val=False): from google.cloud.firestore_v1beta1.constants import SERVER_TIMESTAMP - from google.cloud.firestore_v1beta1.proto import common_pb2 document_path = _make_ref_string(u'little', u'town', u'of', u'ham') document_data = { @@ -1536,8 +1777,7 @@ def _helper(self, merge=False, do_transform=False, exists=None, if empty_val: document_data['mustard'] = {} - write_pbs = self._call_fut( - document_path, document_data, merge, exists) + write_pbs = self._call_fut(document_path, document_data) if empty_val: update_pb = self._make_write_w_document( @@ -1549,32 +1789,120 @@ def _helper(self, merge=False, do_transform=False, exists=None, ) expected_pbs = [update_pb] - if merge: - field_paths = sorted(['cheese', 'crackers']) - update_pb.update_mask.CopyFrom( - common_pb2.DocumentMask(field_paths=field_paths)) - - if exists is not None: - update_pb.current_document.CopyFrom( - common_pb2.Precondition(exists=exists)) - if do_transform: expected_pbs.append( self._make_write_w_transform(document_path, fields=['butter'])) self.assertEqual(write_pbs, expected_pbs) - def test_without_merge(self): + def test_without_transform(self): self._helper() - def test_with_merge(self): - self._helper(merge=True) + def test_w_transform(self): + self._helper(do_transform=True) + + def test_w_transform_and_empty_value(self): + self._helper(do_transform=True, empty_val=True) + + +class Test_pbs_for_set_no_merge(unittest.TestCase): + + @staticmethod + def _call_fut(document_path, document_data): + from google.cloud.firestore_v1beta1 import _helpers + + return _helpers.pbs_for_set_no_merge(document_path, document_data) + + @staticmethod + def _make_write_w_document(document_path, **data): + from google.cloud.firestore_v1beta1.proto import document_pb2 + from google.cloud.firestore_v1beta1.proto import write_pb2 + from google.cloud.firestore_v1beta1._helpers import encode_dict + + return write_pb2.Write( + update=document_pb2.Document( + name=document_path, + fields=encode_dict(data), + ), + ) + + @staticmethod + def _make_write_w_transform(document_path, fields): + from google.cloud.firestore_v1beta1.proto import write_pb2 + from google.cloud.firestore_v1beta1.gapic import enums + + server_val = enums.DocumentTransform.FieldTransform.ServerValue + transforms = [ + write_pb2.DocumentTransform.FieldTransform( + field_path=field, set_to_server_value=server_val.REQUEST_TIME) + for field in fields + ] + + return write_pb2.Write( + transform=write_pb2.DocumentTransform( + document=document_path, + field_transforms=transforms, + ), + ) + + def test_w_empty_document(self): + document_path = _make_ref_string(u'little', u'town', u'of', u'ham') + document_data = {} + + write_pbs = self._call_fut(document_path, document_data) - def test_with_exists_false(self): - self._helper(exists=False) + update_pb = self._make_write_w_document(document_path) + expected_pbs = [update_pb] + self.assertEqual(write_pbs, expected_pbs) + + def test_w_only_server_timestamp(self): + from google.cloud.firestore_v1beta1.constants import SERVER_TIMESTAMP + + document_path = _make_ref_string(u'little', u'town', u'of', u'ham') + document_data = {'butter': SERVER_TIMESTAMP} + + write_pbs = self._call_fut(document_path, document_data) + + update_pb = self._make_write_w_document(document_path) + transform_pb = self._make_write_w_transform(document_path, ['butter']) + expected_pbs = [update_pb, transform_pb] + self.assertEqual(write_pbs, expected_pbs) + + def _helper(self, do_transform=False, empty_val=False): + from google.cloud.firestore_v1beta1.constants import SERVER_TIMESTAMP + + document_path = _make_ref_string(u'little', u'town', u'of', u'ham') + document_data = { + 'cheese': 1.5, + 'crackers': True, + } + + if do_transform: + document_data['butter'] = SERVER_TIMESTAMP + + if empty_val: + document_data['mustard'] = {} + + write_pbs = self._call_fut(document_path, document_data) + + if empty_val: + update_pb = self._make_write_w_document( + document_path, cheese=1.5, crackers=True, mustard={}, + ) + else: + update_pb = self._make_write_w_document( + document_path, cheese=1.5, crackers=True, + ) + expected_pbs = [update_pb] + + if do_transform: + expected_pbs.append( + self._make_write_w_transform(document_path, fields=['butter'])) - def test_with_exists_true(self): - self._helper(exists=True) + self.assertEqual(write_pbs, expected_pbs) + + def test_defaults(self): + self._helper() def test_w_transform(self): self._helper(do_transform=True) @@ -1584,6 +1912,375 @@ def test_w_transform_and_empty_value(self): self._helper(do_transform=True, empty_val=True) +class Test_all_merge_paths(unittest.TestCase): + + @staticmethod + def _call_fut(document_data): + from google.cloud.firestore_v1beta1 import _helpers + + return _helpers.all_merge_paths(document_data) + + @staticmethod + def _make_field_path(*fields): + from google.cloud.firestore_v1beta1 import _helpers + + return _helpers.FieldPath(*fields) + + def test_w_empty(self): + document_data = {} + + ( + transform_paths, actual_data, data_merge, transform_merge, merge, + ) = self._call_fut(document_data) + + self.assertEqual(transform_paths, []) + self.assertEqual(actual_data, {}) + self.assertEqual(data_merge, []) + self.assertEqual(transform_merge, []) + self.assertEqual(merge, []) + + def test_w_simple(self): + document_data = {'a': {'b': 'c'}} + + ( + transform_paths, actual_data, data_merge, transform_merge, merge, + ) = self._call_fut(document_data) + + path = self._make_field_path('a', 'b') + self.assertEqual(transform_paths, []) + self.assertEqual(actual_data, document_data) + self.assertEqual(data_merge, [path]) + self.assertEqual(transform_merge, []) + self.assertEqual(merge, [path]) + + def test_w_server_timestamp(self): + from google.cloud.firestore_v1beta1.constants import SERVER_TIMESTAMP + + document_data = {'a': {'b': SERVER_TIMESTAMP}} + + ( + transform_paths, actual_data, data_merge, transform_merge, merge, + ) = self._call_fut(document_data) + + path = self._make_field_path('a', 'b') + self.assertEqual(transform_paths, [path]) + self.assertEqual(actual_data, {}) + self.assertEqual(data_merge, []) + self.assertEqual(transform_merge, [path]) + self.assertEqual(merge, [path]) + + def test_w_simple_and_server_timestamp(self): + from google.cloud.firestore_v1beta1.constants import SERVER_TIMESTAMP + + document_data = {'a': {'b': 'd', 'c': SERVER_TIMESTAMP}} + + ( + transform_paths, actual_data, data_merge, transform_merge, merge, + ) = self._call_fut(document_data) + + path_a_b = self._make_field_path('a', 'b') + path_a_c = self._make_field_path('a', 'c') + self.assertEqual(transform_paths, [path_a_c]) + self.assertEqual(actual_data, {'a': {'b': 'd'}}) + self.assertEqual(data_merge, [path_a_b]) + self.assertEqual(transform_merge, [path_a_c]) + self.assertEqual(merge, [path_a_b, path_a_c]) + + +class Test_normalize_merge_paths(unittest.TestCase): + + @staticmethod + def _call_fut(document_data, merge): + from google.cloud.firestore_v1beta1 import _helpers + + return _helpers.normalize_merge_paths(document_data, merge) + + @staticmethod + def _make_field_path(*fields): + from google.cloud.firestore_v1beta1 import _helpers + + return _helpers.FieldPath(*fields) + + def test_w_empty_document_empty_merge_list(self): + document_data = {} + + ( + transform_paths, actual_data, data_merge, transform_merge, merge, + ) = self._call_fut(document_data, []) + + self.assertEqual(transform_paths, []) + self.assertEqual(actual_data, {}) + self.assertEqual(data_merge, []) + self.assertEqual(transform_merge, []) + self.assertEqual(merge, []) + + def test_w_merge_path_miss(self): + document_data = {} + merge_path = self._make_field_path('a', 'b') + + with self.assertRaises(KeyError): + self._call_fut(document_data, [merge_path]) + + def test_w_merge_path_parent(self): + document_data = {'a': {'b': 'c', 'd': 'e'}} + + with self.assertRaises(ValueError): + self._call_fut(document_data, ['a', 'a.b']) + + with self.assertRaises(ValueError): + self._call_fut(document_data, ['a.b', 'a']) + + def test_w_simple(self): + document_data = {'a': {'b': 'c', 'd': 'e'}} + merge_path = self._make_field_path('a', 'b') + + ( + transform_paths, actual_data, data_merge, transform_merge, merge, + ) = self._call_fut(document_data, [merge_path]) + + self.assertEqual(transform_paths, []) + self.assertEqual(actual_data, {'a': {'b': 'c'}}) + self.assertEqual(data_merge, [merge_path]) + self.assertEqual(transform_merge, []) + self.assertEqual(merge, [merge_path]) + + def test_w_server_timestamp(self): + from google.cloud.firestore_v1beta1.constants import SERVER_TIMESTAMP + + document_data = {'a': {'b': SERVER_TIMESTAMP, 'c': 'd'}} + merge_string = 'a.b' + merge_path = self._make_field_path('a', 'b') + + ( + transform_paths, actual_data, data_merge, transform_merge, merge, + ) = self._call_fut(document_data, [merge_string]) + + self.assertEqual(transform_paths, [merge_path]) + self.assertEqual(actual_data, {}) + self.assertEqual(data_merge, []) + self.assertEqual(transform_merge, [merge_path]) + self.assertEqual(merge, [merge_path]) + + def test_w_simple_and_server_timestamp(self): + from google.cloud.firestore_v1beta1.constants import SERVER_TIMESTAMP + + document_data = {'a': {'b': SERVER_TIMESTAMP, 'c': 'd'}} + merge_path = self._make_field_path('a') + + ( + transform_paths, actual_data, data_merge, transform_merge, merge, + ) = self._call_fut(document_data, [merge_path]) + + path_a_b = self._make_field_path('a', 'b') + path_a_c = self._make_field_path('a', 'c') + self.assertEqual(transform_paths, [path_a_b]) + self.assertEqual(actual_data, {'a': {'c': 'd'}}) + self.assertEqual(data_merge, [path_a_c]) + self.assertEqual(transform_merge, []) + self.assertEqual(merge, [merge_path]) + + def test_w_simple_and_server_timestamp_two_merge_paths(self): + from google.cloud.firestore_v1beta1.constants import SERVER_TIMESTAMP + + document_data = {'a': {'b': SERVER_TIMESTAMP, 'c': 'd'}} + path_a_b = self._make_field_path('a', 'b') + path_a_c = self._make_field_path('a', 'c') + + ( + transform_paths, actual_data, data_merge, transform_merge, merge, + ) = self._call_fut(document_data, [path_a_b, path_a_c]) + + self.assertEqual(transform_paths, [path_a_b]) + self.assertEqual(actual_data, {'a': {'c': 'd'}}) + self.assertEqual(data_merge, [path_a_c]) + self.assertEqual(transform_merge, [path_a_b]) + self.assertEqual(merge, [path_a_b, path_a_c]) + + +class Test_pbs_for_set_with_merge(unittest.TestCase): + + @staticmethod + def _call_fut(document_path, document_data, merge): + from google.cloud.firestore_v1beta1 import _helpers + + return _helpers.pbs_for_set_with_merge( + document_path, document_data, merge=merge) + + @staticmethod + def _make_write_w_document(document_path, **data): + from google.cloud.firestore_v1beta1.proto import document_pb2 + from google.cloud.firestore_v1beta1.proto import write_pb2 + from google.cloud.firestore_v1beta1._helpers import encode_dict + + return write_pb2.Write( + update=document_pb2.Document( + name=document_path, + fields=encode_dict(data), + ), + ) + + @staticmethod + def _make_write_w_transform(document_path, fields): + from google.cloud.firestore_v1beta1.proto import write_pb2 + from google.cloud.firestore_v1beta1.gapic import enums + + server_val = enums.DocumentTransform.FieldTransform.ServerValue + transforms = [ + write_pb2.DocumentTransform.FieldTransform( + field_path=field, set_to_server_value=server_val.REQUEST_TIME) + for field in fields + ] + + return write_pb2.Write( + transform=write_pb2.DocumentTransform( + document=document_path, + field_transforms=transforms, + ), + ) + + @staticmethod + def _update_document_mask(update_pb, field_paths): + from google.cloud.firestore_v1beta1.proto import common_pb2 + + update_pb.update_mask.CopyFrom( + common_pb2.DocumentMask(field_paths=field_paths)) + + def test_with_merge_true_wo_transform(self): + document_path = _make_ref_string(u'little', u'town', u'of', u'ham') + document_data = { + 'cheese': 1.5, + 'crackers': True, + } + + write_pbs = self._call_fut(document_path, document_data, merge=True) + + update_pb = self._make_write_w_document(document_path, **document_data) + self._update_document_mask( + update_pb, field_paths=sorted(document_data)) + expected_pbs = [update_pb] + self.assertEqual(write_pbs, expected_pbs) + + def test_with_merge_field_wo_transform(self): + document_path = _make_ref_string(u'little', u'town', u'of', u'ham') + document_data = { + 'cheese': 1.5, + 'crackers': True, + } + + write_pbs = self._call_fut( + document_path, document_data, merge=['cheese']) + + update_pb = self._make_write_w_document( + document_path, cheese=document_data['cheese']) + self._update_document_mask( + update_pb, field_paths=['cheese']) + expected_pbs = [update_pb] + self.assertEqual(write_pbs, expected_pbs) + + def test_with_merge_true_w_transform(self): + from google.cloud.firestore_v1beta1.constants import SERVER_TIMESTAMP + + document_path = _make_ref_string(u'little', u'town', u'of', u'ham') + update_data = { + 'cheese': 1.5, + 'crackers': True, + } + document_data = update_data.copy() + document_data['butter'] = SERVER_TIMESTAMP + + write_pbs = self._call_fut(document_path, document_data, merge=True) + + update_pb = self._make_write_w_document(document_path, **update_data) + self._update_document_mask( + update_pb, field_paths=sorted(update_data)) + transform_pb = self._make_write_w_transform( + document_path, fields=['butter']) + expected_pbs = [ + update_pb, + transform_pb, + ] + self.assertEqual(write_pbs, expected_pbs) + + def test_with_merge_field_w_transform(self): + from google.cloud.firestore_v1beta1.constants import SERVER_TIMESTAMP + + document_path = _make_ref_string(u'little', u'town', u'of', u'ham') + update_data = { + 'cheese': 1.5, + 'crackers': True, + } + document_data = update_data.copy() + document_data['butter'] = SERVER_TIMESTAMP + + write_pbs = self._call_fut( + document_path, document_data, merge=['cheese', 'butter']) + + update_pb = self._make_write_w_document( + document_path, cheese=document_data['cheese']) + self._update_document_mask(update_pb, ['cheese']) + transform_pb = self._make_write_w_transform( + document_path, fields=['butter']) + expected_pbs = [ + update_pb, + transform_pb, + ] + self.assertEqual(write_pbs, expected_pbs) + + def test_with_merge_field_w_transform_masking_simple(self): + from google.cloud.firestore_v1beta1.constants import SERVER_TIMESTAMP + + document_path = _make_ref_string(u'little', u'town', u'of', u'ham') + update_data = { + 'cheese': 1.5, + 'crackers': True, + } + document_data = update_data.copy() + document_data['butter'] = {'pecan': SERVER_TIMESTAMP} + + write_pbs = self._call_fut( + document_path, document_data, merge=['butter.pecan']) + + update_pb = self._make_write_w_document(document_path) + transform_pb = self._make_write_w_transform( + document_path, fields=['butter.pecan']) + expected_pbs = [ + update_pb, + transform_pb, + ] + self.assertEqual(write_pbs, expected_pbs) + + def test_with_merge_field_w_transform_parent(self): + from google.cloud.firestore_v1beta1.constants import SERVER_TIMESTAMP + + document_path = _make_ref_string(u'little', u'town', u'of', u'ham') + update_data = { + 'cheese': 1.5, + 'crackers': True, + } + document_data = update_data.copy() + document_data['butter'] = { + 'popcorn': 'yum', + 'pecan': SERVER_TIMESTAMP, + } + + write_pbs = self._call_fut( + document_path, document_data, merge=['cheese', 'butter']) + + update_pb = self._make_write_w_document( + document_path, + cheese=update_data['cheese'], + butter={'popcorn': 'yum'}, + ) + self._update_document_mask(update_pb, ['cheese', 'butter']) + transform_pb = self._make_write_w_transform( + document_path, fields=['butter.pecan']) + expected_pbs = [ + update_pb, + transform_pb, + ] + self.assertEqual(write_pbs, expected_pbs) + + class Test_pbs_for_update(unittest.TestCase): @staticmethod @@ -1620,12 +2317,18 @@ def _helper(self, option=None, do_transform=False, **write_kwargs): map_pb = document_pb2.MapValue(fields={ 'yum': _value_pb(bytes_value=value), }) + + if do_transform: + field_paths = [field_path1, 'blog'] + else: + field_paths = [field_path1] + expected_update_pb = write_pb2.Write( update=document_pb2.Document( name=document_path, fields={'bitez': _value_pb(map_value=map_pb)}, ), - update_mask=common_pb2.DocumentMask(field_paths=[field_path1]), + update_mask=common_pb2.DocumentMask(field_paths=field_paths), **write_kwargs ) if isinstance(option, ExistsOption): diff --git a/tests/unit/test_cross_language.py b/tests/unit/test_cross_language.py index 96abc1af7..b6a99295e 100644 --- a/tests/unit/test_cross_language.py +++ b/tests/unit/test_cross_language.py @@ -16,120 +16,243 @@ import glob import json import os -import unittest import mock -from google.cloud.firestore_v1beta1.proto import test_pb2 +import pytest + from google.protobuf import text_format +from google.cloud.firestore_v1beta1.proto import firestore_pb2 +from google.cloud.firestore_v1beta1.proto import test_pb2 +from google.cloud.firestore_v1beta1.proto import write_pb2 +_UNIMPLEMENTED_FEATURES = [ + # tests having to do with the ArrayUnion, ArrayRemove, and Delete + # transforms + 'create-all-transforms.textproto', + 'create-arrayremove-multi.textproto', + 'create-arrayremove-nested.textproto', + 'create-arrayremove-noarray-nested.textproto', + 'create-arrayremove-noarray.textproto', + 'create-arrayremove.textproto', + 'create-arrayunion-multi.textproto', + 'create-arrayunion-nested.textproto', + 'create-arrayunion-noarray-nested.textproto', + 'create-arrayunion-noarray.textproto', + 'create-arrayunion.textproto', + 'set-all-transforms.textproto', + 'set-arrayremove-multi.textproto', + 'set-arrayremove-nested.textproto', + 'set-arrayremove-noarray-nested.textproto', + 'set-arrayremove-noarray.textproto', + 'set-arrayremove.textproto', + 'set-arrayunion-multi.textproto', + 'set-arrayunion-nested.textproto', + 'set-arrayunion-noarray-nested.textproto', + 'set-arrayunion-noarray.textproto', + 'set-arrayunion.textproto', + 'set-del-merge-alone.textproto', + 'set-del-merge.textproto', + 'set-del-mergeall.textproto', + 'set-del-nomerge.textproto', + 'update-all-transforms.textproto', + 'update-arrayremove-alone.textproto', + 'update-arrayremove-multi.textproto', + 'update-arrayremove-nested.textproto', + 'update-arrayremove-noarray-nested.textproto', + 'update-arrayremove-noarray.textproto', + 'update-arrayremove.textproto', + 'update-arrayunion-alone.textproto', + 'update-arrayunion-multi.textproto', + 'update-arrayunion-nested.textproto', + 'update-arrayunion-noarray-nested.textproto', + 'update-arrayunion-noarray.textproto', + 'update-arrayunion.textproto', + 'update-del-alone.textproto', + 'update-del-dot.textproto', + 'update-del-nested.textproto', + 'update-del-noarray-nested.textproto', + 'update-del-noarray.textproto', + 'update-del.textproto', + ] -class TestCrossLanguage(unittest.TestCase): - - def test_cross_language(self): - filenames = sorted(glob.glob('tests/unit/testdata/*.textproto')) - failed = 0 - descs = [] - for test_filename in filenames: - bytes = open(test_filename, 'r').read() - test_proto = test_pb2.Test() - text_format.Merge(bytes, test_proto) - desc = '%s (%s)' % ( - test_proto.description, - os.path.splitext(os.path.basename(test_filename))[0]) - try: - self.run_write_test(test_proto, desc) - except Exception: - failed += 1 - # print(desc, test_proto) # for debugging - # print(error.args[0]) # for debugging - descs.append(desc) - # for desc in descs: # for debugging - # print(desc) # for debugging - # print(str(failed) + "/" + str(len(filenames))) # for debugging - - def run_write_test(self, test_proto, desc): - from google.cloud.firestore_v1beta1.proto import firestore_pb2 - from google.cloud.firestore_v1beta1.proto import write_pb2 - - # Create a minimal fake GAPIC with a dummy result. - firestore_api = mock.Mock(spec=['commit']) - commit_response = firestore_pb2.CommitResponse( - write_results=[write_pb2.WriteResult()], + +def _load_testproto(filename): + with open(filename, 'r') as tp_file: + tp_text = tp_file.read() + test_proto = test_pb2.Test() + text_format.Merge(tp_text, test_proto) + shortname = os.path.split(filename)[-1] + test_proto.description = ( + test_proto.description + ' (%s)' % shortname ) - firestore_api.commit.return_value = commit_response - - kind = test_proto.WhichOneof("test") - call = None - if kind == "create": - tp = test_proto.create - client, doc = self.setup(firestore_api, tp) - data = convert_data(json.loads(tp.json_data)) - call = functools.partial(doc.create, data) - elif kind == "get": - tp = test_proto.get - client, doc = self.setup(firestore_api, tp) - call = functools.partial(doc.get, None, None) - try: - tp.is_error - except AttributeError: - return - elif kind == "set": - tp = test_proto.set - client, doc = self.setup(firestore_api, tp) - data = convert_data(json.loads(tp.json_data)) - if tp.HasField("option"): - merge = True - else: - merge = False - call = functools.partial(doc.set, data, merge) - elif kind == "update": - tp = test_proto.update - client, doc = self.setup(firestore_api, tp) - data = convert_data(json.loads(tp.json_data)) - if tp.HasField("precondition"): - option = convert_precondition(tp.precondition) - else: - option = None - call = functools.partial(doc.update, data, option) - elif kind == "update_paths": - # Python client doesn't have a way to call update with - # a list of field paths. - return - else: - assert kind == "delete" - tp = test_proto.delete - client, doc = self.setup(firestore_api, tp) - if tp.HasField("precondition"): - option = convert_precondition(tp.precondition) - else: - option = None - call = functools.partial(doc.delete, option) - - if tp.is_error: - # TODO: is there a subclass of Exception we can check for? - with self.assertRaises(Exception): - call() - else: + return test_proto + + +_UNIMPLEMENTED_FEATURE_TESTPROTOS = [ + _load_testproto(filename) for filename in sorted( + glob.glob('tests/unit/testdata/*.textproto')) + if os.path.split(filename)[-1] in _UNIMPLEMENTED_FEATURES +] + +IMPLEMENTED_FEATURE_TESTPROTOS = [ + _load_testproto(filename) for filename in sorted( + glob.glob('tests/unit/testdata/*.textproto')) + if not os.path.split(filename)[-1] in _UNIMPLEMENTED_FEATURES +] + +_CREATE_TESTPROTOS = [ + test_proto for test_proto in IMPLEMENTED_FEATURE_TESTPROTOS + if test_proto.WhichOneof('test') == 'create'] + +_GET_TESTPROTOS = [ + test_proto for test_proto in IMPLEMENTED_FEATURE_TESTPROTOS + if test_proto.WhichOneof('test') == 'get'] + +_SET_TESTPROTOS = [ + test_proto for test_proto in IMPLEMENTED_FEATURE_TESTPROTOS + if test_proto.WhichOneof('test') == 'set'] + +_UPDATE_TESTPROTOS = [ + test_proto for test_proto in IMPLEMENTED_FEATURE_TESTPROTOS + if test_proto.WhichOneof('test') == 'update'] + +_UPDATE_PATHS_TESTPROTOS = [ + test_proto for test_proto in IMPLEMENTED_FEATURE_TESTPROTOS + if test_proto.WhichOneof('test') == 'update_paths'] + +_DELETE_TESTPROTOS = [ + test_proto for test_proto in IMPLEMENTED_FEATURE_TESTPROTOS + if test_proto.WhichOneof('test') == 'delete'] + +_LISTEN_TESTPROTOS = [ + test_proto for test_proto in IMPLEMENTED_FEATURE_TESTPROTOS + if test_proto.WhichOneof('test') == 'listen'] + + +def _mock_firestore_api(): + firestore_api = mock.Mock(spec=['commit']) + commit_response = firestore_pb2.CommitResponse( + write_results=[write_pb2.WriteResult()], + ) + firestore_api.commit.return_value = commit_response + return firestore_api + + +def _make_client_document(firestore_api, testcase): + from google.cloud.firestore_v1beta1 import Client + from google.cloud.firestore_v1beta1.client import DEFAULT_DATABASE + import google.auth.credentials + + _, project, _, database, _, doc_path = testcase.doc_ref_path.split('/', 5) + assert database == DEFAULT_DATABASE + + # Attach the fake GAPIC to a real client. + credentials = mock.Mock(spec=google.auth.credentials.Credentials) + client = Client(project=project, credentials=credentials) + client._firestore_api_internal = firestore_api + return client, client.document(doc_path) + + +def _run_testcase(testcase, call, firestore_api, client): + if getattr(testcase, 'is_error', False): + # TODO: is there a subclass of Exception we can check for? + with pytest.raises(Exception): call() - firestore_api.commit.assert_called_once_with( - client._database_string, - list(tp.request.writes), - transaction=None, - metadata=client._rpc_metadata) + else: + call() + firestore_api.commit.assert_called_once_with( + client._database_string, + list(testcase.request.writes), + transaction=None, + metadata=client._rpc_metadata) + + +@pytest.mark.parametrize('test_proto', _CREATE_TESTPROTOS) +def test_create_testprotos(test_proto): + testcase = test_proto.create + firestore_api = _mock_firestore_api() + client, document = _make_client_document(firestore_api, testcase) + data = convert_data(json.loads(testcase.json_data)) + call = functools.partial(document.create, data) + _run_testcase(testcase, call, firestore_api, client) + + +@pytest.mark.parametrize('test_proto', _GET_TESTPROTOS) +def test_get_testprotos(test_proto): + testcase = test_proto.get + # XXX this stub currently does nothing because no get testcases have + # is_error; taking this bit out causes the existing tests to fail + # due to a lack of batch getting + try: + testcase.is_error + except AttributeError: + return + else: # pragma: NO COVER + testcase = test_proto.get + firestore_api = _mock_firestore_api() + client, document = _make_client_document(firestore_api, testcase) + call = functools.partial(document.get, None, None) + _run_testcase(testcase, call, firestore_api, client) + + +@pytest.mark.parametrize('test_proto', _SET_TESTPROTOS) +def test_set_testprotos(test_proto): + testcase = test_proto.set + firestore_api = _mock_firestore_api() + client, document = _make_client_document(firestore_api, testcase) + data = convert_data(json.loads(testcase.json_data)) + if testcase.HasField("option"): + merge = convert_set_option(testcase.option) + else: + merge = False + call = functools.partial(document.set, data, merge=merge) + _run_testcase(testcase, call, firestore_api, client) + + +@pytest.mark.parametrize('test_proto', _UPDATE_TESTPROTOS) +def test_update_testprotos(test_proto): + testcase = test_proto.update + firestore_api = _mock_firestore_api() + client, document = _make_client_document(firestore_api, testcase) + data = convert_data(json.loads(testcase.json_data)) + if testcase.HasField("precondition"): + option = convert_precondition(testcase.precondition) + else: + option = None + call = functools.partial(document.update, data, option) + _run_testcase(testcase, call, firestore_api, client) - def setup(self, firestore_api, proto): - from google.cloud.firestore_v1beta1 import Client - from google.cloud.firestore_v1beta1.client import DEFAULT_DATABASE - import google.auth.credentials - _, project, _, database, _, doc_path = proto.doc_ref_path.split('/', 5) - self.assertEqual(database, DEFAULT_DATABASE) +@pytest.mark.skip( + reason="Python has no way to call update with a list of field paths.") +@pytest.mark.parametrize('test_proto', _UPDATE_PATHS_TESTPROTOS) +def test_update_paths_testprotos(test_proto): # pragma: NO COVER + pass - # Attach the fake GAPIC to a real client. - credentials = mock.Mock(spec=google.auth.credentials.Credentials) - client = Client(project=project, credentials=credentials) - client._firestore_api_internal = firestore_api - return client, client.document(doc_path) + +@pytest.mark.parametrize('test_proto', _DELETE_TESTPROTOS) +def test_delete_testprotos(test_proto): + testcase = test_proto.delete + firestore_api = _mock_firestore_api() + client, document = _make_client_document(firestore_api, testcase) + if testcase.HasField("precondition"): + option = convert_precondition(testcase.precondition) + else: + option = None + call = functools.partial(document.delete, option) + _run_testcase(testcase, call, firestore_api, client) + + +@pytest.mark.skip(reason="Watch aka listen not yet implemented in Python.") +@pytest.mark.parametrize('test_proto', _LISTEN_TESTPROTOS) +def test_listen_paths_testprotos(test_proto): # pragma: NO COVER + pass + + +@pytest.mark.skip(reason="Feature not yet implemented in Python.") +@pytest.mark.parametrize('test_proto', _UNIMPLEMENTED_FEATURE_TESTPROTOS) +def test_unimplemented_features_testprotos(test_proto): # pragma: NO COVER + pass def convert_data(v): @@ -149,10 +272,24 @@ def convert_data(v): return v +def convert_set_option(option): + from google.cloud.firestore_v1beta1 import _helpers + + if option.fields: + return [ + _helpers.FieldPath(*field.field).to_api_repr() + for field in option.fields + ] + + assert option.all + return True + + def convert_precondition(precond): from google.cloud.firestore_v1beta1 import Client if precond.HasField('exists'): return Client.write_option(exists=precond.exists) - else: # update_time - return Client.write_option(last_update_time=precond.update_time) + + assert precond.HasField('update_time') + return Client.write_option(last_update_time=precond.update_time) diff --git a/tests/unit/test_document.py b/tests/unit/test_document.py index 00067b749..369d98092 100644 --- a/tests/unit/test_document.py +++ b/tests/unit/test_document.py @@ -278,7 +278,7 @@ def _write_pb_for_set(document_path, document_data, merge): ) if merge: _, _, field_paths = _helpers.process_server_timestamp( - document_data) + document_data, split_on_dots=False) field_paths = _helpers.canonicalize_field_paths(field_paths) mask = common_pb2.DocumentMask(field_paths=sorted(field_paths)) write_pbs.update_mask.CopyFrom(mask) @@ -381,11 +381,21 @@ def _update_helper(self, **option_kwargs): client._database_string, [write_pb], transaction=None, metadata=client._rpc_metadata) + def test_update_with_exists(self): + with self.assertRaises(ValueError): + self._update_helper(exists=True) + def test_update(self): self._update_helper() - def test_update_with_exists(self): - self._update_helper(exists=True) + def test_update_with_precondition(self): + from google.protobuf import timestamp_pb2 + + timestamp = timestamp_pb2.Timestamp( + seconds=1058655101, + nanos=100022244, + ) + self._update_helper(last_update_time=timestamp) def test_empty_update(self): # Create a minimal fake GAPIC with a dummy response. diff --git a/tests/unit/testdata/create-1.textproto b/tests/unit/testdata/create-1.textproto deleted file mode 100644 index c77e1fcd2..000000000 --- a/tests/unit/testdata/create-1.textproto +++ /dev/null @@ -1,27 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# A simple call, resulting in a single update operation. - -description: "basic" -create: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{\"a\": 1}" - request: < - database: "projects/projectID/databases/(default)" - writes: < - update: < - name: "projects/projectID/databases/(default)/documents/C/d" - fields: < - key: "a" - value: < - integer_value: 1 - > - > - > - current_document: < - exists: false - > - > - > -> diff --git a/tests/unit/testdata/create-10.textproto b/tests/unit/testdata/create-10.textproto deleted file mode 100644 index 84a43ac87..000000000 --- a/tests/unit/testdata/create-10.textproto +++ /dev/null @@ -1,41 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# A document can have more than one ServerTimestamp field. Since all the -# ServerTimestamp fields are removed, the only field in the update is "a". - -description: "multiple ServerTimestamp fields" -create: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{\"a\": 1, \"b\": \"ServerTimestamp\", \"c\": {\"d\": \"ServerTimestamp\"}}" - request: < - database: "projects/projectID/databases/(default)" - writes: < - update: < - name: "projects/projectID/databases/(default)/documents/C/d" - fields: < - key: "a" - value: < - integer_value: 1 - > - > - > - current_document: < - exists: false - > - > - writes: < - transform: < - document: "projects/projectID/databases/(default)/documents/C/d" - field_transforms: < - field_path: "b" - set_to_server_value: REQUEST_TIME - > - field_transforms: < - field_path: "c.d" - set_to_server_value: REQUEST_TIME - > - > - > - > -> diff --git a/tests/unit/testdata/create-11.textproto b/tests/unit/testdata/create-11.textproto deleted file mode 100644 index 790967a7e..000000000 --- a/tests/unit/testdata/create-11.textproto +++ /dev/null @@ -1,12 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# The ServerTimestamp sentinel must be the value of a field. Firestore transforms -# don't support array indexing. - -description: "ServerTimestamp cannot be in an array value" -create: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{\"a\": [1, 2, \"ServerTimestamp\"]}" - is_error: true -> diff --git a/tests/unit/testdata/create-12.textproto b/tests/unit/testdata/create-12.textproto deleted file mode 100644 index 5af92ae43..000000000 --- a/tests/unit/testdata/create-12.textproto +++ /dev/null @@ -1,12 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# There cannot be an array value anywhere on the path from the document root to -# the ServerTimestamp sentinel. Firestore transforms don't support array indexing. - -description: "ServerTimestamp cannot be anywhere inside an array value" -create: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{\"a\": [1, {\"b\": \"ServerTimestamp\"}]}" - is_error: true -> diff --git a/tests/unit/testdata/create-13.textproto b/tests/unit/testdata/create-13.textproto deleted file mode 100644 index a64e0e1cf..000000000 --- a/tests/unit/testdata/create-13.textproto +++ /dev/null @@ -1,13 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# The Delete sentinel must be the value of a field. Deletes are implemented by -# turning the path to the Delete sentinel into a FieldPath, and FieldPaths do not -# support array indexing. - -description: "Delete cannot be in an array value" -create: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{\"a\": [1, 2, \"Delete\"]}" - is_error: true -> diff --git a/tests/unit/testdata/create-14.textproto b/tests/unit/testdata/create-14.textproto deleted file mode 100644 index 98a50328d..000000000 --- a/tests/unit/testdata/create-14.textproto +++ /dev/null @@ -1,13 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# The Delete sentinel must be the value of a field. Deletes are implemented by -# turning the path to the Delete sentinel into a FieldPath, and FieldPaths do not -# support array indexing. - -description: "Delete cannot be anywhere inside an array value" -create: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{\"a\": [1, {\"b\": \"Delete\"}]}" - is_error: true -> diff --git a/tests/unit/testdata/create-2.textproto b/tests/unit/testdata/create-2.textproto deleted file mode 100644 index 5a68a1873..000000000 --- a/tests/unit/testdata/create-2.textproto +++ /dev/null @@ -1,61 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# A call to a write method with complicated input data. - -description: "complex" -create: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{\"a\": [1, 2.5], \"b\": {\"c\": [\"three\", {\"d\": true}]}}" - request: < - database: "projects/projectID/databases/(default)" - writes: < - update: < - name: "projects/projectID/databases/(default)/documents/C/d" - fields: < - key: "a" - value: < - array_value: < - values: < - integer_value: 1 - > - values: < - double_value: 2.5 - > - > - > - > - fields: < - key: "b" - value: < - map_value: < - fields: < - key: "c" - value: < - array_value: < - values: < - string_value: "three" - > - values: < - map_value: < - fields: < - key: "d" - value: < - boolean_value: true - > - > - > - > - > - > - > - > - > - > - > - current_document: < - exists: false - > - > - > -> diff --git a/tests/unit/testdata/create-3.textproto.failed b/tests/unit/testdata/create-3.textproto.failed deleted file mode 100644 index 9af179462..000000000 --- a/tests/unit/testdata/create-3.textproto.failed +++ /dev/null @@ -1,11 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# Empty fields are not allowed. - -description: "empty field" -create: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{\"\": 1}" - is_error: true -> diff --git a/tests/unit/testdata/create-4.textproto b/tests/unit/testdata/create-4.textproto deleted file mode 100644 index 4da3f7d07..000000000 --- a/tests/unit/testdata/create-4.textproto +++ /dev/null @@ -1,40 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# Create and Set treat their map keys literally. They do not split on dots. - -description: "don\342\200\231t split on dots" -create: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{ \"a.b\": { \"c.d\": 1 }, \"e\": 2 }" - request: < - database: "projects/projectID/databases/(default)" - writes: < - update: < - name: "projects/projectID/databases/(default)/documents/C/d" - fields: < - key: "a.b" - value: < - map_value: < - fields: < - key: "c.d" - value: < - integer_value: 1 - > - > - > - > - > - fields: < - key: "e" - value: < - integer_value: 2 - > - > - > - current_document: < - exists: false - > - > - > -> diff --git a/tests/unit/testdata/create-5.textproto b/tests/unit/testdata/create-5.textproto deleted file mode 100644 index 762a96c82..000000000 --- a/tests/unit/testdata/create-5.textproto +++ /dev/null @@ -1,41 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# Create and Set treat their map keys literally. They do not escape special -# characters. - -description: "non-alpha characters in map keys" -create: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{ \"*\": { \".\": 1 }, \"~\": 2 }" - request: < - database: "projects/projectID/databases/(default)" - writes: < - update: < - name: "projects/projectID/databases/(default)/documents/C/d" - fields: < - key: "*" - value: < - map_value: < - fields: < - key: "." - value: < - integer_value: 1 - > - > - > - > - > - fields: < - key: "~" - value: < - integer_value: 2 - > - > - > - current_document: < - exists: false - > - > - > -> diff --git a/tests/unit/testdata/create-6.textproto b/tests/unit/testdata/create-6.textproto deleted file mode 100644 index efe258fd7..000000000 --- a/tests/unit/testdata/create-6.textproto +++ /dev/null @@ -1,11 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# The Delete sentinel cannot be used in Create, or in Set without a Merge option. - -description: "Delete cannot appear in data" -create: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{\"a\": 1, \"b\": \"Delete\"}" - is_error: true -> diff --git a/tests/unit/testdata/create-7.textproto b/tests/unit/testdata/create-7.textproto deleted file mode 100644 index dc476c298..000000000 --- a/tests/unit/testdata/create-7.textproto +++ /dev/null @@ -1,39 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# A key with the special ServerTimestamp sentinel is removed from the data in the -# update operation. Instead it appears in a separate Transform operation. Note -# that in these tests, the string "ServerTimestamp" should be replaced with the -# special ServerTimestamp value. - -description: "ServerTimestamp with data" -create: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{\"a\": 1, \"b\": \"ServerTimestamp\"}" - request: < - database: "projects/projectID/databases/(default)" - writes: < - update: < - name: "projects/projectID/databases/(default)/documents/C/d" - fields: < - key: "a" - value: < - integer_value: 1 - > - > - > - current_document: < - exists: false - > - > - writes: < - transform: < - document: "projects/projectID/databases/(default)/documents/C/d" - field_transforms: < - field_path: "b" - set_to_server_value: REQUEST_TIME - > - > - > - > -> diff --git a/tests/unit/testdata/create-8.textproto.failed b/tests/unit/testdata/create-8.textproto.failed deleted file mode 100644 index 287e91678..000000000 --- a/tests/unit/testdata/create-8.textproto.failed +++ /dev/null @@ -1,26 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# If the only values in the input are ServerTimestamps, then no update operation -# should be produced unless there are preconditions. - -description: "ServerTimestamp alone" -create: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{\"a\": \"ServerTimestamp\"}" - request: < - database: "projects/projectID/databases/(default)" - writes: < - transform: < - document: "projects/projectID/databases/(default)/documents/C/d" - field_transforms: < - field_path: "a" - set_to_server_value: REQUEST_TIME - > - > - current_document: < - exists: false - > - > - > -> diff --git a/tests/unit/testdata/create-9.textproto b/tests/unit/testdata/create-9.textproto deleted file mode 100644 index 291a657c8..000000000 --- a/tests/unit/testdata/create-9.textproto +++ /dev/null @@ -1,38 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# A ServerTimestamp value can occur at any depth. In this case, the transform -# applies to the field path "b.c". Since "c" is removed from the update, "b" -# becomes empty, so it is also removed from the update. - -description: "nested ServerTimestamp field" -create: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{\"a\": 1, \"b\": {\"c\": \"ServerTimestamp\"}}" - request: < - database: "projects/projectID/databases/(default)" - writes: < - update: < - name: "projects/projectID/databases/(default)/documents/C/d" - fields: < - key: "a" - value: < - integer_value: 1 - > - > - > - current_document: < - exists: false - > - > - writes: < - transform: < - document: "projects/projectID/databases/(default)/documents/C/d" - field_transforms: < - field_path: "b.c" - set_to_server_value: REQUEST_TIME - > - > - > - > -> diff --git a/tests/unit/testdata/delete-1.textproto b/tests/unit/testdata/delete-1.textproto deleted file mode 100644 index 4ceba50b7..000000000 --- a/tests/unit/testdata/delete-1.textproto +++ /dev/null @@ -1,15 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# An ordinary Delete call. - -description: "delete without precondition" -delete: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - request: < - database: "projects/projectID/databases/(default)" - writes: < - delete: "projects/projectID/databases/(default)/documents/C/d" - > - > -> diff --git a/tests/unit/testdata/delete-2.textproto b/tests/unit/testdata/delete-2.textproto deleted file mode 100644 index d7a7e6355..000000000 --- a/tests/unit/testdata/delete-2.textproto +++ /dev/null @@ -1,25 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# Delete supports a last-update-time precondition. - -description: "delete with last-update-time precondition" -delete: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - precondition: < - update_time: < - seconds: 42 - > - > - request: < - database: "projects/projectID/databases/(default)" - writes: < - delete: "projects/projectID/databases/(default)/documents/C/d" - current_document: < - update_time: < - seconds: 42 - > - > - > - > -> diff --git a/tests/unit/testdata/delete-3.textproto b/tests/unit/testdata/delete-3.textproto deleted file mode 100644 index 362781c46..000000000 --- a/tests/unit/testdata/delete-3.textproto +++ /dev/null @@ -1,21 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# Delete supports an exists precondition. - -description: "delete with exists precondition" -delete: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - precondition: < - exists: true - > - request: < - database: "projects/projectID/databases/(default)" - writes: < - delete: "projects/projectID/databases/(default)/documents/C/d" - current_document: < - exists: true - > - > - > -> diff --git a/tests/unit/testdata/get-1.textproto b/tests/unit/testdata/get-1.textproto deleted file mode 100644 index 69abc86e7..000000000 --- a/tests/unit/testdata/get-1.textproto +++ /dev/null @@ -1,12 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# A call to DocumentRef.Get. - -description: "Get a document" -get: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - request: < - name: "projects/projectID/databases/(default)/documents/C/d" - > -> diff --git a/tests/unit/testdata/query-cursor-endbefore-empty-map.textproto b/tests/unit/testdata/query-cursor-endbefore-empty-map.textproto new file mode 100644 index 000000000..c197d23af --- /dev/null +++ b/tests/unit/testdata/query-cursor-endbefore-empty-map.textproto @@ -0,0 +1,41 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# Cursor methods are allowed to use empty maps with EndBefore. It should result in +# an empty map in the query. + +description: "query: EndBefore with explicit empty map" +query: < + coll_path: "projects/projectID/databases/(default)/documents/C" + clauses: < + order_by: < + path: < + field: "a" + > + direction: "asc" + > + > + clauses: < + end_before: < + json_values: "{}" + > + > + query: < + from: < + collection_id: "C" + > + order_by: < + field: < + field_path: "a" + > + direction: ASCENDING + > + end_at: < + values: < + map_value: < + > + > + before: true + > + > +> diff --git a/tests/unit/testdata/query-cursor-endbefore-empty.textproto b/tests/unit/testdata/query-cursor-endbefore-empty.textproto new file mode 100644 index 000000000..a41775abf --- /dev/null +++ b/tests/unit/testdata/query-cursor-endbefore-empty.textproto @@ -0,0 +1,23 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# Cursor methods are not allowed to use empty values with EndBefore. It should +# result in an error. + +description: "query: EndBefore with empty values" +query: < + coll_path: "projects/projectID/databases/(default)/documents/C" + clauses: < + order_by: < + path: < + field: "a" + > + direction: "asc" + > + > + clauses: < + end_before: < + > + > + is_error: true +> diff --git a/tests/unit/testdata/query-cursor-startat-empty-map.textproto b/tests/unit/testdata/query-cursor-startat-empty-map.textproto new file mode 100644 index 000000000..557aca2c9 --- /dev/null +++ b/tests/unit/testdata/query-cursor-startat-empty-map.textproto @@ -0,0 +1,41 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# Cursor methods are allowed to use empty maps with StartAt. It should result in +# an empty map in the query. + +description: "query: StartAt with explicit empty map" +query: < + coll_path: "projects/projectID/databases/(default)/documents/C" + clauses: < + order_by: < + path: < + field: "a" + > + direction: "asc" + > + > + clauses: < + start_at: < + json_values: "{}" + > + > + query: < + from: < + collection_id: "C" + > + order_by: < + field: < + field_path: "a" + > + direction: ASCENDING + > + start_at: < + values: < + map_value: < + > + > + before: true + > + > +> diff --git a/tests/unit/testdata/query-cursor-startat-empty.textproto b/tests/unit/testdata/query-cursor-startat-empty.textproto new file mode 100644 index 000000000..e0c54d98a --- /dev/null +++ b/tests/unit/testdata/query-cursor-startat-empty.textproto @@ -0,0 +1,23 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# Cursor methods are not allowed to use empty values with StartAt. It should +# result in an error. + +description: "query: StartAt with empty values" +query: < + coll_path: "projects/projectID/databases/(default)/documents/C" + clauses: < + order_by: < + path: < + field: "a" + > + direction: "asc" + > + > + clauses: < + start_at: < + > + > + is_error: true +> diff --git a/tests/unit/testdata/set-1.textproto b/tests/unit/testdata/set-1.textproto deleted file mode 100644 index 1332c5092..000000000 --- a/tests/unit/testdata/set-1.textproto +++ /dev/null @@ -1,24 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# A simple call, resulting in a single update operation. - -description: "basic" -set: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{\"a\": 1}" - request: < - database: "projects/projectID/databases/(default)" - writes: < - update: < - name: "projects/projectID/databases/(default)/documents/C/d" - fields: < - key: "a" - value: < - integer_value: 1 - > - > - > - > - > -> diff --git a/tests/unit/testdata/set-10.textproto b/tests/unit/testdata/set-10.textproto deleted file mode 100644 index 42f0617bd..000000000 --- a/tests/unit/testdata/set-10.textproto +++ /dev/null @@ -1,38 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# A document can have more than one ServerTimestamp field. Since all the -# ServerTimestamp fields are removed, the only field in the update is "a". - -description: "multiple ServerTimestamp fields" -set: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{\"a\": 1, \"b\": \"ServerTimestamp\", \"c\": {\"d\": \"ServerTimestamp\"}}" - request: < - database: "projects/projectID/databases/(default)" - writes: < - update: < - name: "projects/projectID/databases/(default)/documents/C/d" - fields: < - key: "a" - value: < - integer_value: 1 - > - > - > - > - writes: < - transform: < - document: "projects/projectID/databases/(default)/documents/C/d" - field_transforms: < - field_path: "b" - set_to_server_value: REQUEST_TIME - > - field_transforms: < - field_path: "c.d" - set_to_server_value: REQUEST_TIME - > - > - > - > -> diff --git a/tests/unit/testdata/set-11.textproto b/tests/unit/testdata/set-11.textproto deleted file mode 100644 index 97adf3197..000000000 --- a/tests/unit/testdata/set-11.textproto +++ /dev/null @@ -1,12 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# The ServerTimestamp sentinel must be the value of a field. Firestore transforms -# don't support array indexing. - -description: "ServerTimestamp cannot be in an array value" -set: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{\"a\": [1, 2, \"ServerTimestamp\"]}" - is_error: true -> diff --git a/tests/unit/testdata/set-12.textproto b/tests/unit/testdata/set-12.textproto deleted file mode 100644 index e7709815b..000000000 --- a/tests/unit/testdata/set-12.textproto +++ /dev/null @@ -1,12 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# There cannot be an array value anywhere on the path from the document root to -# the ServerTimestamp sentinel. Firestore transforms don't support array indexing. - -description: "ServerTimestamp cannot be anywhere inside an array value" -set: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{\"a\": [1, {\"b\": \"ServerTimestamp\"}]}" - is_error: true -> diff --git a/tests/unit/testdata/set-13.textproto b/tests/unit/testdata/set-13.textproto deleted file mode 100644 index 5e71549f1..000000000 --- a/tests/unit/testdata/set-13.textproto +++ /dev/null @@ -1,13 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# The Delete sentinel must be the value of a field. Deletes are implemented by -# turning the path to the Delete sentinel into a FieldPath, and FieldPaths do not -# support array indexing. - -description: "Delete cannot be in an array value" -set: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{\"a\": [1, 2, \"Delete\"]}" - is_error: true -> diff --git a/tests/unit/testdata/set-14.textproto b/tests/unit/testdata/set-14.textproto deleted file mode 100644 index 754602520..000000000 --- a/tests/unit/testdata/set-14.textproto +++ /dev/null @@ -1,13 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# The Delete sentinel must be the value of a field. Deletes are implemented by -# turning the path to the Delete sentinel into a FieldPath, and FieldPaths do not -# support array indexing. - -description: "Delete cannot be anywhere inside an array value" -set: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{\"a\": [1, {\"b\": \"Delete\"}]}" - is_error: true -> diff --git a/tests/unit/testdata/set-15.textproto b/tests/unit/testdata/set-15.textproto deleted file mode 100644 index d13f33464..000000000 --- a/tests/unit/testdata/set-15.textproto +++ /dev/null @@ -1,37 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# The MergeAll option with a simple piece of data. - -description: "MergeAll" -set: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - option: < - all: true - > - json_data: "{\"a\": 1, \"b\": 2}" - request: < - database: "projects/projectID/databases/(default)" - writes: < - update: < - name: "projects/projectID/databases/(default)/documents/C/d" - fields: < - key: "a" - value: < - integer_value: 1 - > - > - fields: < - key: "b" - value: < - integer_value: 2 - > - > - > - update_mask: < - field_paths: "a" - field_paths: "b" - > - > - > -> diff --git a/tests/unit/testdata/set-16.textproto b/tests/unit/testdata/set-16.textproto deleted file mode 100644 index 1f44417e8..000000000 --- a/tests/unit/testdata/set-16.textproto +++ /dev/null @@ -1,45 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# MergeAll with nested fields results in an update mask that includes entries for -# all the leaf fields. - -description: "MergeAll with nested fields" -set: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - option: < - all: true - > - json_data: "{\"h\": { \"g\": 3, \"f\": 4 }}" - request: < - database: "projects/projectID/databases/(default)" - writes: < - update: < - name: "projects/projectID/databases/(default)/documents/C/d" - fields: < - key: "h" - value: < - map_value: < - fields: < - key: "f" - value: < - integer_value: 4 - > - > - fields: < - key: "g" - value: < - integer_value: 3 - > - > - > - > - > - > - update_mask: < - field_paths: "h.f" - field_paths: "h.g" - > - > - > -> diff --git a/tests/unit/testdata/set-17.textproto b/tests/unit/testdata/set-17.textproto deleted file mode 100644 index e68dba296..000000000 --- a/tests/unit/testdata/set-17.textproto +++ /dev/null @@ -1,32 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# Fields in the input data but not in a merge option are pruned. - -description: "Merge with a field" -set: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - option: < - fields: < - field: "a" - > - > - json_data: "{\"a\": 1, \"b\": 2}" - request: < - database: "projects/projectID/databases/(default)" - writes: < - update: < - name: "projects/projectID/databases/(default)/documents/C/d" - fields: < - key: "a" - value: < - integer_value: 1 - > - > - > - update_mask: < - field_paths: "a" - > - > - > -> diff --git a/tests/unit/testdata/set-18.textproto b/tests/unit/testdata/set-18.textproto deleted file mode 100644 index 17bf34488..000000000 --- a/tests/unit/testdata/set-18.textproto +++ /dev/null @@ -1,41 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# A merge option where the field is not at top level. Only fields mentioned in the -# option are present in the update operation. - -description: "Merge with a nested field" -set: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - option: < - fields: < - field: "h" - field: "g" - > - > - json_data: "{\"h\": {\"g\": 4, \"f\": 5}}" - request: < - database: "projects/projectID/databases/(default)" - writes: < - update: < - name: "projects/projectID/databases/(default)/documents/C/d" - fields: < - key: "h" - value: < - map_value: < - fields: < - key: "g" - value: < - integer_value: 4 - > - > - > - > - > - > - update_mask: < - field_paths: "h.g" - > - > - > -> diff --git a/tests/unit/testdata/set-19.textproto b/tests/unit/testdata/set-19.textproto deleted file mode 100644 index 34af3a136..000000000 --- a/tests/unit/testdata/set-19.textproto +++ /dev/null @@ -1,46 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# If a field path is in a merge option, the value at that path replaces the stored -# value. That is true even if the value is complex. - -description: "Merge field is not a leaf" -set: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - option: < - fields: < - field: "h" - > - > - json_data: "{\"h\": {\"g\": 5, \"f\": 6}, \"e\": 7}" - request: < - database: "projects/projectID/databases/(default)" - writes: < - update: < - name: "projects/projectID/databases/(default)/documents/C/d" - fields: < - key: "h" - value: < - map_value: < - fields: < - key: "f" - value: < - integer_value: 6 - > - > - fields: < - key: "g" - value: < - integer_value: 5 - > - > - > - > - > - > - update_mask: < - field_paths: "h" - > - > - > -> diff --git a/tests/unit/testdata/set-2.textproto b/tests/unit/testdata/set-2.textproto deleted file mode 100644 index 36b264633..000000000 --- a/tests/unit/testdata/set-2.textproto +++ /dev/null @@ -1,58 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# A call to a write method with complicated input data. - -description: "complex" -set: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{\"a\": [1, 2.5], \"b\": {\"c\": [\"three\", {\"d\": true}]}}" - request: < - database: "projects/projectID/databases/(default)" - writes: < - update: < - name: "projects/projectID/databases/(default)/documents/C/d" - fields: < - key: "a" - value: < - array_value: < - values: < - integer_value: 1 - > - values: < - double_value: 2.5 - > - > - > - > - fields: < - key: "b" - value: < - map_value: < - fields: < - key: "c" - value: < - array_value: < - values: < - string_value: "three" - > - values: < - map_value: < - fields: < - key: "d" - value: < - boolean_value: true - > - > - > - > - > - > - > - > - > - > - > - > - > -> diff --git a/tests/unit/testdata/set-20.textproto b/tests/unit/testdata/set-20.textproto deleted file mode 100644 index 2da9b2879..000000000 --- a/tests/unit/testdata/set-20.textproto +++ /dev/null @@ -1,40 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# A merge with fields that use special characters. - -description: "Merge with FieldPaths" -set: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - option: < - fields: < - field: "*" - field: "~" - > - > - json_data: "{\"*\": {\"~\": true}}" - request: < - database: "projects/projectID/databases/(default)" - writes: < - update: < - name: "projects/projectID/databases/(default)/documents/C/d" - fields: < - key: "*" - value: < - map_value: < - fields: < - key: "~" - value: < - boolean_value: true - > - > - > - > - > - > - update_mask: < - field_paths: "`*`.`~`" - > - > - > -> diff --git a/tests/unit/testdata/set-21.textproto b/tests/unit/testdata/set-21.textproto deleted file mode 100644 index cf4d9959c..000000000 --- a/tests/unit/testdata/set-21.textproto +++ /dev/null @@ -1,40 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# Just as when no merge option is specified, ServerTimestamp sentinel values are -# removed from the data in the update operation and become transforms. - -description: "ServerTimestamp with MergeAll" -set: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - option: < - all: true - > - json_data: "{\"a\": 1, \"b\": \"ServerTimestamp\"}" - request: < - database: "projects/projectID/databases/(default)" - writes: < - update: < - name: "projects/projectID/databases/(default)/documents/C/d" - fields: < - key: "a" - value: < - integer_value: 1 - > - > - > - update_mask: < - field_paths: "a" - > - > - writes: < - transform: < - document: "projects/projectID/databases/(default)/documents/C/d" - field_transforms: < - field_path: "b" - set_to_server_value: REQUEST_TIME - > - > - > - > -> diff --git a/tests/unit/testdata/set-22.textproto b/tests/unit/testdata/set-22.textproto deleted file mode 100644 index f6d609699..000000000 --- a/tests/unit/testdata/set-22.textproto +++ /dev/null @@ -1,45 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# Just as when no merge option is specified, ServerTimestamp sentinel values are -# removed from the data in the update operation and become transforms. - -description: "ServerTimestamp with Merge of both fields" -set: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - option: < - fields: < - field: "a" - > - fields: < - field: "b" - > - > - json_data: "{\"a\": 1, \"b\": \"ServerTimestamp\"}" - request: < - database: "projects/projectID/databases/(default)" - writes: < - update: < - name: "projects/projectID/databases/(default)/documents/C/d" - fields: < - key: "a" - value: < - integer_value: 1 - > - > - > - update_mask: < - field_paths: "a" - > - > - writes: < - transform: < - document: "projects/projectID/databases/(default)/documents/C/d" - field_transforms: < - field_path: "b" - set_to_server_value: REQUEST_TIME - > - > - > - > -> diff --git a/tests/unit/testdata/set-23.textproto b/tests/unit/testdata/set-23.textproto deleted file mode 100644 index 5b7c061ab..000000000 --- a/tests/unit/testdata/set-23.textproto +++ /dev/null @@ -1,33 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# If the ServerTimestamp value is not mentioned in a merge option, then it is -# pruned from the data but does not result in a transform. - -description: "If is ServerTimestamp not in Merge, no transform" -set: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - option: < - fields: < - field: "a" - > - > - json_data: "{\"a\": 1, \"b\": \"ServerTimestamp\"}" - request: < - database: "projects/projectID/databases/(default)" - writes: < - update: < - name: "projects/projectID/databases/(default)/documents/C/d" - fields: < - key: "a" - value: < - integer_value: 1 - > - > - > - update_mask: < - field_paths: "a" - > - > - > -> diff --git a/tests/unit/testdata/set-24.textproto b/tests/unit/testdata/set-24.textproto deleted file mode 100644 index 7827dde1a..000000000 --- a/tests/unit/testdata/set-24.textproto +++ /dev/null @@ -1,28 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# If all the fields in the merge option have ServerTimestamp values, then no -# update operation is produced, only a transform. - -description: "If no ordinary values in Merge, no write" -set: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - option: < - fields: < - field: "b" - > - > - json_data: "{\"a\": 1, \"b\": \"ServerTimestamp\"}" - request: < - database: "projects/projectID/databases/(default)" - writes: < - transform: < - document: "projects/projectID/databases/(default)/documents/C/d" - field_transforms: < - field_path: "b" - set_to_server_value: REQUEST_TIME - > - > - > - > -> diff --git a/tests/unit/testdata/set-25.textproto b/tests/unit/testdata/set-25.textproto deleted file mode 100644 index 0696c176e..000000000 --- a/tests/unit/testdata/set-25.textproto +++ /dev/null @@ -1,20 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# The client signals an error if a merge option mentions a path that is not in the -# input data. - -description: "Merge fields must all be present in data" -set: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - option: < - fields: < - field: "b" - > - fields: < - field: "a" - > - > - json_data: "{\"a\": 1}" - is_error: true -> diff --git a/tests/unit/testdata/set-26.textproto b/tests/unit/testdata/set-26.textproto deleted file mode 100644 index b2a720bb2..000000000 --- a/tests/unit/testdata/set-26.textproto +++ /dev/null @@ -1,17 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# The client signals an error if the Delete sentinel is in the input data, but not -# selected by a merge option, because this is most likely a programming bug. - -description: "Delete cannot appear in an unmerged field" -set: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - option: < - fields: < - field: "a" - > - > - json_data: "{\"a\": 1, \"b\": \"Delete\"}" - is_error: true -> diff --git a/tests/unit/testdata/set-3.textproto b/tests/unit/testdata/set-3.textproto deleted file mode 100644 index 992683f6a..000000000 --- a/tests/unit/testdata/set-3.textproto +++ /dev/null @@ -1,11 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# Empty fields are not allowed. - -description: "empty field" -set: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{\"\": 1}" - is_error: true -> diff --git a/tests/unit/testdata/set-4.textproto b/tests/unit/testdata/set-4.textproto deleted file mode 100644 index f2915771b..000000000 --- a/tests/unit/testdata/set-4.textproto +++ /dev/null @@ -1,37 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# Create and Set treat their map keys literally. They do not split on dots. - -description: "don\342\200\231t split on dots" -set: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{ \"a.b\": { \"c.d\": 1 }, \"e\": 2 }" - request: < - database: "projects/projectID/databases/(default)" - writes: < - update: < - name: "projects/projectID/databases/(default)/documents/C/d" - fields: < - key: "a.b" - value: < - map_value: < - fields: < - key: "c.d" - value: < - integer_value: 1 - > - > - > - > - > - fields: < - key: "e" - value: < - integer_value: 2 - > - > - > - > - > -> diff --git a/tests/unit/testdata/set-5.textproto b/tests/unit/testdata/set-5.textproto deleted file mode 100644 index c465121fe..000000000 --- a/tests/unit/testdata/set-5.textproto +++ /dev/null @@ -1,38 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# Create and Set treat their map keys literally. They do not escape special -# characters. - -description: "non-alpha characters in map keys" -set: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{ \"*\": { \".\": 1 }, \"~\": 2 }" - request: < - database: "projects/projectID/databases/(default)" - writes: < - update: < - name: "projects/projectID/databases/(default)/documents/C/d" - fields: < - key: "*" - value: < - map_value: < - fields: < - key: "." - value: < - integer_value: 1 - > - > - > - > - > - fields: < - key: "~" - value: < - integer_value: 2 - > - > - > - > - > -> diff --git a/tests/unit/testdata/set-6.textproto b/tests/unit/testdata/set-6.textproto deleted file mode 100644 index 6ef0a7061..000000000 --- a/tests/unit/testdata/set-6.textproto +++ /dev/null @@ -1,11 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# The Delete sentinel cannot be used in Create, or in Set without a Merge option. - -description: "Delete cannot appear in data" -set: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{\"a\": 1, \"b\": \"Delete\"}" - is_error: true -> diff --git a/tests/unit/testdata/set-7.textproto b/tests/unit/testdata/set-7.textproto deleted file mode 100644 index de4a08700..000000000 --- a/tests/unit/testdata/set-7.textproto +++ /dev/null @@ -1,36 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# A key with the special ServerTimestamp sentinel is removed from the data in the -# update operation. Instead it appears in a separate Transform operation. Note -# that in these tests, the string "ServerTimestamp" should be replaced with the -# special ServerTimestamp value. - -description: "ServerTimestamp with data" -set: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{\"a\": 1, \"b\": \"ServerTimestamp\"}" - request: < - database: "projects/projectID/databases/(default)" - writes: < - update: < - name: "projects/projectID/databases/(default)/documents/C/d" - fields: < - key: "a" - value: < - integer_value: 1 - > - > - > - > - writes: < - transform: < - document: "projects/projectID/databases/(default)/documents/C/d" - field_transforms: < - field_path: "b" - set_to_server_value: REQUEST_TIME - > - > - > - > -> diff --git a/tests/unit/testdata/set-8.textproto b/tests/unit/testdata/set-8.textproto deleted file mode 100644 index 48e0c6a09..000000000 --- a/tests/unit/testdata/set-8.textproto +++ /dev/null @@ -1,23 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# If the only values in the input are ServerTimestamps, then no update operation -# should be produced unless there are preconditions. - -description: "ServerTimestamp alone" -set: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{\"a\": \"ServerTimestamp\"}" - request: < - database: "projects/projectID/databases/(default)" - writes: < - transform: < - document: "projects/projectID/databases/(default)/documents/C/d" - field_transforms: < - field_path: "a" - set_to_server_value: REQUEST_TIME - > - > - > - > -> diff --git a/tests/unit/testdata/set-9.textproto b/tests/unit/testdata/set-9.textproto deleted file mode 100644 index db0f09871..000000000 --- a/tests/unit/testdata/set-9.textproto +++ /dev/null @@ -1,35 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# A ServerTimestamp value can occur at any depth. In this case, the transform -# applies to the field path "b.c". Since "c" is removed from the update, "b" -# becomes empty, so it is also removed from the update. - -description: "nested ServerTimestamp field" -set: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{\"a\": 1, \"b\": {\"c\": \"ServerTimestamp\"}}" - request: < - database: "projects/projectID/databases/(default)" - writes: < - update: < - name: "projects/projectID/databases/(default)/documents/C/d" - fields: < - key: "a" - value: < - integer_value: 1 - > - > - > - > - writes: < - transform: < - document: "projects/projectID/databases/(default)/documents/C/d" - field_transforms: < - field_path: "b.c" - set_to_server_value: REQUEST_TIME - > - > - > - > -> diff --git a/tests/unit/testdata/test-suite.binproto b/tests/unit/testdata/test-suite.binproto index 0619a2fc1..6e3ce3973 100644 Binary files a/tests/unit/testdata/test-suite.binproto and b/tests/unit/testdata/test-suite.binproto differ diff --git a/tests/unit/testdata/tests.binprotos b/tests/unit/testdata/tests.binprotos deleted file mode 100644 index 0d1fea6ec..000000000 Binary files a/tests/unit/testdata/tests.binprotos and /dev/null differ diff --git a/tests/unit/testdata/update-1.textproto b/tests/unit/testdata/update-1.textproto deleted file mode 100644 index 57f5fdef8..000000000 --- a/tests/unit/testdata/update-1.textproto +++ /dev/null @@ -1,30 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# A simple call, resulting in a single update operation. - -description: "basic" -update: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{\"a\": 1}" - request: < - database: "projects/projectID/databases/(default)" - writes: < - update: < - name: "projects/projectID/databases/(default)/documents/C/d" - fields: < - key: "a" - value: < - integer_value: 1 - > - > - > - update_mask: < - field_paths: "a" - > - current_document: < - exists: true - > - > - > -> diff --git a/tests/unit/testdata/update-10.textproto b/tests/unit/testdata/update-10.textproto deleted file mode 100644 index 8e55522d1..000000000 --- a/tests/unit/testdata/update-10.textproto +++ /dev/null @@ -1,11 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# In the input data, one field cannot be a prefix of another. - -description: "prefix #2" -update: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{\"a\": 1, \"a.b\": 2}" - is_error: true -> diff --git a/tests/unit/testdata/update-11.textproto b/tests/unit/testdata/update-11.textproto deleted file mode 100644 index 3867f7e73..000000000 --- a/tests/unit/testdata/update-11.textproto +++ /dev/null @@ -1,11 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# The Delete sentinel must be the value of a top-level key. - -description: "Delete cannot be nested" -update: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{\"a\": {\"b\": \"Delete\"}}" - is_error: true -> diff --git a/tests/unit/testdata/update-12.textproto.failed b/tests/unit/testdata/update-12.textproto.failed deleted file mode 100644 index 6ee322ccc..000000000 --- a/tests/unit/testdata/update-12.textproto.failed +++ /dev/null @@ -1,14 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# The Update method does not support an explicit exists precondition. - -description: "Exists precondition is invalid" -update: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - precondition: < - exists: true - > - json_data: "{\"a\": 1}" - is_error: true -> diff --git a/tests/unit/testdata/update-13.textproto b/tests/unit/testdata/update-13.textproto deleted file mode 100644 index 51b13f067..000000000 --- a/tests/unit/testdata/update-13.textproto +++ /dev/null @@ -1,42 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# A key with the special ServerTimestamp sentinel is removed from the data in the -# update operation. Instead it appears in a separate Transform operation. Note -# that in these tests, the string "ServerTimestamp" should be replaced with the -# special ServerTimestamp value. - -description: "ServerTimestamp with data" -update: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{\"a\": 1, \"b\": \"ServerTimestamp\"}" - request: < - database: "projects/projectID/databases/(default)" - writes: < - update: < - name: "projects/projectID/databases/(default)/documents/C/d" - fields: < - key: "a" - value: < - integer_value: 1 - > - > - > - update_mask: < - field_paths: "a" - > - current_document: < - exists: true - > - > - writes: < - transform: < - document: "projects/projectID/databases/(default)/documents/C/d" - field_transforms: < - field_path: "b" - set_to_server_value: REQUEST_TIME - > - > - > - > -> diff --git a/tests/unit/testdata/update-14.textproto.failed b/tests/unit/testdata/update-14.textproto.failed deleted file mode 100644 index d767ddeb3..000000000 --- a/tests/unit/testdata/update-14.textproto.failed +++ /dev/null @@ -1,26 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# If the only values in the input are ServerTimestamps, then no update operation -# should be produced unless there are preconditions. - -description: "ServerTimestamp alone" -update: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{\"a\": \"ServerTimestamp\"}" - request: < - database: "projects/projectID/databases/(default)" - writes: < - transform: < - document: "projects/projectID/databases/(default)/documents/C/d" - field_transforms: < - field_path: "a" - set_to_server_value: REQUEST_TIME - > - > - current_document: < - exists: true - > - > - > -> diff --git a/tests/unit/testdata/update-15.textproto.failed b/tests/unit/testdata/update-15.textproto.failed deleted file mode 100644 index 918f82328..000000000 --- a/tests/unit/testdata/update-15.textproto.failed +++ /dev/null @@ -1,42 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# A ServerTimestamp value can occur at any depth. In this case, the transform -# applies to the field path "b.c". Since "c" is removed from the update, "b" -# becomes empty, so it is also removed from the update. - -description: "nested ServerTimestamp field" -update: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{\"a\": 1, \"b\": {\"c\": \"ServerTimestamp\"}}" - request: < - database: "projects/projectID/databases/(default)" - writes: < - update: < - name: "projects/projectID/databases/(default)/documents/C/d" - fields: < - key: "a" - value: < - integer_value: 1 - > - > - > - update_mask: < - field_paths: "a" - field_paths: "b" - > - current_document: < - exists: true - > - > - writes: < - transform: < - document: "projects/projectID/databases/(default)/documents/C/d" - field_transforms: < - field_path: "b.c" - set_to_server_value: REQUEST_TIME - > - > - > - > -> diff --git a/tests/unit/testdata/update-16.textproto.failed b/tests/unit/testdata/update-16.textproto.failed deleted file mode 100644 index 88ab47961..000000000 --- a/tests/unit/testdata/update-16.textproto.failed +++ /dev/null @@ -1,49 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# A document can have more than one ServerTimestamp field. Since all the -# ServerTimestamp fields are removed, the only field in the update is "a". - -# b is not in the mask because it will be set in the transform. c must be in the -# mask: it should be replaced entirely. The transform will set c.d to the -# timestamp, but the update will delete the rest of c. - -description: "multiple ServerTimestamp fields" -update: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{\"a\": 1, \"b\": \"ServerTimestamp\", \"c\": {\"d\": \"ServerTimestamp\"}}" - request: < - database: "projects/projectID/databases/(default)" - writes: < - update: < - name: "projects/projectID/databases/(default)/documents/C/d" - fields: < - key: "a" - value: < - integer_value: 1 - > - > - > - update_mask: < - field_paths: "a" - field_paths: "c" - > - current_document: < - exists: true - > - > - writes: < - transform: < - document: "projects/projectID/databases/(default)/documents/C/d" - field_transforms: < - field_path: "b" - set_to_server_value: REQUEST_TIME - > - field_transforms: < - field_path: "c.d" - set_to_server_value: REQUEST_TIME - > - > - > - > -> diff --git a/tests/unit/testdata/update-17.textproto b/tests/unit/testdata/update-17.textproto deleted file mode 100644 index cd7b87ebe..000000000 --- a/tests/unit/testdata/update-17.textproto +++ /dev/null @@ -1,12 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# The ServerTimestamp sentinel must be the value of a field. Firestore transforms -# don't support array indexing. - -description: "ServerTimestamp cannot be in an array value" -update: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{\"a\": [1, 2, \"ServerTimestamp\"]}" - is_error: true -> diff --git a/tests/unit/testdata/update-18.textproto b/tests/unit/testdata/update-18.textproto deleted file mode 100644 index e2b0d4320..000000000 --- a/tests/unit/testdata/update-18.textproto +++ /dev/null @@ -1,12 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# There cannot be an array value anywhere on the path from the document root to -# the ServerTimestamp sentinel. Firestore transforms don't support array indexing. - -description: "ServerTimestamp cannot be anywhere inside an array value" -update: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{\"a\": [1, {\"b\": \"ServerTimestamp\"}]}" - is_error: true -> diff --git a/tests/unit/testdata/update-19.textproto b/tests/unit/testdata/update-19.textproto deleted file mode 100644 index eee3961d9..000000000 --- a/tests/unit/testdata/update-19.textproto +++ /dev/null @@ -1,13 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# The Delete sentinel must be the value of a field. Deletes are implemented by -# turning the path to the Delete sentinel into a FieldPath, and FieldPaths do not -# support array indexing. - -description: "Delete cannot be in an array value" -update: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{\"a\": [1, 2, \"Delete\"]}" - is_error: true -> diff --git a/tests/unit/testdata/update-2.textproto b/tests/unit/testdata/update-2.textproto deleted file mode 100644 index 6a3795cd8..000000000 --- a/tests/unit/testdata/update-2.textproto +++ /dev/null @@ -1,65 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# A call to a write method with complicated input data. - -description: "complex" -update: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{\"a\": [1, 2.5], \"b\": {\"c\": [\"three\", {\"d\": true}]}}" - request: < - database: "projects/projectID/databases/(default)" - writes: < - update: < - name: "projects/projectID/databases/(default)/documents/C/d" - fields: < - key: "a" - value: < - array_value: < - values: < - integer_value: 1 - > - values: < - double_value: 2.5 - > - > - > - > - fields: < - key: "b" - value: < - map_value: < - fields: < - key: "c" - value: < - array_value: < - values: < - string_value: "three" - > - values: < - map_value: < - fields: < - key: "d" - value: < - boolean_value: true - > - > - > - > - > - > - > - > - > - > - > - update_mask: < - field_paths: "a" - field_paths: "b" - > - current_document: < - exists: true - > - > - > -> diff --git a/tests/unit/testdata/update-20.textproto b/tests/unit/testdata/update-20.textproto deleted file mode 100644 index b62646972..000000000 --- a/tests/unit/testdata/update-20.textproto +++ /dev/null @@ -1,13 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# The Delete sentinel must be the value of a field. Deletes are implemented by -# turning the path to the Delete sentinel into a FieldPath, and FieldPaths do not -# support array indexing. - -description: "Delete cannot be anywhere inside an array value" -update: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{\"a\": [1, {\"b\": \"Delete\"}]}" - is_error: true -> diff --git a/tests/unit/testdata/update-21.textproto b/tests/unit/testdata/update-21.textproto deleted file mode 100644 index 356c91b44..000000000 --- a/tests/unit/testdata/update-21.textproto +++ /dev/null @@ -1,44 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# The Update method splits top-level keys at dots. - -description: "split on dots" -update: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{\"a.b.c\": 1}" - request: < - database: "projects/projectID/databases/(default)" - writes: < - update: < - name: "projects/projectID/databases/(default)/documents/C/d" - fields: < - key: "a" - value: < - map_value: < - fields: < - key: "b" - value: < - map_value: < - fields: < - key: "c" - value: < - integer_value: 1 - > - > - > - > - > - > - > - > - > - update_mask: < - field_paths: "a.b.c" - > - current_document: < - exists: true - > - > - > -> diff --git a/tests/unit/testdata/update-22.textproto b/tests/unit/testdata/update-22.textproto deleted file mode 100644 index 9f11612eb..000000000 --- a/tests/unit/testdata/update-22.textproto +++ /dev/null @@ -1,45 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# The Update method splits only top-level keys at dots. Keys at other levels are -# taken literally. - -description: "Split on dots for top-level keys only" -update: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{\"h.g\": {\"j.k\": 6}}" - request: < - database: "projects/projectID/databases/(default)" - writes: < - update: < - name: "projects/projectID/databases/(default)/documents/C/d" - fields: < - key: "h" - value: < - map_value: < - fields: < - key: "g" - value: < - map_value: < - fields: < - key: "j.k" - value: < - integer_value: 6 - > - > - > - > - > - > - > - > - > - update_mask: < - field_paths: "h.g" - > - current_document: < - exists: true - > - > - > -> diff --git a/tests/unit/testdata/update-23.textproto b/tests/unit/testdata/update-23.textproto deleted file mode 100644 index 52a112268..000000000 --- a/tests/unit/testdata/update-23.textproto +++ /dev/null @@ -1,46 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# After expanding top-level dotted fields, fields with Delete values are pruned -# from the output data, but appear in the update mask. - -description: "Delete with a dotted field" -update: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{\"a\": 1, \"b.c\": \"Delete\", \"b.d\": 2}" - request: < - database: "projects/projectID/databases/(default)" - writes: < - update: < - name: "projects/projectID/databases/(default)/documents/C/d" - fields: < - key: "a" - value: < - integer_value: 1 - > - > - fields: < - key: "b" - value: < - map_value: < - fields: < - key: "d" - value: < - integer_value: 2 - > - > - > - > - > - > - update_mask: < - field_paths: "a" - field_paths: "b.c" - field_paths: "b.d" - > - current_document: < - exists: true - > - > - > -> diff --git a/tests/unit/testdata/update-24.textproto.failed b/tests/unit/testdata/update-24.textproto.failed deleted file mode 100644 index 387921a13..000000000 --- a/tests/unit/testdata/update-24.textproto.failed +++ /dev/null @@ -1,27 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# Like other uses of ServerTimestamp, the data is pruned and the field does not -# appear in the update mask, because it is in the transform. In this case An -# update operation is produced just to hold the precondition. - -description: "ServerTimestamp with dotted field" -update: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{\"a.b.c\": \"ServerTimestamp\"}" - request: < - database: "projects/projectID/databases/(default)" - writes: < - transform: < - document: "projects/projectID/databases/(default)/documents/C/d" - field_transforms: < - field_path: "a.b.c" - set_to_server_value: REQUEST_TIME - > - > - current_document: < - exists: true - > - > - > -> diff --git a/tests/unit/testdata/update-25.textproto.failed b/tests/unit/testdata/update-25.textproto.failed deleted file mode 100644 index e00db3a29..000000000 --- a/tests/unit/testdata/update-25.textproto.failed +++ /dev/null @@ -1,12 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# The keys of the data given to Update are interpreted, unlike those of Create and -# Set. They cannot contain special characters. - -description: "invalid character" -update: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{\"a~b\": 1}" - is_error: true -> diff --git a/tests/unit/testdata/update-4.textproto b/tests/unit/testdata/update-4.textproto deleted file mode 100644 index fdeb15e02..000000000 --- a/tests/unit/testdata/update-4.textproto +++ /dev/null @@ -1,32 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# If a field's value is the Delete sentinel, then it doesn't appear in the update -# data, but does in the mask. - -description: "Delete" -update: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{\"a\": 1, \"b\": \"Delete\"}" - request: < - database: "projects/projectID/databases/(default)" - writes: < - update: < - name: "projects/projectID/databases/(default)/documents/C/d" - fields: < - key: "a" - value: < - integer_value: 1 - > - > - > - update_mask: < - field_paths: "a" - field_paths: "b" - > - current_document: < - exists: true - > - > - > -> diff --git a/tests/unit/testdata/update-5.textproto b/tests/unit/testdata/update-5.textproto deleted file mode 100644 index 2f920e19b..000000000 --- a/tests/unit/testdata/update-5.textproto +++ /dev/null @@ -1,25 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# If the input data consists solely of Deletes, then the update operation has no -# map, just an update mask. - -description: "Delete alone" -update: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{\"a\": \"Delete\"}" - request: < - database: "projects/projectID/databases/(default)" - writes: < - update: < - name: "projects/projectID/databases/(default)/documents/C/d" - > - update_mask: < - field_paths: "a" - > - current_document: < - exists: true - > - > - > -> diff --git a/tests/unit/testdata/update-6.textproto b/tests/unit/testdata/update-6.textproto deleted file mode 100644 index 2a214bc50..000000000 --- a/tests/unit/testdata/update-6.textproto +++ /dev/null @@ -1,37 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# The Update call supports a last-update-time precondition. - -description: "last-update-time precondition" -update: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - precondition: < - update_time: < - seconds: 42 - > - > - json_data: "{\"a\": 1}" - request: < - database: "projects/projectID/databases/(default)" - writes: < - update: < - name: "projects/projectID/databases/(default)/documents/C/d" - fields: < - key: "a" - value: < - integer_value: 1 - > - > - > - update_mask: < - field_paths: "a" - > - current_document: < - update_time: < - seconds: 42 - > - > - > - > -> diff --git a/tests/unit/testdata/update-7.textproto.failed b/tests/unit/testdata/update-7.textproto.failed deleted file mode 100644 index 036fe0273..000000000 --- a/tests/unit/testdata/update-7.textproto.failed +++ /dev/null @@ -1,11 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# It is a client-side error to call Update with empty data. - -description: "no paths" -update: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{}" - is_error: true -> diff --git a/tests/unit/testdata/update-8.textproto.failed b/tests/unit/testdata/update-8.textproto.failed deleted file mode 100644 index f056c6c25..000000000 --- a/tests/unit/testdata/update-8.textproto.failed +++ /dev/null @@ -1,11 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# Empty fields are not allowed. - -description: "empty field path component" -update: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{\"a..b\": 1}" - is_error: true -> diff --git a/tests/unit/testdata/update-9.textproto b/tests/unit/testdata/update-9.textproto deleted file mode 100644 index c60f402be..000000000 --- a/tests/unit/testdata/update-9.textproto +++ /dev/null @@ -1,11 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# In the input data, one field cannot be a prefix of another. - -description: "prefix #1" -update: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - json_data: "{\"a.b\": 1, \"a\": 2}" - is_error: true -> diff --git a/tests/unit/testdata/update-paths-1.textproto b/tests/unit/testdata/update-paths-1.textproto deleted file mode 100644 index 4cb1970f7..000000000 --- a/tests/unit/testdata/update-paths-1.textproto +++ /dev/null @@ -1,33 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# A simple call, resulting in a single update operation. - -description: "basic" -update_paths: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - field_paths: < - field: "a" - > - json_values: "1" - request: < - database: "projects/projectID/databases/(default)" - writes: < - update: < - name: "projects/projectID/databases/(default)/documents/C/d" - fields: < - key: "a" - value: < - integer_value: 1 - > - > - > - update_mask: < - field_paths: "a" - > - current_document: < - exists: true - > - > - > -> diff --git a/tests/unit/testdata/update-paths-10.textproto b/tests/unit/testdata/update-paths-10.textproto deleted file mode 100644 index 99923d9c2..000000000 --- a/tests/unit/testdata/update-paths-10.textproto +++ /dev/null @@ -1,19 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# In the input data, one field cannot be a prefix of another. - -description: "prefix #2" -update_paths: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - field_paths: < - field: "a" - > - field_paths: < - field: "a" - field: "b" - > - json_values: "1" - json_values: "2" - is_error: true -> diff --git a/tests/unit/testdata/update-paths-11.textproto b/tests/unit/testdata/update-paths-11.textproto deleted file mode 100644 index 1c1fab654..000000000 --- a/tests/unit/testdata/update-paths-11.textproto +++ /dev/null @@ -1,14 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# The Delete sentinel must be the value of a top-level key. - -description: "Delete cannot be nested" -update_paths: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - field_paths: < - field: "a" - > - json_values: "{\"b\": \"Delete\"}" - is_error: true -> diff --git a/tests/unit/testdata/update-paths-12.textproto b/tests/unit/testdata/update-paths-12.textproto deleted file mode 100644 index aa24f0f94..000000000 --- a/tests/unit/testdata/update-paths-12.textproto +++ /dev/null @@ -1,17 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# The Update method does not support an explicit exists precondition. - -description: "Exists precondition is invalid" -update_paths: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - precondition: < - exists: true - > - field_paths: < - field: "a" - > - json_values: "1" - is_error: true -> diff --git a/tests/unit/testdata/update-paths-13.textproto b/tests/unit/testdata/update-paths-13.textproto deleted file mode 100644 index 6d594d046..000000000 --- a/tests/unit/testdata/update-paths-13.textproto +++ /dev/null @@ -1,49 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# A key with the special ServerTimestamp sentinel is removed from the data in the -# update operation. Instead it appears in a separate Transform operation. Note -# that in these tests, the string "ServerTimestamp" should be replaced with the -# special ServerTimestamp value. - -description: "ServerTimestamp with data" -update_paths: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - field_paths: < - field: "a" - > - field_paths: < - field: "b" - > - json_values: "1" - json_values: "\"ServerTimestamp\"" - request: < - database: "projects/projectID/databases/(default)" - writes: < - update: < - name: "projects/projectID/databases/(default)/documents/C/d" - fields: < - key: "a" - value: < - integer_value: 1 - > - > - > - update_mask: < - field_paths: "a" - > - current_document: < - exists: true - > - > - writes: < - transform: < - document: "projects/projectID/databases/(default)/documents/C/d" - field_transforms: < - field_path: "b" - set_to_server_value: REQUEST_TIME - > - > - > - > -> diff --git a/tests/unit/testdata/update-paths-14.textproto b/tests/unit/testdata/update-paths-14.textproto deleted file mode 100644 index 8f987336a..000000000 --- a/tests/unit/testdata/update-paths-14.textproto +++ /dev/null @@ -1,29 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# If the only values in the input are ServerTimestamps, then no update operation -# should be produced unless there are preconditions. - -description: "ServerTimestamp alone" -update_paths: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - field_paths: < - field: "a" - > - json_values: "\"ServerTimestamp\"" - request: < - database: "projects/projectID/databases/(default)" - writes: < - transform: < - document: "projects/projectID/databases/(default)/documents/C/d" - field_transforms: < - field_path: "a" - set_to_server_value: REQUEST_TIME - > - > - current_document: < - exists: true - > - > - > -> diff --git a/tests/unit/testdata/update-paths-15.textproto b/tests/unit/testdata/update-paths-15.textproto deleted file mode 100644 index ec9f4bcf5..000000000 --- a/tests/unit/testdata/update-paths-15.textproto +++ /dev/null @@ -1,49 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# A ServerTimestamp value can occur at any depth. In this case, the transform -# applies to the field path "b.c". Since "c" is removed from the update, "b" -# becomes empty, so it is also removed from the update. - -description: "nested ServerTimestamp field" -update_paths: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - field_paths: < - field: "a" - > - field_paths: < - field: "b" - > - json_values: "1" - json_values: "{\"c\": \"ServerTimestamp\"}" - request: < - database: "projects/projectID/databases/(default)" - writes: < - update: < - name: "projects/projectID/databases/(default)/documents/C/d" - fields: < - key: "a" - value: < - integer_value: 1 - > - > - > - update_mask: < - field_paths: "a" - field_paths: "b" - > - current_document: < - exists: true - > - > - writes: < - transform: < - document: "projects/projectID/databases/(default)/documents/C/d" - field_transforms: < - field_path: "b.c" - set_to_server_value: REQUEST_TIME - > - > - > - > -> diff --git a/tests/unit/testdata/update-paths-16.textproto b/tests/unit/testdata/update-paths-16.textproto deleted file mode 100644 index 435c48908..000000000 --- a/tests/unit/testdata/update-paths-16.textproto +++ /dev/null @@ -1,56 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# A document can have more than one ServerTimestamp field. Since all the -# ServerTimestamp fields are removed, the only field in the update is "a". - -description: "multiple ServerTimestamp fields" -update_paths: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - field_paths: < - field: "a" - > - field_paths: < - field: "b" - > - field_paths: < - field: "c" - > - json_values: "1" - json_values: "\"ServerTimestamp\"" - json_values: "{\"d\": \"ServerTimestamp\"}" - request: < - database: "projects/projectID/databases/(default)" - writes: < - update: < - name: "projects/projectID/databases/(default)/documents/C/d" - fields: < - key: "a" - value: < - integer_value: 1 - > - > - > - update_mask: < - field_paths: "a" - field_paths: "c" - > - current_document: < - exists: true - > - > - writes: < - transform: < - document: "projects/projectID/databases/(default)/documents/C/d" - field_transforms: < - field_path: "b" - set_to_server_value: REQUEST_TIME - > - field_transforms: < - field_path: "c.d" - set_to_server_value: REQUEST_TIME - > - > - > - > -> diff --git a/tests/unit/testdata/update-paths-17.textproto b/tests/unit/testdata/update-paths-17.textproto deleted file mode 100644 index aca10feb0..000000000 --- a/tests/unit/testdata/update-paths-17.textproto +++ /dev/null @@ -1,15 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# The ServerTimestamp sentinel must be the value of a field. Firestore transforms -# don't support array indexing. - -description: "ServerTimestamp cannot be in an array value" -update_paths: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - field_paths: < - field: "a" - > - json_values: "[1, 2, \"ServerTimestamp\"]" - is_error: true -> diff --git a/tests/unit/testdata/update-paths-18.textproto b/tests/unit/testdata/update-paths-18.textproto deleted file mode 100644 index e6c2139fa..000000000 --- a/tests/unit/testdata/update-paths-18.textproto +++ /dev/null @@ -1,15 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# There cannot be an array value anywhere on the path from the document root to -# the ServerTimestamp sentinel. Firestore transforms don't support array indexing. - -description: "ServerTimestamp cannot be anywhere inside an array value" -update_paths: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - field_paths: < - field: "a" - > - json_values: "[1, {\"b\": \"ServerTimestamp\"}]" - is_error: true -> diff --git a/tests/unit/testdata/update-paths-19.textproto b/tests/unit/testdata/update-paths-19.textproto deleted file mode 100644 index 356d79d0a..000000000 --- a/tests/unit/testdata/update-paths-19.textproto +++ /dev/null @@ -1,16 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# The Delete sentinel must be the value of a field. Deletes are implemented by -# turning the path to the Delete sentinel into a FieldPath, and FieldPaths do not -# support array indexing. - -description: "Delete cannot be in an array value" -update_paths: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - field_paths: < - field: "a" - > - json_values: "[1, 2, \"Delete\"]" - is_error: true -> diff --git a/tests/unit/testdata/update-paths-2.textproto b/tests/unit/testdata/update-paths-2.textproto deleted file mode 100644 index c8d964a66..000000000 --- a/tests/unit/testdata/update-paths-2.textproto +++ /dev/null @@ -1,72 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# A call to a write method with complicated input data. - -description: "complex" -update_paths: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - field_paths: < - field: "a" - > - field_paths: < - field: "b" - > - json_values: "[1, 2.5]" - json_values: "{\"c\": [\"three\", {\"d\": true}]}" - request: < - database: "projects/projectID/databases/(default)" - writes: < - update: < - name: "projects/projectID/databases/(default)/documents/C/d" - fields: < - key: "a" - value: < - array_value: < - values: < - integer_value: 1 - > - values: < - double_value: 2.5 - > - > - > - > - fields: < - key: "b" - value: < - map_value: < - fields: < - key: "c" - value: < - array_value: < - values: < - string_value: "three" - > - values: < - map_value: < - fields: < - key: "d" - value: < - boolean_value: true - > - > - > - > - > - > - > - > - > - > - > - update_mask: < - field_paths: "a" - field_paths: "b" - > - current_document: < - exists: true - > - > - > -> diff --git a/tests/unit/testdata/update-paths-20.textproto b/tests/unit/testdata/update-paths-20.textproto deleted file mode 100644 index c0373ba2b..000000000 --- a/tests/unit/testdata/update-paths-20.textproto +++ /dev/null @@ -1,16 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# The Delete sentinel must be the value of a field. Deletes are implemented by -# turning the path to the Delete sentinel into a FieldPath, and FieldPaths do not -# support array indexing. - -description: "Delete cannot be anywhere inside an array value" -update_paths: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - field_paths: < - field: "a" - > - json_values: "[1, {\"b\": \"Delete\"}]" - is_error: true -> diff --git a/tests/unit/testdata/update-paths-21.textproto b/tests/unit/testdata/update-paths-21.textproto deleted file mode 100644 index df3d52c72..000000000 --- a/tests/unit/testdata/update-paths-21.textproto +++ /dev/null @@ -1,42 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# The UpdatePaths or equivalent method takes a list of FieldPaths. Each FieldPath -# is a sequence of uninterpreted path components. - -description: "multiple-element field path" -update_paths: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - field_paths: < - field: "a" - field: "b" - > - json_values: "1" - request: < - database: "projects/projectID/databases/(default)" - writes: < - update: < - name: "projects/projectID/databases/(default)/documents/C/d" - fields: < - key: "a" - value: < - map_value: < - fields: < - key: "b" - value: < - integer_value: 1 - > - > - > - > - > - > - update_mask: < - field_paths: "a.b" - > - current_document: < - exists: true - > - > - > -> diff --git a/tests/unit/testdata/update-paths-22.textproto b/tests/unit/testdata/update-paths-22.textproto deleted file mode 100644 index 28788eb7f..000000000 --- a/tests/unit/testdata/update-paths-22.textproto +++ /dev/null @@ -1,48 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# FieldPath components are not split on dots. - -description: "FieldPath elements are not split on dots" -update_paths: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - field_paths: < - field: "a.b" - field: "f.g" - > - json_values: "{\"n.o\": 7}" - request: < - database: "projects/projectID/databases/(default)" - writes: < - update: < - name: "projects/projectID/databases/(default)/documents/C/d" - fields: < - key: "a.b" - value: < - map_value: < - fields: < - key: "f.g" - value: < - map_value: < - fields: < - key: "n.o" - value: < - integer_value: 7 - > - > - > - > - > - > - > - > - > - update_mask: < - field_paths: "`a.b`.`f.g`" - > - current_document: < - exists: true - > - > - > -> diff --git a/tests/unit/testdata/update-paths-23.textproto b/tests/unit/testdata/update-paths-23.textproto deleted file mode 100644 index d5cc5c606..000000000 --- a/tests/unit/testdata/update-paths-23.textproto +++ /dev/null @@ -1,53 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# FieldPaths can contain special characters. - -description: "special characters" -update_paths: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - field_paths: < - field: "*" - field: "~" - > - field_paths: < - field: "*" - field: "`" - > - json_values: "1" - json_values: "2" - request: < - database: "projects/projectID/databases/(default)" - writes: < - update: < - name: "projects/projectID/databases/(default)/documents/C/d" - fields: < - key: "*" - value: < - map_value: < - fields: < - key: "`" - value: < - integer_value: 2 - > - > - fields: < - key: "~" - value: < - integer_value: 1 - > - > - > - > - > - > - update_mask: < - field_paths: "`*`.`\\``" - field_paths: "`*`.`~`" - > - current_document: < - exists: true - > - > - > -> diff --git a/tests/unit/testdata/update-paths-24.textproto b/tests/unit/testdata/update-paths-24.textproto deleted file mode 100644 index 069cf49a9..000000000 --- a/tests/unit/testdata/update-paths-24.textproto +++ /dev/null @@ -1,13 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# A FieldPath of length zero is invalid. - -description: "empty field path" -update_paths: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - field_paths: < - > - json_values: "1" - is_error: true -> diff --git a/tests/unit/testdata/update-paths-25.textproto b/tests/unit/testdata/update-paths-25.textproto deleted file mode 100644 index b081c4e2b..000000000 --- a/tests/unit/testdata/update-paths-25.textproto +++ /dev/null @@ -1,22 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# The same field cannot occur more than once. - -description: "duplicate field path" -update_paths: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - field_paths: < - field: "a" - > - field_paths: < - field: "b" - > - field_paths: < - field: "a" - > - json_values: "1" - json_values: "2" - json_values: "3" - is_error: true -> diff --git a/tests/unit/testdata/update-paths-3.textproto b/tests/unit/testdata/update-paths-3.textproto deleted file mode 100644 index 5bc2bb94c..000000000 --- a/tests/unit/testdata/update-paths-3.textproto +++ /dev/null @@ -1,14 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# Empty fields are not allowed. - -description: "empty field" -update_paths: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - field_paths: < - field: "" - > - json_values: "1" - is_error: true -> diff --git a/tests/unit/testdata/update-paths-4.textproto b/tests/unit/testdata/update-paths-4.textproto deleted file mode 100644 index 307fd3aa7..000000000 --- a/tests/unit/testdata/update-paths-4.textproto +++ /dev/null @@ -1,39 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# If a field's value is the Delete sentinel, then it doesn't appear in the update -# data, but does in the mask. - -description: "Delete" -update_paths: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - field_paths: < - field: "a" - > - field_paths: < - field: "b" - > - json_values: "1" - json_values: "\"Delete\"" - request: < - database: "projects/projectID/databases/(default)" - writes: < - update: < - name: "projects/projectID/databases/(default)/documents/C/d" - fields: < - key: "a" - value: < - integer_value: 1 - > - > - > - update_mask: < - field_paths: "a" - field_paths: "b" - > - current_document: < - exists: true - > - > - > -> diff --git a/tests/unit/testdata/update-paths-5.textproto b/tests/unit/testdata/update-paths-5.textproto deleted file mode 100644 index 354fde994..000000000 --- a/tests/unit/testdata/update-paths-5.textproto +++ /dev/null @@ -1,28 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# If the input data consists solely of Deletes, then the update operation has no -# map, just an update mask. - -description: "Delete alone" -update_paths: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - field_paths: < - field: "a" - > - json_values: "\"Delete\"" - request: < - database: "projects/projectID/databases/(default)" - writes: < - update: < - name: "projects/projectID/databases/(default)/documents/C/d" - > - update_mask: < - field_paths: "a" - > - current_document: < - exists: true - > - > - > -> diff --git a/tests/unit/testdata/update-paths-6.textproto b/tests/unit/testdata/update-paths-6.textproto deleted file mode 100644 index 02ca343cb..000000000 --- a/tests/unit/testdata/update-paths-6.textproto +++ /dev/null @@ -1,40 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# The Update call supports a last-update-time precondition. - -description: "last-update-time precondition" -update_paths: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - precondition: < - update_time: < - seconds: 42 - > - > - field_paths: < - field: "a" - > - json_values: "1" - request: < - database: "projects/projectID/databases/(default)" - writes: < - update: < - name: "projects/projectID/databases/(default)/documents/C/d" - fields: < - key: "a" - value: < - integer_value: 1 - > - > - > - update_mask: < - field_paths: "a" - > - current_document: < - update_time: < - seconds: 42 - > - > - > - > -> diff --git a/tests/unit/testdata/update-paths-7.textproto b/tests/unit/testdata/update-paths-7.textproto deleted file mode 100644 index 88e270f7a..000000000 --- a/tests/unit/testdata/update-paths-7.textproto +++ /dev/null @@ -1,10 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# It is a client-side error to call Update with empty data. - -description: "no paths" -update_paths: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - is_error: true -> diff --git a/tests/unit/testdata/update-paths-8.textproto b/tests/unit/testdata/update-paths-8.textproto deleted file mode 100644 index d3aafe36f..000000000 --- a/tests/unit/testdata/update-paths-8.textproto +++ /dev/null @@ -1,15 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# Empty fields are not allowed. - -description: "empty field path component" -update_paths: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - field_paths: < - field: "*" - field: "" - > - json_values: "1" - is_error: true -> diff --git a/tests/unit/testdata/update-paths-9.textproto b/tests/unit/testdata/update-paths-9.textproto deleted file mode 100644 index 1f9b058ce..000000000 --- a/tests/unit/testdata/update-paths-9.textproto +++ /dev/null @@ -1,19 +0,0 @@ -# DO NOT MODIFY. -# This file was generated by cloud.google.com/go/firestore/cmd/generate-firestore-tests. - -# In the input data, one field cannot be a prefix of another. - -description: "prefix #1" -update_paths: < - doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" - field_paths: < - field: "a" - field: "b" - > - field_paths: < - field: "a" - > - json_values: "1" - json_values: "2" - is_error: true ->