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

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

Powered by Google App Engine
This is Rietveld 408576698