testutil.cc 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653
  1. /*
  2. * Copyright 2018 Google
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include "Firestore/core/test/unit/testutil/testutil.h"
  17. #include <algorithm>
  18. #include <chrono> // NOLINT(build/c++11)
  19. #include <set>
  20. #include "Firestore/core/include/firebase/firestore/geo_point.h"
  21. #include "Firestore/core/include/firebase/firestore/timestamp.h"
  22. #include "Firestore/core/src/core/composite_filter.h"
  23. #include "Firestore/core/src/core/direction.h"
  24. #include "Firestore/core/src/core/field_filter.h"
  25. #include "Firestore/core/src/core/order_by.h"
  26. #include "Firestore/core/src/core/query.h"
  27. #include "Firestore/core/src/model/delete_mutation.h"
  28. #include "Firestore/core/src/model/document.h"
  29. #include "Firestore/core/src/model/document_key.h"
  30. #include "Firestore/core/src/model/document_set.h"
  31. #include "Firestore/core/src/model/field_mask.h"
  32. #include "Firestore/core/src/model/field_path.h"
  33. #include "Firestore/core/src/model/field_transform.h"
  34. #include "Firestore/core/src/model/mutable_document.h"
  35. #include "Firestore/core/src/model/patch_mutation.h"
  36. #include "Firestore/core/src/model/precondition.h"
  37. #include "Firestore/core/src/model/set_mutation.h"
  38. #include "Firestore/core/src/model/transform_operation.h"
  39. #include "Firestore/core/src/model/value_util.h"
  40. #include "Firestore/core/src/model/verify_mutation.h"
  41. #include "Firestore/core/src/nanopb/byte_string.h"
  42. #include "Firestore/core/src/nanopb/message.h"
  43. #include "Firestore/core/src/nanopb/nanopb_util.h"
  44. #include "Firestore/core/src/remote/remote_event.h"
  45. #include "Firestore/core/src/util/hard_assert.h"
  46. #include "Firestore/core/src/util/statusor.h"
  47. #include "Firestore/core/src/util/string_format.h"
  48. #include "Firestore/core/test/unit/remote/fake_target_metadata_provider.h"
  49. #include "absl/memory/memory.h"
  50. namespace firebase {
  51. namespace firestore {
  52. namespace testutil {
  53. using model::DeepClone;
  54. using model::Document;
  55. using model::DocumentComparator;
  56. using model::DocumentKey;
  57. using model::DocumentSet;
  58. using model::FieldMask;
  59. using model::FieldPath;
  60. using model::FieldTransform;
  61. using model::MutableDocument;
  62. using model::NullValue;
  63. using model::ObjectValue;
  64. using model::Precondition;
  65. using model::TargetId;
  66. using model::TransformOperation;
  67. using nanopb::ByteString;
  68. using nanopb::Message;
  69. using nanopb::SetRepeatedField;
  70. using nanopb::SharedMessage;
  71. using remote::RemoteEvent;
  72. using util::StringFormat;
  73. /**
  74. * A string sentinel that can be used with PatchMutation() to mark a field for
  75. * deletion.
  76. */
  77. constexpr const char* kDeleteSentinel = "<DELETE>";
  78. // We use a canonical NaN bit pattern that's common for both Objective-C and
  79. // Java. Specifically:
  80. //
  81. // - sign: 0
  82. // - exponent: 11 bits, all 1
  83. // - significand: 52 bits, MSB=1, rest=0
  84. //
  85. // This matches the Firestore backend which uses Double.doubleToLongBits from
  86. // the JDK (which is defined to normalize all NaNs to this value). This also
  87. // happens to be a common value for NAN in C++, but C++ does not require this
  88. // specific NaN value to be used, so we normalize.
  89. const uint64_t kCanonicalNanBits = 0x7ff8000000000000ULL;
  90. namespace details {
  91. Message<google_firestore_v1_Value> BlobValue(
  92. std::initializer_list<uint8_t> octets) {
  93. nanopb::ByteString contents{octets};
  94. Message<google_firestore_v1_Value> result;
  95. result->which_value_type = google_firestore_v1_Value_bytes_value_tag;
  96. result->bytes_value = nanopb::MakeBytesArray(octets.begin(), octets.size());
  97. return result;
  98. }
  99. } // namespace details
  100. ByteString Bytes(std::initializer_list<uint8_t> octets) {
  101. return ByteString(octets);
  102. }
  103. Message<google_firestore_v1_Value> Value(std::nullptr_t) {
  104. return DeepClone(NullValue());
  105. }
  106. Message<google_firestore_v1_Value> Value(double value) {
  107. Message<google_firestore_v1_Value> result;
  108. result->which_value_type = google_firestore_v1_Value_double_value_tag;
  109. result->double_value = value;
  110. return result;
  111. }
  112. Message<google_firestore_v1_Value> Value(Timestamp value) {
  113. Message<google_firestore_v1_Value> result;
  114. result->which_value_type = google_firestore_v1_Value_timestamp_value_tag;
  115. result->timestamp_value.seconds = value.seconds();
  116. result->timestamp_value.nanos = value.nanoseconds();
  117. return result;
  118. }
  119. Message<google_firestore_v1_Value> Value(const char* value) {
  120. Message<google_firestore_v1_Value> result;
  121. result->which_value_type = google_firestore_v1_Value_string_value_tag;
  122. result->string_value = nanopb::MakeBytesArray(value);
  123. return result;
  124. }
  125. Message<google_firestore_v1_Value> Value(const std::string& value) {
  126. Message<google_firestore_v1_Value> result;
  127. result->which_value_type = google_firestore_v1_Value_string_value_tag;
  128. result->string_value = nanopb::MakeBytesArray(value);
  129. return result;
  130. }
  131. Message<google_firestore_v1_Value> Value(const nanopb::ByteString& value) {
  132. Message<google_firestore_v1_Value> result;
  133. result->which_value_type = google_firestore_v1_Value_bytes_value_tag;
  134. result->bytes_value = nanopb::MakeBytesArray(value.begin(), value.size());
  135. return result;
  136. }
  137. Message<google_firestore_v1_Value> Value(const GeoPoint& value) {
  138. Message<google_firestore_v1_Value> result;
  139. result->which_value_type = google_firestore_v1_Value_geo_point_value_tag;
  140. result->geo_point_value.latitude = value.latitude();
  141. result->geo_point_value.longitude = value.longitude();
  142. return result;
  143. }
  144. Message<google_firestore_v1_Value> Value(
  145. Message<google_firestore_v1_Value> value) {
  146. return value;
  147. }
  148. Message<google_firestore_v1_Value> Value(
  149. Message<google_firestore_v1_MapValue> value) {
  150. Message<google_firestore_v1_Value> result;
  151. result->which_value_type = google_firestore_v1_Value_map_value_tag;
  152. result->map_value = *value.release();
  153. return result;
  154. }
  155. Message<google_firestore_v1_Value> Value(
  156. Message<google_firestore_v1_ArrayValue> value) {
  157. Message<google_firestore_v1_Value> result;
  158. result->which_value_type = google_firestore_v1_Value_array_value_tag;
  159. result->array_value = *value.release();
  160. return result;
  161. }
  162. Message<google_firestore_v1_Value> Value(const model::ObjectValue& value) {
  163. return DeepClone(value.Get());
  164. }
  165. ObjectValue WrapObject(Message<google_firestore_v1_Value> value) {
  166. return ObjectValue{std::move(value)};
  167. }
  168. model::DocumentKey Key(absl::string_view path) {
  169. return model::DocumentKey::FromPathString(std::string(path));
  170. }
  171. model::FieldPath Field(absl::string_view field) {
  172. auto path = model::FieldPath::FromServerFormat(std::string(field));
  173. return path.ConsumeValueOrDie();
  174. }
  175. model::DatabaseId DbId(std::string project) {
  176. size_t slash = project.find('/');
  177. if (slash == std::string::npos) {
  178. return model::DatabaseId(std::move(project), model::DatabaseId::kDefault);
  179. } else {
  180. std::string database_id = project.substr(slash + 1);
  181. project = project.substr(0, slash);
  182. return model::DatabaseId(std::move(project), std::move(database_id));
  183. }
  184. }
  185. Message<google_firestore_v1_Value> Ref(std::string project,
  186. absl::string_view path) {
  187. model::DatabaseId database_id = DbId(std::move(project));
  188. Message<google_firestore_v1_Value> result;
  189. result->which_value_type = google_firestore_v1_Value_reference_value_tag;
  190. result->string_value = nanopb::MakeBytesArray(
  191. StringFormat("projects/%s/databases/%s/documents/%s",
  192. database_id.project_id(), database_id.database_id(), path));
  193. return result;
  194. }
  195. model::ResourcePath Resource(absl::string_view field) {
  196. return model::ResourcePath::FromString(std::string(field));
  197. }
  198. model::SnapshotVersion Version(int64_t version) {
  199. namespace chr = std::chrono;
  200. auto timepoint =
  201. chr::time_point<chr::system_clock>(chr::microseconds(version));
  202. return model::SnapshotVersion{Timestamp::FromTimePoint(timepoint)};
  203. }
  204. model::SnapshotVersion Version(int64_t seconds, int32_t nanoseconds) {
  205. return model::SnapshotVersion{Timestamp(seconds, nanoseconds)};
  206. }
  207. model::MutableDocument Doc(absl::string_view key,
  208. int64_t version,
  209. Message<google_firestore_v1_Value> data) {
  210. return MutableDocument::FoundDocument(Key(key), Version(version),
  211. ObjectValue{std::move(data)})
  212. .WithReadTime(Version(version));
  213. }
  214. model::MutableDocument Doc(absl::string_view key, int64_t version) {
  215. return MutableDocument::FoundDocument(Key(key), Version(version),
  216. ObjectValue{})
  217. .WithReadTime(Version(version));
  218. }
  219. model::MutableDocument DeletedDoc(absl::string_view key, int64_t version) {
  220. return MutableDocument::NoDocument(Key(key), Version(version))
  221. .WithReadTime(Version(version));
  222. }
  223. model::MutableDocument DeletedDoc(DocumentKey key, int64_t version) {
  224. return MutableDocument::NoDocument(std::move(key), Version(version))
  225. .WithReadTime(Version(version));
  226. }
  227. model::MutableDocument UnknownDoc(absl::string_view key, int64_t version) {
  228. return MutableDocument::UnknownDocument(Key(key), Version(version));
  229. }
  230. model::MutableDocument InvalidDoc(absl::string_view key) {
  231. return MutableDocument::InvalidDocument(Key(key));
  232. }
  233. DocumentComparator DocComparator(absl::string_view field_path) {
  234. return Query("docs").AddingOrderBy(OrderBy(field_path)).Comparator();
  235. }
  236. DocumentSet DocSet(DocumentComparator comp, std::vector<Document> docs) {
  237. DocumentSet set{std::move(comp)};
  238. for (const Document& doc : docs) {
  239. set = set.insert(doc);
  240. }
  241. return set;
  242. }
  243. core::FieldFilter::Operator OperatorFromString(absl::string_view s) {
  244. if (s == "<") {
  245. return core::FieldFilter::Operator::LessThan;
  246. } else if (s == "<=") {
  247. return core::FieldFilter::Operator::LessThanOrEqual;
  248. } else if (s == "==") {
  249. return core::FieldFilter::Operator::Equal;
  250. } else if (s == "!=") {
  251. return core::FieldFilter::Operator::NotEqual;
  252. } else if (s == ">") {
  253. return core::FieldFilter::Operator::GreaterThan;
  254. } else if (s == ">=") {
  255. return core::FieldFilter::Operator::GreaterThanOrEqual;
  256. // Both are accepted for compatibility with spec tests and existing
  257. // canonical ids.
  258. } else if (s == "array_contains" || s == "array-contains") {
  259. return core::FieldFilter::Operator::ArrayContains;
  260. } else if (s == "in") {
  261. return core::FieldFilter::Operator::In;
  262. } else if (s == "array-contains-any") {
  263. return core::FieldFilter::Operator::ArrayContainsAny;
  264. } else if (s == "not-in") {
  265. return core::FieldFilter::Operator::NotIn;
  266. } else {
  267. HARD_FAIL("Unknown operator: %s", s);
  268. }
  269. }
  270. core::FieldFilter Filter(absl::string_view key,
  271. absl::string_view op,
  272. Message<google_firestore_v1_Value> value) {
  273. return core::FieldFilter::Create(Field(key), OperatorFromString(op),
  274. std::move(value));
  275. }
  276. core::FieldFilter Filter(absl::string_view key,
  277. absl::string_view op,
  278. Message<google_firestore_v1_ArrayValue> value) {
  279. return core::FieldFilter::Create(Field(key), OperatorFromString(op),
  280. Value(std::move(value)));
  281. }
  282. core::FieldFilter Filter(absl::string_view key,
  283. absl::string_view op,
  284. std::nullptr_t) {
  285. return Filter(key, op, DeepClone(NullValue()));
  286. }
  287. core::FieldFilter Filter(absl::string_view key,
  288. absl::string_view op,
  289. const char* value) {
  290. return Filter(key, op, Value(value));
  291. }
  292. core::FieldFilter Filter(absl::string_view key,
  293. absl::string_view op,
  294. int value) {
  295. return Filter(key, op, Value(value));
  296. }
  297. core::FieldFilter Filter(absl::string_view key,
  298. absl::string_view op,
  299. double value) {
  300. return Filter(key, op, Value(value));
  301. }
  302. core::CompositeFilter AndFilters(std::vector<core::Filter> filters) {
  303. return core::CompositeFilter::Create(std::move(filters),
  304. core::CompositeFilter::Operator::And);
  305. }
  306. core::CompositeFilter OrFilters(std::vector<core::Filter> filters) {
  307. return core::CompositeFilter::Create(std::move(filters),
  308. core::CompositeFilter::Operator::Or);
  309. }
  310. core::Direction Direction(absl::string_view direction) {
  311. if (direction == "asc") {
  312. return core::Direction::Ascending;
  313. } else if (direction == "desc") {
  314. return core::Direction::Descending;
  315. } else {
  316. HARD_FAIL("Unknown direction: %s (use \"asc\" or \"desc\")", direction);
  317. }
  318. }
  319. core::OrderBy OrderBy(absl::string_view key, absl::string_view direction) {
  320. return core::OrderBy(Field(key), Direction(direction));
  321. }
  322. core::OrderBy OrderBy(model::FieldPath field_path, core::Direction direction) {
  323. return core::OrderBy(std::move(field_path), direction);
  324. }
  325. core::Query Query(absl::string_view path) {
  326. return core::Query(Resource(path));
  327. }
  328. core::Query CollectionGroupQuery(absl::string_view collection_id) {
  329. return core::Query(model::ResourcePath::Empty(),
  330. std::make_shared<const std::string>(collection_id));
  331. }
  332. /** Creates a remote event that inserts a list of documents. */
  333. RemoteEvent AddedRemoteEvent(const std::vector<MutableDocument>& docs,
  334. const std::vector<TargetId>& added_to_targets) {
  335. HARD_ASSERT(!docs.empty(), "Cannot pass empty docs array");
  336. const model::ResourcePath& collection_path = docs[0].key().path().PopLast();
  337. auto metadata_provider =
  338. remote::FakeTargetMetadataProvider::CreateEmptyResultProvider(
  339. collection_path, added_to_targets);
  340. remote::WatchChangeAggregator aggregator{&metadata_provider};
  341. model::SnapshotVersion version;
  342. for (const MutableDocument& doc : docs) {
  343. HARD_ASSERT(!doc.has_local_mutations(),
  344. "Docs from remote updates shouldn't have local changes.");
  345. remote::DocumentWatchChange change{added_to_targets, {}, doc.key(), doc};
  346. aggregator.HandleDocumentChange(change);
  347. version = version > doc.version() ? version : doc.version();
  348. }
  349. return aggregator.CreateRemoteEvent(version);
  350. }
  351. /** Creates a remote event that inserts a new document. */
  352. RemoteEvent AddedRemoteEvent(const MutableDocument& doc,
  353. const std::vector<TargetId>& added_to_targets) {
  354. std::vector<MutableDocument> docs{doc};
  355. return AddedRemoteEvent(docs, added_to_targets);
  356. }
  357. /** Creates a remote event with changes to a document. */
  358. RemoteEvent UpdateRemoteEvent(
  359. const MutableDocument& doc,
  360. const std::vector<TargetId>& updated_in_targets,
  361. const std::vector<TargetId>& removed_from_targets) {
  362. return UpdateRemoteEventWithLimboTargets(doc, updated_in_targets,
  363. removed_from_targets, {});
  364. }
  365. RemoteEvent UpdateRemoteEventWithLimboTargets(
  366. const MutableDocument& doc,
  367. const std::vector<TargetId>& updated_in_targets,
  368. const std::vector<TargetId>& removed_from_targets,
  369. const std::vector<TargetId>& limbo_targets) {
  370. HARD_ASSERT(!doc.is_found_document() || !doc.has_local_mutations(),
  371. "Docs from remote updates shouldn't have local changes.");
  372. remote::DocumentWatchChange change{updated_in_targets, removed_from_targets,
  373. doc.key(), doc};
  374. std::vector<TargetId> listens = updated_in_targets;
  375. listens.insert(listens.end(), removed_from_targets.begin(),
  376. removed_from_targets.end());
  377. auto metadata_provider =
  378. remote::FakeTargetMetadataProvider::CreateSingleResultProvider(
  379. doc.key(), listens, limbo_targets);
  380. remote::WatchChangeAggregator aggregator{&metadata_provider};
  381. aggregator.HandleDocumentChange(change);
  382. return aggregator.CreateRemoteEvent(doc.version());
  383. }
  384. // TODO(chenbrian): Rewrite SetMutation to allow parsing of field
  385. // transforms directly in the `values` parameter once the UserDataReader/
  386. // UserDataWriter changes are ported from Web and Android.
  387. model::SetMutation SetMutation(
  388. absl::string_view path,
  389. Message<google_firestore_v1_Value> values,
  390. std::vector<std::pair<std::string, TransformOperation>> transforms) {
  391. std::vector<FieldTransform> field_transforms;
  392. for (auto&& pair : transforms) {
  393. auto field_path = Field(std::move(pair.first));
  394. TransformOperation&& op_ptr = std::move(pair.second);
  395. FieldTransform transform(std::move(field_path), std::move(op_ptr));
  396. field_transforms.push_back(std::move(transform));
  397. }
  398. return model::SetMutation(Key(path), model::ObjectValue{std::move(values)},
  399. model::Precondition::None(),
  400. std::move(field_transforms));
  401. }
  402. // TODO(chenbrian): Rewrite PatchMutation to allow parsing of field
  403. // transforms directly in the `values` parameter once the UserDataReader/
  404. // UserDataWriter changes are ported from Web and Android.
  405. model::PatchMutation PatchMutation(
  406. absl::string_view path,
  407. Message<google_firestore_v1_Value> values,
  408. // TODO(rsgowman): Investigate changing update_mask to a set.
  409. std::vector<std::pair<std::string, TransformOperation>> transforms) {
  410. return PatchMutationHelper(path, std::move(values), std::move(transforms),
  411. Precondition::Exists(true), absl::nullopt);
  412. }
  413. model::PatchMutation PatchMutation(
  414. absl::string_view path,
  415. Message<google_firestore_v1_Value> values,
  416. const std::vector<model::FieldPath>& update_mask,
  417. std::vector<std::pair<std::string, TransformOperation>> transforms) {
  418. return PatchMutationHelper(path, std::move(values), std::move(transforms),
  419. Precondition::Exists(true), update_mask);
  420. }
  421. // TODO(chenbrian): Rewrite MergeMutation to allow parsing of field
  422. // transforms directly in the `values` parameter once the UserDataReader/
  423. // UserDataWriter changes are ported from Web and Android.
  424. model::PatchMutation MergeMutation(
  425. absl::string_view path,
  426. Message<google_firestore_v1_Value> values,
  427. const std::vector<model::FieldPath>& update_mask,
  428. std::vector<std::pair<std::string, TransformOperation>> transforms) {
  429. return PatchMutationHelper(path, std::move(values), std::move(transforms),
  430. Precondition::None(), update_mask);
  431. }
  432. model::PatchMutation PatchMutationHelper(
  433. absl::string_view path,
  434. Message<google_firestore_v1_Value> values,
  435. std::vector<std::pair<std::string, TransformOperation>> transforms,
  436. Precondition precondition,
  437. const absl::optional<std::vector<model::FieldPath>>& update_mask) {
  438. ObjectValue object_value{};
  439. std::set<FieldPath> field_mask_paths;
  440. std::vector<FieldTransform> field_transforms;
  441. for (auto&& pair : transforms) {
  442. auto field_path = Field(std::move(pair.first));
  443. TransformOperation&& op_ptr = std::move(pair.second);
  444. FieldTransform transform(std::move(field_path), std::move(op_ptr));
  445. field_transforms.push_back(std::move(transform));
  446. }
  447. for (pb_size_t i = 0; i < values->map_value.fields_count; ++i) {
  448. FieldPath field_path =
  449. Field(nanopb::MakeStringView(values->map_value.fields[i].key));
  450. field_mask_paths.insert(field_path);
  451. const google_firestore_v1_Value& value = values->map_value.fields[i].value;
  452. if (value.which_value_type != google_firestore_v1_Value_string_value_tag ||
  453. nanopb::MakeStringView(value.string_value) != kDeleteSentinel) {
  454. object_value.Set(field_path, DeepClone(value));
  455. } else if (nanopb::MakeStringView(value.string_value) == kDeleteSentinel) {
  456. object_value.Delete(field_path);
  457. }
  458. }
  459. FieldMask mask(
  460. update_mask.has_value()
  461. ? std::set<FieldPath>(update_mask->begin(), update_mask->end())
  462. : field_mask_paths);
  463. return model::PatchMutation(Key(path), std::move(object_value),
  464. std::move(mask), precondition,
  465. std::move(field_transforms));
  466. }
  467. std::pair<std::string, TransformOperation> ServerTimestamp(std::string field) {
  468. return std::pair<std::string, TransformOperation>(
  469. std::move(field), model::ServerTimestampTransform());
  470. }
  471. std::pair<std::string, TransformOperation> Increment(
  472. std::string field, Message<google_firestore_v1_Value> operand) {
  473. model::NumericIncrementTransform transform(std::move(operand));
  474. return std::pair<std::string, TransformOperation>(std::move(field),
  475. std::move(transform));
  476. }
  477. std::pair<std::string, TransformOperation> ArrayUnion(
  478. std::string field,
  479. const std::vector<Message<google_firestore_v1_Value>>& operands) {
  480. Message<google_firestore_v1_ArrayValue> array_value;
  481. SetRepeatedField(&array_value->values, &array_value->values_count,
  482. operands.begin(), operands.end(),
  483. [](const Message<google_firestore_v1_Value>& value) {
  484. return *DeepClone(*value).release();
  485. });
  486. model::ArrayTransform transform(TransformOperation::Type::ArrayUnion,
  487. std::move(array_value));
  488. return std::pair<std::string, TransformOperation>(std::move(field),
  489. std::move(transform));
  490. }
  491. model::DeleteMutation DeleteMutation(absl::string_view path) {
  492. return model::DeleteMutation(Key(path), Precondition::None());
  493. }
  494. model::VerifyMutation VerifyMutation(absl::string_view path, int64_t version) {
  495. return model::VerifyMutation(Key(path),
  496. Precondition::UpdateTime(Version(version)));
  497. }
  498. model::MutationResult MutationResult(int64_t version) {
  499. return model::MutationResult(Version(version), Array());
  500. }
  501. nanopb::ByteString ResumeToken(int64_t snapshot_version) {
  502. if (snapshot_version == 0) {
  503. // TODO(rsgowman): The other platforms return null here, though I'm not sure
  504. // if they ever rely on that. I suspect it'd be sufficient to return '{}'.
  505. // But for now, we'll just abort() until we hit a test case that actually
  506. // makes use of this.
  507. HARD_FAIL("Unsupported snapshot version %s", snapshot_version);
  508. }
  509. std::string snapshot_string =
  510. std::string("snapshot-") + std::to_string(snapshot_version);
  511. return nanopb::ByteString(snapshot_string);
  512. }
  513. model::FieldIndex MakeFieldIndex(const std::string& collection_group) {
  514. return {-1, collection_group, {}, model::FieldIndex::InitialState()};
  515. }
  516. model::FieldIndex MakeFieldIndex(const std::string& collection_group,
  517. int32_t index_id,
  518. model::IndexState state) {
  519. return {index_id, collection_group, {}, state};
  520. }
  521. model::FieldIndex MakeFieldIndex(const std::string& collection_group,
  522. const std::string& field,
  523. model::Segment::Kind kind) {
  524. return {-1,
  525. collection_group,
  526. {model::Segment{Field(field), kind}},
  527. model::FieldIndex::InitialState()};
  528. }
  529. model::FieldIndex MakeFieldIndex(const std::string& collection_group,
  530. const std::string& field_1,
  531. model::Segment::Kind kind_1,
  532. const std::string& field_2,
  533. model::Segment::Kind kind_2) {
  534. return {-1,
  535. collection_group,
  536. {model::Segment{Field(field_1), kind_1},
  537. model::Segment{Field(field_2), kind_2}},
  538. model::FieldIndex::InitialState()};
  539. }
  540. model::FieldIndex MakeFieldIndex(const std::string& collection_group,
  541. const std::string& field_1,
  542. model::Segment::Kind kind_1,
  543. const std::string& field_2,
  544. model::Segment::Kind kind_2,
  545. const std::string& field_3,
  546. model::Segment::Kind kind_3) {
  547. return {-1,
  548. collection_group,
  549. {model::Segment{Field(field_1), kind_1},
  550. model::Segment{Field(field_2), kind_2},
  551. model::Segment{Field(field_3), kind_3}},
  552. model::FieldIndex::InitialState()};
  553. }
  554. model::FieldIndex MakeFieldIndex(const std::string& collection_group,
  555. int32_t index_id,
  556. model::IndexState state,
  557. const std::string& field_1,
  558. model::Segment::Kind kind_1) {
  559. return {index_id,
  560. collection_group,
  561. {model::Segment{Field(field_1), kind_1}},
  562. state};
  563. }
  564. } // namespace testutil
  565. } // namespace firestore
  566. } // namespace firebase