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

Side by Side Diff: pkg/gcloud/test/db/e2e/db_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 db_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: User
12 /// ancestor: no
13 /// properties:
14 /// - name: name
15 /// direction: asc
16 /// - name: nickname
17 /// direction: desc
18 ///
19 /// - kind: User
20 /// ancestor: no
21 /// properties:
22 /// - name: name
23 /// direction: desc
24 /// - name: nickname
25 /// direction: desc
26 ///
27 /// - kind: User
28 /// ancestor: no
29 /// properties:
30 /// - name: name
31 /// direction: desc
32 /// - name: nickname
33 /// direction: asc
34 ///
35 /// - kind: User
36 /// ancestor: no
37 /// properties:
38 /// - name: language
39 /// direction: asc
40 /// - name: name
41 /// direction: asc
42 ///
43 /// $ gcloud preview datastore create-indexes .
44 /// 02:19 PM Host: appengine.google.com
45 /// 02:19 PM Uploading index definitions.
46
47 import 'dart:async';
48
49 import 'package:unittest/unittest.dart';
50 import 'package:gcloud/db.dart' as db;
51 import 'package:gcloud/src/datastore_impl.dart' as datastore_impl;
52
53 import '../../datastore/e2e/datastore_test_impl.dart' as datastore_test;
54 import '../../common_e2e.dart';
55
56 @db.Kind()
57 class Person extends db.Model {
58 @db.StringProperty()
59 String name;
60
61 @db.IntProperty()
62 int age;
63
64 @db.ModelKeyProperty()
65 db.Key wife;
66
67 operator==(Object other) => sameAs(other);
68
69 sameAs(Object other) {
70 return other is Person &&
71 id == other.id &&
72 parentKey == other.parentKey &&
73 name == other.name &&
74 age == other.age &&
75 wife == other.wife;
76 }
77
78 String toString() => 'Person(id: $id, name: $name, age: $age)';
79 }
80
81
82 @db.Kind()
83 class User extends Person {
84 @db.StringProperty()
85 String nickname;
86
87 @db.StringListProperty(propertyName: 'language')
88 List<String> languages = const [];
89
90 sameAs(Object other) {
91 if (!(super.sameAs(other) && other is User && nickname == other.nickname))
92 return false;
93
94 User user = other;
95 if (languages == null) {
96 if (user.languages == null) return true;
97 return false;
98 }
99 if (languages.length != user.languages.length) {
100 return false;
101 }
102
103 for (int i = 0; i < languages.length; i++) {
104 if (languages[i] != user.languages[i]) {
105 return false;
106 }
107 }
108 return true;
109 }
110
111 String toString() =>
112 'User(${super.toString()}, nickname: $nickname, languages: $languages';
113 }
114
115
116 @db.Kind()
117 class ExpandoPerson extends db.ExpandoModel {
118 @db.StringProperty()
119 String name;
120
121 @db.StringProperty(propertyName: 'NN')
122 String nickname;
123
124 operator==(Object other) {
125 if (other is ExpandoPerson && id == other.id && name == other.name) {
126 if (additionalProperties.length != other.additionalProperties.length) {
127 return false;
128 }
129 for (var key in additionalProperties.keys) {
130 if (additionalProperties[key] != other.additionalProperties[key]) {
131 return false;
132 }
133 }
134 return true;
135 }
136 return false;
137 }
138 }
139
140
141 Future sleep(Duration duration) {
142 var completer = new Completer();
143 new Timer(duration, completer.complete);
144 return completer.future;
145 }
146
147 runTests(db.DatastoreDB store) {
148 void compareModels(List<db.Model> expectedModels,
149 List<db.Model> models,
150 {bool anyOrder: false}) {
151 expect(models.length, equals(expectedModels.length));
152 if (anyOrder) {
153 // Do expensive O(n^2) search.
154 for (var searchModel in expectedModels) {
155 bool found = false;
156 for (var m in models) {
157 if (m == searchModel) {
158 found = true;
159 break;
160 }
161 }
162 expect(found, isTrue);
163 }
164 } else {
165 for (var i = 0; i < expectedModels.length; i++) {
166 expect(models[i], equals(expectedModels[i]));
167 }
168 }
169 }
170
171 Future testInsertLookupDelete(
172 List<db.Model> objects, {bool transactional: false}) {
173 var keys = objects.map((db.Model obj) => obj.key).toList();
174
175 if (transactional) {
176 return store.withTransaction((db.Transaction commitTransaction) {
177 commitTransaction.queueMutations(inserts: objects);
178 return commitTransaction.commit();
179 }).then((_) {
180 return store.withTransaction((db.Transaction deleteTransaction) {
181 return deleteTransaction.lookup(keys).then((List<db.Model> models) {
182 compareModels(objects, models);
183 deleteTransaction.queueMutations(deletes: keys);
184 return deleteTransaction.commit();
185 });
186 });
187 });
188 } else {
189 return store.commit(inserts: objects).then(expectAsync((_) {
190 return store.lookup(keys).then(expectAsync((List<db.Model> models) {
191 compareModels(objects, models);
192 return store.commit(deletes: keys).then(expectAsync((_) {
193 return store.lookup(keys).then(expectAsync((List<db.Model> models) {
194 for (var i = 0; i < models.length; i++) {
195 expect(models[i], isNull);
196 }
197 }));
198 }));
199 }));
200 }));
201 }
202 }
203
204 group('key', () {
205 test('equal_and_hashcode', () {
206 var k1 = store.emptyKey.append(User, id: 10).append(Person, id: 12);
207 var k2 = store.newPartition(null)
208 .emptyKey.append(User, id: 10).append(Person, id: 12);
209 expect(k1, equals(k2));
210 expect(k1.hashCode, equals(k2.hashCode));
211 });
212 });
213
214 group('e2e_db', () {
215 group('insert_lookup_delete', () {
216 test('persons', () {
217 var root = store.emptyKey;
218 var persons = [];
219 for (var i = 1; i <= 10; i++) {
220 persons.add(new Person()
221 ..id = i
222 ..parentKey = root
223 ..age = 42 + i
224 ..name = 'user$i');
225 }
226 persons.first.wife = persons.last.key;
227 return testInsertLookupDelete(persons);
228 });
229 test('users', () {
230 var root = store.emptyKey;
231 var users = [];
232 for (var i = 1; i <= 10; i++) {
233 users.add(new User()
234 ..id = i
235 ..parentKey = root
236 ..age = 42 + i
237 ..name = 'user$i'
238 ..nickname = 'nickname${i%3}');
239 }
240 return testInsertLookupDelete(users);
241 });
242 test('expando_insert', () {
243 var root = store.emptyKey;
244 var expandoPersons = [];
245 for (var i = 1; i <= 10; i++) {
246 var expandoPerson = new ExpandoPerson()
247 ..parentKey = root
248 ..id = i
249 ..name = 'user$i';
250 expandoPerson.foo = 'foo$i';
251 expandoPerson.bar = i;
252 expect(expandoPerson.additionalProperties['foo'], equals('foo$i'));
253 expect(expandoPerson.additionalProperties['bar'], equals(i));
254 expandoPersons.add(expandoPerson);
255 }
256 return testInsertLookupDelete(expandoPersons);
257 });
258 test('transactional_insert', () {
259 var root = store.emptyKey;
260 var models = [];
261
262 models.add(new Person()
263 ..id = 1
264 ..parentKey = root
265 ..age = 1
266 ..name = 'user1');
267 models.add(new User()
268 ..id = 2
269 ..parentKey = root
270 ..age = 2
271 ..name = 'user2'
272 ..nickname = 'nickname2');
273 var expandoPerson = new ExpandoPerson()
274 ..parentKey = root
275 ..id = 3
276 ..name = 'user1';
277 expandoPerson.foo = 'foo1';
278 expandoPerson.bar = 2;
279
280 return testInsertLookupDelete(models, transactional: true);
281 });
282
283 test('parent_key', () {
284 var root = store.emptyKey;
285 var users = [];
286 for (var i = 333; i <= 334; i++) {
287 users.add(new User()
288 ..id = i
289 ..parentKey = root
290 ..age = 42 + i
291 ..name = 'user$i'
292 ..nickname = 'nickname${i%3}');
293 }
294 var persons = [];
295 for (var i = 335; i <= 336; i++) {
296 persons.add(new Person()
297 ..id = i
298 ..parentKey = root
299 ..age = 42 + i
300 ..name = 'person$i');
301 }
302
303 // We test that we can insert + lookup
304 // users[0], (persons[0] + users[0] as parent)
305 // persons[1], (users[1] + persons[0] as parent)
306 persons[0].parentKey = users[0].key;
307 users[1].parentKey = persons[1].key;
308
309 return testInsertLookupDelete([]..addAll(users)..addAll(persons));
310 });
311
312 test('auto_ids', () {
313 var root = store.emptyKey;
314 var persons = [];
315 persons.add(new Person()
316 ..id = 42
317 ..parentKey = root
318 ..age = 80
319 ..name = 'user80');
320 // Auto id person with parentKey
321 persons.add(new Person()
322 ..parentKey = root
323 ..age = 81
324 ..name = 'user81');
325 // Auto id person without parentKey
326 persons.add(new Person()
327 ..age = 82
328 ..name = 'user82');
329 // Auto id person with non-root parentKey
330 var fatherKey = persons.first.parentKey;
331 persons.add(new Person()
332 ..parentKey = fatherKey
333 ..age = 83
334 ..name = 'user83');
335 persons.add(new Person()
336 ..id = 43
337 ..parentKey = root
338 ..age = 84
339 ..name = 'user84');
340 return store.commit(inserts: persons).then(expectAsync((_) {
341 // At this point, autoIds are allocated and are relfected in the
342 // models (as well as parentKey if it was empty).
343
344 var keys = persons.map((db.Model obj) => obj.key).toList();
345
346 for (var i = 0; i < persons.length; i++) {
347 expect(persons[i].age, equals(80 + i));
348 expect(persons[i].name, equals('user${80 + i}'));
349 }
350
351 expect(persons[0].id, equals(42));
352 expect(persons[0].parentKey, equals(root));
353
354 expect(persons[1].id, isNotNull);
355 expect(persons[1].id is int, isTrue);
356 expect(persons[1].parentKey, equals(root));
357
358 expect(persons[2].id, isNotNull);
359 expect(persons[2].id is int, isTrue);
360 expect(persons[2].parentKey, equals(root));
361
362 expect(persons[3].id, isNotNull);
363 expect(persons[3].id is int, isTrue);
364 expect(persons[3].parentKey, equals(fatherKey));
365
366 expect(persons[4].id, equals(43));
367 expect(persons[4].parentKey, equals(root));
368
369 expect(persons[1].id != persons[2].id, isTrue);
370 // NOTE: We can't make assumptions about the id of persons[3],
371 // because an id doesn't need to be globally unique, only under
372 // entities with the same parent.
373
374 return store.lookup(keys).then(expectAsync((List<Person> models) {
375 // Since the id/parentKey fields are set after commit and a lookup
376 // returns new model instances, we can do full model comparision
377 // here.
378 compareModels(persons, models);
379 return store.commit(deletes: keys).then(expectAsync((_) {
380 return store.lookup(keys).then(expectAsync((List models) {
381 for (var i = 0; i < models.length; i++) {
382 expect(models[i], isNull);
383 }
384 }));
385 }));
386 }));
387 }));
388 });
389 });
390
391 test('query', () {
392 var root = store.emptyKey;
393 var users = [];
394 for (var i = 1; i <= 10; i++) {
395 var languages = [];
396 if (i == 9) {
397 languages = ['foo'];
398 } else if (i == 10) {
399 languages = ['foo', 'bar'];
400 }
401 users.add(new User()
402 ..id = i
403 ..parentKey = root
404 ..age = 42 + i
405 ..name = 'user$i'
406 ..nickname = 'nickname${i%3}'
407 ..languages = languages);
408 }
409
410 var expandoPersons = [];
411 for (var i = 1; i <= 3; i++) {
412 var expandoPerson = new ExpandoPerson()
413 ..parentKey = root
414 ..id = i
415 ..name = 'user$i'
416 ..nickname = 'nickuser$i';
417 expandoPerson.foo = 'foo$i';
418 expandoPerson.bar = i;
419 expect(expandoPerson.additionalProperties['foo'], equals('foo$i'));
420 expect(expandoPerson.additionalProperties['bar'], equals(i));
421 expandoPersons.add(expandoPerson);
422 }
423
424 var LOWER_BOUND = 'user2';
425
426 var usersSortedNameDescNicknameAsc = new List.from(users);
427 usersSortedNameDescNicknameAsc.sort((User a, User b) {
428 var result = b.name.compareTo(a.name);
429 if (result == 0) return a.nickname.compareTo(b.nickname);
430 return result;
431 });
432
433 var usersSortedNameDescNicknameDesc = new List.from(users);
434 usersSortedNameDescNicknameDesc.sort((User a, User b) {
435 var result = b.name.compareTo(a.name);
436 if (result == 0) return b.nickname.compareTo(a.nickname);
437 return result;
438 });
439
440 var usersSortedAndFilteredNameDescNicknameAsc =
441 usersSortedNameDescNicknameAsc.where((User u) {
442 return LOWER_BOUND.compareTo(u.name) <= 0;
443 }).toList();
444
445 var usersSortedAndFilteredNameDescNicknameDesc =
446 usersSortedNameDescNicknameDesc.where((User u) {
447 return LOWER_BOUND.compareTo(u.name) <= 0;
448 }).toList();
449
450 var fooUsers = users.where(
451 (User u) => u.languages.contains('foo')).toList();
452 var barUsers = users.where(
453 (User u) => u.languages.contains('bar')).toList();
454
455 var allInserts = []
456 ..addAll(users)
457 ..addAll(expandoPersons);
458 var allKeys = allInserts.map((db.Model model) => model.key).toList();
459 return store.commit(inserts: allInserts).then((_) {
460 return waitUntilEntitiesReady(store, allKeys).then((_) {
461 var tests = [
462 // Queries for [Person] return no results, we only have [User]
463 // objects.
464 () {
465 return store.query(Person).run().toList()
466 .then((List<db.Model> models) {
467 compareModels([], models);
468 });
469 },
470
471 // All users query
472 () {
473 return store.query(User).run().toList()
474 .then((List<db.Model> models) {
475 compareModels(users, models, anyOrder: true);
476 });
477 },
478
479 // Sorted query
480 () {
481 return store.query(User)
482 ..order('-name')
483 ..order('nickname')
484 ..run().toList().then((List<db.Model> models) {
485 compareModels(
486 usersSortedNameDescNicknameAsc, models);
487 });
488 },
489 () {
490 return store.query(User)
491 ..order('-name')
492 ..order('-nickname')
493 ..run().toList().then((List<db.Model> models) {
494 compareModels(
495 usersSortedNameDescNicknameDesc, models);
496 });
497 },
498
499 // Sorted query with filter
500 () {
501 return store.query(User)
502 ..filter('name >=', LOWER_BOUND)
503 ..order('-name')
504 ..order('nickname')
505 ..run().toList().then((List<db.Model> models) {
506 compareModels(usersSortedAndFilteredNameDescNicknameAsc,
507 models);
508 });
509 },
510 () {
511 return store.query(User)
512 ..filter('name >=', LOWER_BOUND)
513 ..order('-name')
514 ..order('-nickname')
515 ..run().toList().then((List<db.Model> models) {
516 compareModels(usersSortedAndFilteredNameDescNicknameDesc,
517 models);
518 });
519 },
520
521 // Filter lists
522 /* FIXME: TODO: FIXME: "IN" not supported in public proto/apiary */
523 () {
524 return store.query(User)
525 ..filter('languages IN', ['foo'])
526 ..order('name')
527 ..run().toList().then((List<db.Model> models) {
528 compareModels(fooUsers, models, anyOrder: true);
529 });
530 },
531 () {
532 return store.query(User)
533 ..filter('languages IN', ['bar'])
534 ..order('name')
535 ..run().toList().then((List<db.Model> models) {
536 compareModels(barUsers, models, anyOrder: true);
537 });
538 },
539
540 // Simple limit/offset test.
541 () {
542 return store.query(User)
543 ..order('-name')
544 ..order('nickname')
545 ..offset(3)
546 ..limit(4)
547 ..run().toList().then((List<db.Model> models) {
548 var expectedModels =
549 usersSortedAndFilteredNameDescNicknameAsc.sublist(3, 7);
550 compareModels(expectedModels, models);
551 });
552 },
553
554 // Expando queries: Filter on normal property.
555 () {
556 return store.query(ExpandoPerson)
557 ..filter('name =', expandoPersons.last.name)
558 ..run().toList().then((List<db.Model> models) {
559 compareModels([expandoPersons.last], models);
560 });
561 },
562 // Expando queries: Filter on expanded String property
563 () {
564 return store.query(ExpandoPerson)
565 ..filter('foo =', expandoPersons.last.foo)
566 ..run().toList().then((List<db.Model> models) {
567 compareModels([expandoPersons.last], models);
568 });
569 },
570 // Expando queries: Filter on expanded int property
571 () {
572 return store.query(ExpandoPerson)
573 ..filter('bar =', expandoPersons.last.bar)
574 ..run().toList().then((List<db.Model> models) {
575 compareModels([expandoPersons.last], models);
576 });
577 },
578 // Expando queries: Filter normal property with different
579 // propertyName (datastore name is 'NN').
580 () {
581 return store.query(ExpandoPerson)
582 ..filter('nickname =', expandoPersons.last.nickname)
583 ..run().toList().then((List<db.Model> models) {
584 compareModels([expandoPersons.last], models);
585 });
586 },
587
588 // Delete results
589 () => store.commit(deletes: allKeys),
590
591 // Wait until the entity deletes are reflected in the indices.
592 () => waitUntilEntitiesGone(store, allKeys),
593
594 // Make sure queries don't return results
595 () => store.lookup(allKeys).then((List<db.Model> models) {
596 expect(models.length, equals(allKeys.length));
597 for (var model in models) {
598 expect(model, isNull);
599 }
600 }),
601 ];
602 return Future.forEach(tests, (f) => f());
603 });
604 });
605 });
606 });
607 }
608
609 Future waitUntilEntitiesReady(db.DatastoreDB mdb, List<db.Key> keys) {
610 return waitUntilEntitiesHelper(mdb, keys, true);
611 }
612
613 Future waitUntilEntitiesGone(db.DatastoreDB mdb, List<db.Key> keys) {
614 return waitUntilEntitiesHelper(mdb, keys, false);
615 }
616
617 Future waitUntilEntitiesHelper(db.DatastoreDB mdb,
618 List<db.Key> keys,
619 bool positive) {
620 var keysByKind = {};
621 for (var key in keys) {
622 keysByKind.putIfAbsent(key.type, () => []).add(key);
623 }
624
625 Future waitForKeys(Type kind, List<db.Key> keys) {
626 return mdb.query(kind).run().toList().then((List<db.Model> models) {
627 for (var key in keys) {
628 bool found = false;
629 for (var model in models) {
630 if (key == model.key) found = true;
631 }
632 if (positive) {
633 if (!found) return waitForKeys(kind, keys);
634 } else {
635 if (found) return waitForKeys(kind, keys);
636 }
637 }
638 return null;
639 });
640 }
641
642 return Future.forEach(keysByKind.keys.toList(), (Type kind) {
643 return waitForKeys(kind, keysByKind[kind]);
644 });
645 }
646
647 main() {
648 var scopes = datastore_impl.DatastoreImpl.SCOPES;
649
650 withAuthClient(scopes, (String project, httpClient) {
651 var datastore = new datastore_impl.DatastoreImpl(httpClient, 's~$project');
652 return datastore_test.cleanupDB(datastore).then((_) {
653 return runE2EUnittest(() => runTests(new db.DatastoreDB(datastore)));
654 });
655 });
656 }
OLDNEW
« no previous file with comments | « pkg/gcloud/test/datastore/error_matchers.dart ('k') | pkg/gcloud/test/db/e2e/metamodel_test_impl.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698