Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(47)

Side by Side Diff: pkg/gcloud/test/datastore/e2e/datastore_test_impl.dart

Issue 804973002: Add appengine/gcloud/mustache dependencies. (Closed) Base URL: git@github.com:dart-lang/pub-dartlang-dart.git@master
Patch Set: Added AUTHORS/LICENSE/PATENTS files Created 6 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « pkg/gcloud/test/common_e2e.dart ('k') | pkg/gcloud/test/datastore/e2e/utils.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 library datastore_test;
6
7 /// NOTE: In order to run these tests, the following datastore indices must
8 /// exist:
9 /// $ cat index.yaml
10 /// indexes:
11 /// - kind: TestQueryKind
12 /// ancestor: no
13 /// properties:
14 /// - name: indexedProp
15 /// direction: asc
16 /// - name: blobPropertyIndexed
17 /// direction: asc
18 ///
19 /// - kind: TestQueryKind
20 /// ancestor: no
21 /// properties:
22 /// - name: listproperty
23 /// - name: test_property
24 /// direction: desc
25 /// $ gcloud preview datastore create-indexes .
26 /// 02:19 PM Host: appengine.google.com
27 /// 02:19 PM Uploading index definitions.
28
29
30 import 'dart:async';
31
32 import 'package:gcloud/datastore.dart';
33 import 'package:gcloud/src/datastore_impl.dart' as datastore_impl;
34 import 'package:gcloud/common.dart';
35 import 'package:unittest/unittest.dart';
36
37 import '../error_matchers.dart';
38 import 'utils.dart';
39
40 import '../../common_e2e.dart';
41
42 Future sleep(Duration duration) {
43 var completer = new Completer();
44 new Timer(duration, completer.complete);
45 return completer.future;
46 }
47
48 Future<List<Entity>> consumePages(FirstPageProvider provider) {
49 return new StreamFromPages(provider).stream.toList();
50 }
51
52 runTests(Datastore datastore) {
53 Future withTransaction(Function f, {bool xg: false}) {
54 return datastore.beginTransaction(crossEntityGroup: xg).then(f);
55 }
56
57 Future<List<Key>> insert(List<Entity> entities,
58 List<Entity> autoIdEntities,
59 {bool transactional: true}) {
60 if (transactional) {
61 return withTransaction((Transaction transaction) {
62 return datastore.commit(inserts: entities,
63 autoIdInserts: autoIdEntities,
64 transaction: transaction).then((result) {
65 if (autoIdEntities != null && autoIdEntities.length > 0) {
66 expect(result.autoIdInsertKeys.length,
67 equals(autoIdEntities.length));
68 }
69 return result.autoIdInsertKeys;
70 });
71 }, xg: true);
72 } else {
73 return datastore.commit(inserts: entities, autoIdInserts: autoIdEntities)
74 .then((result) {
75 if (autoIdEntities != null && autoIdEntities.length > 0) {
76 expect(result.autoIdInsertKeys.length,
77 equals(autoIdEntities.length));
78 }
79 return result.autoIdInsertKeys;
80 });
81 }
82 }
83
84 Future delete(List<Key> keys, {bool transactional: true}) {
85 if (transactional) {
86 return withTransaction((Transaction t) {
87 return datastore.commit(deletes: keys, transaction: t)
88 .then((result) => null);
89 }, xg: true);
90 } else {
91 return datastore.commit(deletes: keys).then((_) => _);
92 }
93 }
94
95 Future<List<Entity>> lookup(List<Key> keys, {bool transactional: true}) {
96 if (transactional) {
97 return withTransaction((Transaction transaction) {
98 return datastore.lookup(keys, transaction: transaction);
99 }, xg: true);
100 } else {
101 return datastore.lookup(keys);
102 }
103 }
104
105 bool isValidKey(Key key, {bool ignoreIds: false}) {
106 if (key.elements.length == 0) return false;
107
108 for (var element in key.elements) {
109 if (element.kind == null || element.kind is! String) return false;
110 if (!ignoreIds) {
111 if (element.id == null ||
112 (element.id is! String && element.id is! int)) {
113 return false;
114 }
115 }
116 }
117 return true;
118 }
119
120 bool compareKey(Key a, Key b, {bool ignoreIds: false}) {
121 if (a.partition != b.partition) return false;
122 if (a.elements.length != b.elements.length) return false;
123 for (int i = 0; i < a.elements.length; i++) {
124 if (a.elements[i].kind != b.elements[i].kind) return false;
125 if (!ignoreIds && a.elements[i].id != b.elements[i].id) return false;
126 }
127 return true;
128 }
129
130 bool compareEntity(Entity a, Entity b, {bool ignoreIds: false}) {
131 if (!compareKey(a.key, b.key, ignoreIds: ignoreIds)) return false;
132 if (a.properties.length != b.properties.length) return false;
133 for (var key in a.properties.keys) {
134 if (!b.properties.containsKey(key)) return false;
135 if (a.properties[key] != null && a.properties[key] is List) {
136 var aList = a.properties[key];
137 var bList = b.properties[key];
138 if (aList.length != bList.length) return false;
139 for (var i = 0; i < aList.length; i++) {
140 if (aList[i] != bList[i]) return false;
141 }
142 } else if (a.properties[key] is BlobValue) {
143 if (b.properties[key] is BlobValue) {
144 var b1 = (a.properties[key] as BlobValue).bytes;
145 var b2 = (b.properties[key] as BlobValue).bytes;
146 if (b1.length != b2.length) return false;
147 for (var i = 0; i < b1.length; i++) {
148 if (b1[i] != b2[i]) return false;
149 }
150 return true;
151 }
152 return false;
153 } else {
154 if (a.properties[key] != b.properties[key]) {
155 return false;
156 }
157 }
158 }
159 return true;
160 }
161
162 group('e2e_datastore', () {
163 group('insert', () {
164 Future<List<Key>> testInsert(List<Entity> entities,
165 {bool transactional: false, bool xg: false, bool unnamed: true}) {
166 Future<List<Key>> test(Transaction transaction) {
167 return datastore.commit(autoIdInserts: entities,
168 transaction: transaction)
169 .then((CommitResult result) {
170 expect(result.autoIdInsertKeys.length, equals(entities.length));
171
172 for (var i = 0; i < result.autoIdInsertKeys.length; i++) {
173 var key = result.autoIdInsertKeys[i];
174 expect(isValidKey(key), isTrue);
175 if (unnamed) {
176 expect(compareKey(key, entities[i].key, ignoreIds: true),
177 isTrue);
178 } else {
179 expect(compareKey(key, entities[i].key), isTrue);
180 }
181 }
182 return result.autoIdInsertKeys;
183 });
184 }
185
186 if (transactional) {
187 return withTransaction(test, xg: xg);
188 }
189 return test(null);
190 }
191
192 Future<List<Key>> testInsertNegative(List<Entity> entities,
193 {bool transactional: false, bool xg: false}) {
194 test(Transaction transaction) {
195 expect(datastore.commit(inserts: entities,
196 transaction: transaction),
197 throwsA(isApplicationError));
198 }
199
200 if (transactional) {
201 return withTransaction(test, xg: xg);
202 }
203 return test(null);
204 }
205
206 var unnamedEntities1 = buildEntities(42, 43);
207 var unnamedEntities5 = buildEntities(1, 6);
208 var unnamedEntities20 = buildEntities(6, 26);
209 var named20000 = buildEntities(
210 1000, 21001, idFunction: (i) => 'named_${i}_of_10000');
211
212 test('insert', () {
213 return testInsert(unnamedEntities5, transactional: false).then((keys) {
214 return delete(keys).then((_) {
215 return lookup(keys).then((List<Entity> entities) {
216 entities.forEach((Entity e) => expect(e, isNull));
217 });
218 });
219 });
220 });
221
222 test('insert_transactional', () {
223 return testInsert(unnamedEntities1, transactional: true).then((keys) {
224 return delete(keys).then((_) {
225 return lookup(keys).then((List<Entity> entities) {
226 entities.forEach((Entity e) => expect(e, isNull));
227 });
228 });
229 });
230 });
231
232 test('insert_transactional_xg', () {
233 return testInsert(
234 unnamedEntities5, transactional: true, xg: true).then((keys) {
235 return delete(keys).then((_) {
236 return lookup(keys).then((List<Entity> entities) {
237 entities.forEach((Entity e) => expect(e, isNull));
238 });
239 });
240 });
241 });
242
243 // Does not work with cloud datastore REST api, why?
244 test('negative_insert_transactional', () {
245 return testInsertNegative(unnamedEntities5, transactional: true);
246 });
247
248 // Does not work with cloud datastore REST api, why?
249 test('negative_insert_transactional_xg', () {
250 return testInsertNegative(
251 unnamedEntities20, transactional: true, xg: true);
252 });
253
254 test('negative_insert_20000_entities', () {
255 // Maybe it should not be a [DataStoreError] here?
256 // FIXME/TODO: This was adapted
257 expect(datastore.commit(inserts: named20000),
258 throws);
259 });
260
261 // TODO: test invalid inserts (like entities without key, ...)
262 });
263
264 group('allocate_ids', () {
265 test('allocate_ids_query', () {
266 compareResult(List<Key> keys, List<Key> completedKeys) {
267 expect(completedKeys.length, equals(keys.length));
268 for (int i = 0; i < keys.length; i++) {
269 var insertedKey = keys[i];
270 var completedKey = completedKeys[i];
271
272 expect(completedKey.elements.length,
273 equals(insertedKey.elements.length));
274 for (int j = 0; j < insertedKey.elements.length - 1; j++) {
275 expect(completedKey.elements[j], equals(insertedKey.elements[j]));
276 }
277 for (int j = insertedKey.elements.length - 1;
278 j < insertedKey.elements.length;
279 j++) {
280 expect(completedKey.elements[j].kind,
281 equals(insertedKey.elements[j].kind));
282 expect(completedKey.elements[j].id, isNotNull);
283 expect(completedKey.elements[j].id, isInt);
284 }
285 }
286 }
287
288 var keys = buildKeys(1, 4);
289 return datastore.allocateIds(keys).then((List<Key> completedKeys) {
290 compareResult(keys, completedKeys);
291 // TODO: Make sure we can insert these keys
292 // FIXME: Insert currently doesn't through if entities already exist!
293 });
294 });
295 });
296
297 group('lookup', () {
298 Future testLookup(List<Key> keysToLookup,
299 List<Entity> entitiesToLookup,
300 {bool transactional: false,
301 bool xg: false,
302 bool negative: false,
303 bool named: false}) {
304 expect(keysToLookup.length, equals(entitiesToLookup.length));
305 for (var i = 0; i < keysToLookup.length; i++) {
306 expect(compareKey(keysToLookup[i],
307 entitiesToLookup[i].key,
308 ignoreIds: !named), isTrue);
309 }
310
311 Future test(Transaction transaction) {
312 return datastore.lookup(keysToLookup)
313 .then((List<Entity> entities) {
314 expect(entities.length, equals(keysToLookup.length));
315 if (negative) {
316 for (int i = 0; i < entities.length; i++) {
317 expect(entities[i], isNull);
318 }
319 } else {
320 for (var i = 0; i < entities.length; i++) {
321 expect(compareKey(entities[i].key, keysToLookup[i]), isTrue);
322 expect(compareEntity(entities[i],
323 entitiesToLookup[i],
324 ignoreIds: !named), isTrue);
325 }
326 }
327 if (transaction != null) {
328 return
329 datastore.commit(transaction: transaction).then((_) => null);
330 }
331 });
332 }
333
334 if (transactional) {
335 return withTransaction(test, xg: xg);
336 }
337 return test(null);
338 }
339
340 var unnamedEntities1 = buildEntities(42, 43);
341 var unnamedEntities5 = buildEntities(1, 6);
342 var unnamedEntities20 = buildEntities(6, 26);
343 var entitiesWithAllPropertyTypes = buildEntityWithAllProperties(1, 6);
344
345 test('lookup', () {
346 return insert([], unnamedEntities20, transactional: false).then((keys) {
347 keys.forEach((key) => expect(isValidKey(key), isTrue));
348 return testLookup(keys, unnamedEntities20).then((_) {
349 return delete(keys, transactional: false);
350 });
351 });
352 });
353
354 test('lookup_with_all_properties', () {
355 return insert(entitiesWithAllPropertyTypes, [], transactional: false)
356 .then((_) {
357 var keys = entitiesWithAllPropertyTypes.map((e) => e.key).toList();
358 return testLookup(keys, entitiesWithAllPropertyTypes).then((_) {
359 return delete(keys, transactional: false);
360 });
361 });
362 });
363
364 test('lookup_transactional', () {
365 return insert([], unnamedEntities1).then((keys) {
366 keys.forEach((key) => expect(isValidKey(key), isTrue));
367 return testLookup(keys, unnamedEntities1, transactional: true)
368 .then((_) => delete(keys));
369 });
370 });
371
372 test('lookup_transactional_xg', () {
373 return insert([], unnamedEntities5).then((keys) {
374 keys.forEach((key) => expect(isValidKey(key), isTrue));
375 return testLookup(
376 keys, unnamedEntities5, transactional: true, xg: true).then((_) {
377 return delete(keys);
378 });
379 });
380 });
381
382 // TODO: ancestor lookups, string id lookups
383 });
384
385 group('delete', () {
386 Future testDelete(List<Key> keys,
387 {bool transactional: false, bool xg: false}) {
388 Future test(Transaction transaction) {
389 return datastore.commit(deletes: keys).then((_) {
390 if (transaction != null) {
391 return datastore.commit(transaction: transaction);
392 }
393 });
394 }
395
396 if (transactional) {
397 return withTransaction(test, xg: xg);
398 }
399 return test(null);
400 }
401
402 var unnamedEntities1 = buildEntities(42, 43);
403 var unnamedEntities5 = buildEntities(1, 6);
404 var unnamedEntities99 = buildEntities(6, 106);
405
406 test('delete', () {
407 return insert([], unnamedEntities99, transactional: false).then((keys) {
408 keys.forEach((key) => expect(isValidKey(key), isTrue));
409 return lookup(keys, transactional: false).then((entities) {
410 entities.forEach((e) => expect(e, isNotNull));
411 return testDelete(keys).then((_) {
412 return lookup(keys, transactional: false).then((entities) {
413 entities.forEach((e) => expect(e, isNull));
414 });
415 });
416 });
417 });
418 });
419
420 // This should not work with [unamedEntities20], but is working!
421 // FIXME TODO FIXME : look into this.
422 test('delete_transactional', () {
423 return insert([], unnamedEntities99, transactional: false).then((keys) {
424 keys.forEach((key) => expect(isValidKey(key), isTrue));
425 return lookup(keys, transactional: false).then((entities) {
426 entities.forEach((e) => expect(e, isNotNull));
427 return testDelete(keys, transactional: true).then((_) {
428 return lookup(keys, transactional: false).then((entities) {
429 entities.forEach((e) => expect(e, isNull));
430 });
431 });
432 });
433 });
434 });
435
436 test('delete_transactional_xg', () {
437 return insert([], unnamedEntities99, transactional: false).then((keys) {
438 keys.forEach((key) => expect(isValidKey(key), isTrue));
439 return lookup(keys, transactional: false).then((entities) {
440 expect(entities.length, equals(unnamedEntities99.length));
441 entities.forEach((e) => expect(e, isNotNull));
442 return testDelete(keys, transactional: true, xg: true).then((_) {
443 return lookup(keys, transactional: false).then((entities) {
444 expect(entities.length, equals(unnamedEntities99.length));
445 entities.forEach((e) => expect(e, isNull));
446 });
447 });
448 });
449 });
450 });
451
452 // TODO: ancestor deletes, string id deletes
453 });
454
455 group('rollback', () {
456 Future testRollback(List<Key> keys, {bool xg: false}) {
457 return withTransaction((Transaction transaction) {
458 return datastore.lookup(keys, transaction: transaction)
459 .then((List<Entity> entitites) {
460 return datastore.rollback(transaction);
461 });
462 }, xg: xg);
463 }
464
465 var namedEntities1 = buildEntities(42, 43, idFunction: (i) => "i$i");
466 var namedEntities5 = buildEntities(1, 6, idFunction: (i) => "i$i");
467
468 var namedEntities1Keys = namedEntities1.map((e) => e.key).toList();
469 var namedEntities5Keys = namedEntities5.map((e) => e.key).toList();
470
471 test('rollback', () {
472 return testRollback(namedEntities1Keys);
473 });
474
475 test('rollback_xg', () {
476 return testRollback(namedEntities5Keys, xg: true);
477 });
478 });
479
480 group('empty_commit', () {
481 Future testEmptyCommit(
482 List<Key> keys, {bool transactional: false, bool xg: false}) {
483 Future test(Transaction transaction) {
484 return datastore.lookup(keys, transaction: transaction)
485 .then((List<Entity> entitites) {
486 return datastore.commit(transaction: transaction);
487 });
488 }
489
490 if (transactional) {
491 return withTransaction(test, xg: xg);
492 } else {
493 return test(null);
494 }
495 }
496
497 var namedEntities1 = buildEntities(42, 43, idFunction: (i) => "i$i");
498 var namedEntities5 = buildEntities(1, 6, idFunction: (i) => "i$i");
499 var namedEntities20 = buildEntities(6, 26, idFunction: (i) => "i$i");
500
501 var namedEntities1Keys = namedEntities1.map((e) => e.key).toList();
502 var namedEntities5Keys = namedEntities5.map((e) => e.key).toList();
503 var namedEntities20Keys = namedEntities20.map((e) => e.key).toList();
504
505 test('empty_commit', () {
506 return testEmptyCommit(namedEntities20Keys);
507 });
508
509 test('empty_commit_transactional', () {
510 return testEmptyCommit(namedEntities1Keys);
511 });
512
513 test('empty_commit_transactional_xg', () {
514 return testEmptyCommit(namedEntities5Keys);
515 });
516
517 test('negative_empty_commit_xg', () {
518 expect(testEmptyCommit(
519 namedEntities20Keys, transactional: true, xg: true),
520 throwsA(isApplicationError));
521 });
522 });
523
524 group('conflicting_transaction', () {
525 Future testConflictingTransaction(
526 List<Entity> entities, {bool xg: false}) {
527 Future test(
528 List<Entity> entities, Transaction transaction, value) {
529
530 // Change entities:
531 var changedEntities = new List<Entity>(entities.length);
532 for (int i = 0; i < entities.length; i++) {
533 var entity = entities[i];
534 var newProperties = new Map.from(entity.properties);
535 for (var prop in newProperties.keys) {
536 newProperties[prop] = "${newProperties[prop]}conflict$value";
537 }
538 changedEntities[i] =
539 new Entity(entity.key, newProperties);
540 }
541 return datastore.commit(inserts: changedEntities,
542 transaction: transaction);
543 }
544
545 // Insert first
546 return insert(entities, [], transactional: true).then((_) {
547 var keys = entities.map((e) => e.key).toList();
548
549 var NUM_TRANSACTIONS = 10;
550
551 // Start transactions
552 var transactions = [];
553 for (var i = 0; i < NUM_TRANSACTIONS; i++) {
554 transactions.add(datastore.beginTransaction(crossEntityGroup: xg));
555 }
556 return Future.wait(transactions)
557 .then((List<Transaction> transactions) {
558 // Do a lookup for the entities in every transaction
559 var lookups = [];
560 for (var transaction in transactions) {
561 lookups.add(
562 datastore.lookup(keys, transaction: transaction));
563 }
564 return Future.wait(lookups).then((List<List<Entity>> results) {
565 // Do a conflicting commit in every transaction.
566 var commits = [];
567 for (var i = 0; i < transactions.length; i++) {
568 var transaction = transactions[i];
569 commits.add(test(results[i], transaction, i));
570 }
571 return Future.wait(commits);
572 });
573 });
574 });
575 }
576
577 var namedEntities1 = buildEntities(42, 43, idFunction: (i) => "i$i");
578 var namedEntities5 = buildEntities(1, 6, idFunction: (i) => "i$i");
579
580 test('conflicting_transaction', () {
581 expect(testConflictingTransaction(namedEntities1),
582 throwsA(isTransactionAbortedError));
583 });
584
585 test('conflicting_transaction_xg', () {
586 expect(testConflictingTransaction(namedEntities5, xg: true),
587 throwsA(isTransactionAbortedError));
588 });
589 });
590
591 group('query', () {
592 Future testQuery(String kind,
593 {List<Filter> filters,
594 List<Order> orders,
595 bool transactional: false,
596 bool xg: false,
597 int offset,
598 int limit}) {
599 Future<List<Entity>> test(Transaction transaction) {
600 var query = new Query(
601 kind: kind, filters: filters, orders: orders,
602 offset: offset, limit: limit);
603 return consumePages((_) => datastore.query(query))
604 .then((List<Entity> entities) {
605 if (transaction != null) {
606 return datastore.commit(transaction: transaction)
607 .then((_) => entities);
608 }
609 return entities;
610 });
611 }
612
613 if (transactional) {
614 return withTransaction(test, xg: xg);
615 }
616 return test(null);
617 }
618
619 Future testQueryAndCompare(String kind,
620 List<Entity> expectedEntities,
621 {List<Filter> filters,
622 List<Order> orders,
623 bool transactional: false,
624 bool xg: false,
625 bool correctOrder: true,
626 int offset,
627 int limit}) {
628 return testQuery(kind,
629 filters: filters,
630 orders: orders,
631 transactional: transactional,
632 xg: xg,
633 offset: offset,
634 limit: limit).then((List<Entity> entities) {
635 expect(entities.length, equals(expectedEntities.length));
636
637 if (correctOrder) {
638 for (int i = 0; i < entities.length; i++) {
639 expect(compareEntity(entities[i], expectedEntities[i]), isTrue);
640 }
641 } else {
642 for (int i = 0; i < entities.length; i++) {
643 bool found = false;
644 for (int j = 0; j < expectedEntities.length; j++) {
645 if (compareEntity(entities[i], expectedEntities[i])) {
646 found = true;
647 }
648 }
649 expect(found, isTrue);
650 }
651 }
652 });
653 }
654 Future testOffsetLimitQuery(String kind,
655 List<Entity> expectedEntities,
656 {List<Order> orders,
657 bool transactional: false,
658 bool xg: false}) {
659 // We query for all subsets of expectedEntities
660 // NOTE: This is O(0.5 * n^2) queries, but n is currently only 6.
661 List<Function> queryTests = [];
662 for (int start = 0; start < expectedEntities.length; start++) {
663 for (int end = start; end < expectedEntities.length; end++) {
664 int offset = start;
665 int limit = end - start;
666 var entities = expectedEntities.sublist(offset, offset + limit);
667 queryTests.add(() {
668 return testQueryAndCompare(
669 kind, entities, transactional: transactional,
670 xg: xg, orders: orders,
671 offset: offset, limit: limit);
672 });
673 }
674 }
675 // Query with limit higher than the number of results.
676 queryTests.add(() {
677 return testQueryAndCompare(
678 kind, expectedEntities, transactional: transactional,
679 xg: xg, orders: orders,
680 offset: 0, limit: expectedEntities.length * 10);
681 });
682
683 return Future.forEach(queryTests, (f) => f());
684 }
685
686 const TEST_QUERY_KIND = 'TestQueryKind';
687 var stringNamedEntities = buildEntities(
688 1, 6, idFunction: (i) => 'str$i', kind: TEST_QUERY_KIND);
689 var stringNamedKeys = stringNamedEntities.map((e) => e.key).toList();
690
691 var QUERY_KEY = TEST_PROPERTY_KEY_PREFIX;
692 var QUERY_UPPER_BOUND = "${TEST_PROPERTY_VALUE_PREFIX}4";
693 var QUERY_LOWER_BOUND = "${TEST_PROPERTY_VALUE_PREFIX}1";
694 var QUERY_LIST_ENTRY = '${TEST_LIST_VALUE}2';
695 var QUERY_INDEX_VALUE = '${TEST_INDEXED_PROPERTY_VALUE_PREFIX}1';
696
697 var reverseOrderFunction = (Entity a, Entity b) {
698 // Reverse the order
699 return -1 * (a.properties[QUERY_KEY] as String)
700 .compareTo(b.properties[QUERY_KEY]);
701 };
702
703 var filterFunction = (Entity entity) {
704 var value = entity.properties[QUERY_KEY];
705 return value.compareTo(QUERY_UPPER_BOUND) == -1 &&
706 value.compareTo(QUERY_LOWER_BOUND) == 1;
707 };
708 var listFilterFunction = (Entity entity) {
709 var values = entity.properties[TEST_LIST_PROPERTY];
710 return values.contains(QUERY_LIST_ENTRY);
711 };
712 var indexFilterMatches = (Entity entity) {
713 return entity.properties[TEST_INDEXED_PROPERTY] == QUERY_INDEX_VALUE;
714 };
715
716 var sorted = stringNamedEntities.toList()..sort(reverseOrderFunction);
717 var filtered = stringNamedEntities.where(filterFunction).toList();
718 var sortedAndFiltered = sorted.where(filterFunction).toList();
719 var sortedAndListFiltered = sorted.where(listFilterFunction).toList();
720 var indexedEntity = sorted.where(indexFilterMatches).toList();
721 expect(indexedEntity.length, equals(1));
722
723 var filters = [
724 new Filter(FilterRelation.GreatherThan, QUERY_KEY, QUERY_LOWER_BOUND),
725 new Filter(FilterRelation.LessThan, QUERY_KEY, QUERY_UPPER_BOUND),
726 ];
727 var listFilters = [
728 new Filter(FilterRelation.In, TEST_LIST_PROPERTY, [QUERY_LIST_ENTRY])
729 ];
730 var indexedPropertyFilter = [
731 new Filter(FilterRelation.Equal,
732 TEST_INDEXED_PROPERTY,
733 QUERY_INDEX_VALUE),
734 new Filter(FilterRelation.Equal,
735 TEST_BLOB_INDEXED_PROPERTY,
736 TEST_BLOB_INDEXED_VALUE)
737 ];
738 var unIndexedPropertyFilter = [
739 new Filter(FilterRelation.Equal,
740 TEST_UNINDEXED_PROPERTY,
741 QUERY_INDEX_VALUE)
742 ];
743
744 var orders = [new Order(OrderDirection.Decending, QUERY_KEY)];
745
746 test('query', () {
747 return insert(stringNamedEntities, []).then((keys) {
748 return waitUntilEntitiesReady(datastore, stringNamedKeys).then((_) {
749 var tests = [
750 // EntityKind query
751 () => testQueryAndCompare(
752 TEST_QUERY_KIND, stringNamedEntities, transactional: false,
753 correctOrder: false),
754 () => testQueryAndCompare(
755 TEST_QUERY_KIND, stringNamedEntities, transactional: true,
756 correctOrder: false),
757 () => testQueryAndCompare(
758 TEST_QUERY_KIND, stringNamedEntities, transactional: true,
759 correctOrder: false, xg: true),
760
761 // EntityKind query with order
762 () => testQueryAndCompare(
763 TEST_QUERY_KIND, sorted, transactional: false,
764 orders: orders),
765 () => testQueryAndCompare(
766 TEST_QUERY_KIND, sorted, transactional: true,
767 orders: orders),
768 () => testQueryAndCompare(
769 TEST_QUERY_KIND, sorted, transactional: false, xg: true,
770 orders: orders),
771
772 // EntityKind query with filter
773 () => testQueryAndCompare(
774 TEST_QUERY_KIND, filtered, transactional: false,
775 filters: filters),
776 () => testQueryAndCompare(
777 TEST_QUERY_KIND, filtered, transactional: true,
778 filters: filters),
779 () => testQueryAndCompare(
780 TEST_QUERY_KIND, filtered, transactional: false, xg: true,
781 filters: filters),
782
783 // EntityKind query with filter + order
784 () => testQueryAndCompare(
785 TEST_QUERY_KIND, sortedAndFiltered, transactional: false,
786 filters: filters, orders: orders),
787 () => testQueryAndCompare(
788 TEST_QUERY_KIND, sortedAndFiltered, transactional: true,
789 filters: filters, orders: orders),
790 () => testQueryAndCompare(
791 TEST_QUERY_KIND, sortedAndFiltered, transactional: false,
792 xg: true, filters: filters, orders: orders),
793
794 // EntityKind query with IN filter + order
795 () => testQueryAndCompare(
796 TEST_QUERY_KIND, sortedAndListFiltered, transactional: false,
797 filters: listFilters, orders: orders),
798 () => testQueryAndCompare(
799 TEST_QUERY_KIND, sortedAndListFiltered, transactional: true,
800 filters: listFilters, orders: orders),
801 () => testQueryAndCompare(
802 TEST_QUERY_KIND, sortedAndListFiltered, transactional: false,
803 xg: true, filters: listFilters, orders: orders),
804
805 // Limit & Offset test
806 () => testOffsetLimitQuery(
807 TEST_QUERY_KIND, sorted, transactional: false,
808 orders: orders),
809 () => testOffsetLimitQuery(
810 TEST_QUERY_KIND, sorted, transactional: true, orders: orders),
811 () => testOffsetLimitQuery(
812 TEST_QUERY_KIND, sorted, transactional: false,
813 xg: true, orders: orders),
814
815 // Query for indexed property
816 () => testQueryAndCompare(
817 TEST_QUERY_KIND, indexedEntity, transactional: false,
818 filters: indexedPropertyFilter),
819 () => testQueryAndCompare(
820 TEST_QUERY_KIND, indexedEntity, transactional: true,
821 filters: indexedPropertyFilter),
822 () => testQueryAndCompare(
823 TEST_QUERY_KIND, indexedEntity, transactional: false,
824 xg: true, filters: indexedPropertyFilter),
825
826 // Query for un-indexed property
827 () => testQueryAndCompare(
828 TEST_QUERY_KIND, [], transactional: false,
829 filters: unIndexedPropertyFilter),
830 () => testQueryAndCompare(
831 TEST_QUERY_KIND, [], transactional: true,
832 filters: unIndexedPropertyFilter),
833 () => testQueryAndCompare(
834 TEST_QUERY_KIND, [], transactional: false,
835 xg: true, filters: unIndexedPropertyFilter),
836
837 // Delete results
838 () => delete(stringNamedKeys, transactional: true),
839
840 // Wait until the entity deletes are reflected in the indices.
841 () => waitUntilEntitiesGone(datastore, stringNamedKeys),
842
843 // Make sure queries don't return results
844 () => testQueryAndCompare(
845 TEST_QUERY_KIND, [], transactional: false),
846 () => testQueryAndCompare(
847 TEST_QUERY_KIND, [], transactional: true),
848 () => testQueryAndCompare(
849 TEST_QUERY_KIND, [], transactional: true, xg: true),
850 () => testQueryAndCompare(
851 TEST_QUERY_KIND, [], transactional: false,
852 filters: filters, orders: orders),
853 ];
854 return Future.forEach(tests, (f) => f());
855 });
856 });
857
858 // TODO: query by multiple keys, multiple sort oders, ...
859 });
860
861 test('ancestor_query', () {
862 /*
863 * This test creates an
864 * RootKind:1 -- This defines the entity group (no entity with that key)
865 * + SubKind:1 -- This a subpath (no entity with that key)
866 * + SubSubKind:1 -- This is a real entity of kind SubSubKind
867 * + SubSubKind2:1 -- This is a real entity of kind SubSubKind2
868 */
869 var rootKey = new Key.fromParent('RootKind', 1);
870 var subKey = new Key.fromParent('SubKind', 1, parent: rootKey);
871 var subSubKey = new Key.fromParent('SubSubKind', 1, parent: subKey);
872 var subSubKey2 = new Key.fromParent('SubSubKind2', 1, parent: subKey);
873 var properties = { 'foo' : 'bar' };
874
875 var entity = new Entity(subSubKey, properties);
876 var entity2 = new Entity(subSubKey2, properties);
877
878 var orders = [new Order(OrderDirection.Ascending, '__key__')];
879
880 return datastore.commit(inserts: [entity, entity2]).then((_) {
881 var futures = [
882 // FIXME/TODO: Ancestor queries should be strongly consistent.
883 // We should not need to wait for them.
884 () {
885 return waitUntilEntitiesReady(datastore, [subSubKey, subSubKey2]);
886 },
887 // Test that lookup only returns inserted entities.
888 () {
889 return datastore.lookup([rootKey, subKey, subSubKey, subSubKey2])
890 .then((List<Entity> entities) {
891 expect(entities.length, 4);
892 expect(entities[0], isNull);
893 expect(entities[1], isNull);
894 expect(entities[2], isNotNull);
895 expect(entities[3], isNotNull);
896 expect(compareEntity(entity, entities[2]), isTrue);
897 expect(compareEntity(entity2, entities[3]), isTrue);
898 });
899 },
900
901 // Query by ancestor.
902 // - by [rootKey]
903 () {
904 var ancestorQuery =
905 new Query(ancestorKey: rootKey, orders: orders);
906 return consumePages((_) => datastore.query(ancestorQuery))
907 .then((results) {
908 expect(results.length, 2);
909 expect(compareEntity(entity, results[0]), isTrue);
910 expect(compareEntity(entity2, results[1]), isTrue);
911 });
912 },
913 // - by [subKey]
914 () {
915 var ancestorQuery =
916 new Query(ancestorKey: subKey, orders: orders);
917 return consumePages((_) => datastore.query(ancestorQuery))
918 .then((results) {
919 expect(results.length, 2);
920 expect(compareEntity(entity, results[0]), isTrue);
921 expect(compareEntity(entity2, results[1]), isTrue);
922 });
923 },
924 // - by [subSubKey]
925 () {
926 var ancestorQuery = new Query(ancestorKey: subSubKey);
927 return consumePages((_) => datastore.query(ancestorQuery))
928 .then((results) {
929 expect(results.length, 1);
930 expect(compareEntity(entity, results[0]), isTrue);
931 });
932 },
933 // - by [subSubKey2]
934 () {
935 var ancestorQuery = new Query(ancestorKey: subSubKey2);
936 return consumePages((_) => datastore.query(ancestorQuery))
937 .then((results) {
938 expect(results.length, 1);
939 expect(compareEntity(entity2, results[0]), isTrue);
940 });
941 },
942
943 // Query by ancestor and kind.
944 // - by [rootKey] + 'SubSubKind'
945 () {
946 var query = new Query(ancestorKey: rootKey, kind: 'SubSubKind');
947 return consumePages((_) => datastore.query(query))
948 .then((List<Entity> results) {
949 expect(results.length, 1);
950 expect(compareEntity(entity, results[0]), isTrue);
951 });
952 },
953 // - by [rootKey] + 'SubSubKind2'
954 () {
955 var query = new Query(ancestorKey: rootKey, kind: 'SubSubKind2');
956 return consumePages((_) => datastore.query(query))
957 .then((List<Entity> results) {
958 expect(results.length, 1);
959 expect(compareEntity(entity2, results[0]), isTrue);
960 });
961 },
962 // - by [subSubKey] + 'SubSubKind'
963 () {
964 var query = new Query(ancestorKey: subSubKey, kind: 'SubSubKind');
965 return consumePages((_) => datastore.query(query))
966 .then((List<Entity> results) {
967 expect(results.length, 1);
968 expect(compareEntity(entity, results[0]), isTrue);
969 });
970 },
971 // - by [subSubKey2] + 'SubSubKind2'
972 () {
973 var query =
974 new Query(ancestorKey: subSubKey2, kind: 'SubSubKind2');
975 return consumePages((_) => datastore.query(query))
976 .then((List<Entity> results) {
977 expect(results.length, 1);
978 expect(compareEntity(entity2, results[0]), isTrue);
979 });
980 },
981 // - by [subSubKey] + 'SubSubKind2'
982 () {
983 var query =
984 new Query(ancestorKey: subSubKey, kind: 'SubSubKind2');
985 return consumePages((_) => datastore.query(query))
986 .then((List<Entity> results) {
987 expect(results.length, 0);
988 });
989 },
990 // - by [subSubKey2] + 'SubSubKind'
991 () {
992 var query =
993 new Query(ancestorKey: subSubKey2, kind: 'SubSubKind');
994 return consumePages((_) => datastore.query(query))
995 .then((List<Entity> results) {
996 expect(results.length, 0);
997 });
998 },
999
1000 // Cleanup
1001 () {
1002 return datastore.commit(deletes: [subSubKey, subSubKey2]);
1003 }
1004 ];
1005 return Future.forEach(futures, (f) => f()).then(expectAsync((_) {}));
1006 });
1007 });
1008 });
1009 });
1010 }
1011
1012 Future cleanupDB(Datastore db) {
1013 Future<List<String>> getNamespaces() {
1014 var q = new Query(kind: '__namespace__');
1015 return consumePages((_) => db.query(q)).then((List<Entity> entities) {
1016 return entities.map((Entity e) {
1017 var id = e.key.elements.last.id;
1018 if (id == 1) return null;
1019 return id;
1020 }).toList();
1021 });
1022 }
1023
1024 Future<List<String>> getKinds(String namespace) {
1025 var partition = new Partition(namespace);
1026 var q = new Query(kind: '__kind__');
1027 return consumePages((_) => db.query(q, partition: partition))
1028 .then((List<Entity> entities) {
1029 return entities
1030 .map((Entity e) => e.key.elements.last.id)
1031 .where((String kind) => !kind.contains('__'))
1032 .toList();
1033 });
1034 }
1035
1036 // cleanup() will call itself again as long as the DB is not clean.
1037 cleanup(String namespace, String kind) {
1038 var partition = new Partition(namespace);
1039 var q = new Query(kind: kind, limit: 500);
1040 return consumePages((_) => db.query(q, partition: partition))
1041 .then((List<Entity> entities) {
1042 if (entities.length == 0) return null;
1043
1044 print('[cleanupDB]: Removing left-over ${entities.length} entities');
1045 var deletes = entities.map((e) => e.key).toList();
1046 return db.commit(deletes: deletes).then((_) => cleanup(namespace, kind));
1047 });
1048 }
1049
1050 return getNamespaces().then((List<String> namespaces) {
1051 return Future.forEach(namespaces, (String namespace) {
1052 return getKinds(namespace).then((List<String> kinds) {
1053 return Future.forEach(kinds, (String kind) {
1054 return cleanup(namespace, kind);
1055 });
1056 });
1057 });
1058 });
1059 }
1060
1061 Future waitUntilEntitiesReady(Datastore db, List<Key> keys) {
1062 return waitUntilEntitiesHelper(db, keys, true);
1063 }
1064
1065 Future waitUntilEntitiesGone(Datastore db, List<Key> keys) {
1066 return waitUntilEntitiesHelper(db, keys, false);
1067 }
1068
1069 Future waitUntilEntitiesHelper(Datastore db, List<Key> keys, bool positive) {
1070 var keysByKind = {};
1071 for (var key in keys) {
1072 keysByKind.putIfAbsent(key.elements.last.kind, () => []).add(key);
1073 }
1074
1075 Future waitForKeys(String kind, List<Key> keys) {
1076 var q = new Query(kind: kind);
1077 return consumePages((_) => db.query(q)).then((entities) {
1078 for (var key in keys) {
1079 bool found = false;
1080 for (var entity in entities) {
1081 if (key == entity.key) found = true;
1082 }
1083 if (positive) {
1084 if (!found) return waitForKeys(kind, keys);
1085 } else {
1086 if (found) return waitForKeys(kind, keys);
1087 }
1088 }
1089 return null;
1090 });
1091 }
1092
1093 return Future.forEach(keysByKind.keys.toList(), (String kind) {
1094 return waitForKeys(kind, keysByKind[kind]);
1095 });
1096 }
1097
1098 main() {
1099 var scopes = datastore_impl.DatastoreImpl.SCOPES;
1100
1101 withAuthClient(scopes, (String project, httpClient) {
1102 var datastore = new datastore_impl.DatastoreImpl(httpClient, 's~$project');
1103 return cleanupDB(datastore).then((_) {
1104 return runE2EUnittest(() => runTests(datastore));
1105 });
1106 });
1107 }
OLDNEW
« no previous file with comments | « pkg/gcloud/test/common_e2e.dart ('k') | pkg/gcloud/test/datastore/e2e/utils.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698