2020-01-30 13:08:18 -08:00
|
|
|
import 'dart:async';
|
|
|
|
|
2019-07-05 00:53:19 -07:00
|
|
|
@TestOn('!browser') // todo: Figure out why this doesn't run in js
|
|
|
|
|
2019-12-04 12:45:09 -08:00
|
|
|
// ignore_for_file: lines_longer_than_80_chars
|
|
|
|
|
2019-07-05 00:53:19 -07:00
|
|
|
/*
|
|
|
|
These tests don't work when compiled to js:
|
|
|
|
|
|
|
|
NoSuchMethodError: method not found: 'beginTransaction$0' (executor.beginTransaction$0 is not a function)
|
|
|
|
package:moor/src/runtime/database.dart 185:45 <fn>
|
|
|
|
org-dartlang-sdk:///sdk/lib/_internal/js_runtime/lib/async_patch.dart 313:19 _wrapJsFunctionForAsync.closure.$protected
|
|
|
|
org-dartlang-sdk:///sdk/lib/_internal/js_runtime/lib/async_patch.dart 338:23 _wrapJsFunctionForAsync.<fn>
|
|
|
|
package:stack_trace StackZoneSpecification._registerBinaryCallback.<fn>
|
|
|
|
org-dartlang-sdk:///sdk/lib/_internal/js_runtime/lib/async_patch.dart 242:3 Object._asyncStartSync
|
|
|
|
package:moor/src/runtime/database.dart 185:13 QueryEngine.transaction.<fn>
|
|
|
|
test/data/utils/mocks.dart 22:20 MockExecutor.<fn>
|
|
|
|
package:mockito/src/mock.dart 128:22 MockExecutor.noSuchMethod
|
|
|
|
*/
|
|
|
|
|
2019-09-26 13:52:20 -07:00
|
|
|
import 'package:test/test.dart';
|
2019-06-21 11:29:42 -07:00
|
|
|
import 'package:moor/moor.dart';
|
2019-03-10 04:38:53 -07:00
|
|
|
|
|
|
|
import 'data/tables/todos.dart';
|
|
|
|
import 'data/utils/mocks.dart';
|
|
|
|
|
|
|
|
void main() {
|
|
|
|
TodoDb db;
|
|
|
|
MockExecutor executor;
|
|
|
|
MockStreamQueries streamQueries;
|
|
|
|
|
|
|
|
setUp(() {
|
|
|
|
executor = MockExecutor();
|
|
|
|
streamQueries = MockStreamQueries();
|
2019-10-30 11:25:26 -07:00
|
|
|
|
|
|
|
final connection = createConnection(executor, streamQueries);
|
|
|
|
db = TodoDb.connect(connection);
|
2019-03-10 04:38:53 -07:00
|
|
|
});
|
|
|
|
|
2020-01-30 13:08:18 -08:00
|
|
|
test('streams in transactions are isolated and scoped', () async {
|
|
|
|
// create a database without mocked stream queries
|
|
|
|
db = TodoDb(MockExecutor());
|
|
|
|
|
|
|
|
Stream<int> stream;
|
|
|
|
|
|
|
|
final didSetUpStream = Completer<void>();
|
|
|
|
final makeUpdate = Completer<void>();
|
|
|
|
final complete = Completer<void>();
|
|
|
|
|
|
|
|
final transaction = db.transaction(() async {
|
|
|
|
stream = db
|
2020-02-17 11:43:42 -08:00
|
|
|
.customSelect(
|
2020-01-30 13:08:18 -08:00
|
|
|
'SELECT _mocked_',
|
|
|
|
readsFrom: {db.users},
|
|
|
|
)
|
|
|
|
.map((r) => r.readInt('_mocked_'))
|
|
|
|
.watchSingle();
|
|
|
|
didSetUpStream.complete();
|
|
|
|
|
|
|
|
await makeUpdate.future;
|
|
|
|
db.markTablesUpdated({db.users});
|
|
|
|
|
|
|
|
await complete.future;
|
|
|
|
});
|
|
|
|
|
|
|
|
final emittedValues = <dynamic>[];
|
|
|
|
var didComplete = false;
|
|
|
|
|
|
|
|
// wait for the transaction to setup the stream
|
|
|
|
await didSetUpStream.future;
|
|
|
|
stream.listen(emittedValues.add, onDone: () => didComplete = true);
|
|
|
|
|
|
|
|
// Stream should emit initial select
|
|
|
|
await pumpEventQueue();
|
|
|
|
expect(emittedValues, hasLength(1));
|
|
|
|
|
|
|
|
// update tables inside the transaction -> stream should emit another value
|
|
|
|
makeUpdate.complete();
|
|
|
|
await pumpEventQueue();
|
|
|
|
expect(emittedValues, hasLength(2));
|
|
|
|
|
|
|
|
// update tables outside of the transaction -> stream should NOT update
|
|
|
|
db.markTablesUpdated({db.users});
|
|
|
|
await pumpEventQueue();
|
|
|
|
expect(emittedValues, hasLength(2));
|
|
|
|
|
|
|
|
complete.complete();
|
|
|
|
await transaction;
|
|
|
|
expect(didComplete, isTrue, reason: 'Stream must complete');
|
|
|
|
});
|
|
|
|
|
|
|
|
test('stream queries terminate on exceptional transaction', () async {
|
|
|
|
Stream stream;
|
|
|
|
|
|
|
|
try {
|
2019-09-13 12:04:15 -07:00
|
|
|
await db.transaction(() async {
|
2020-01-30 13:08:18 -08:00
|
|
|
stream = db.select(db.users).watch();
|
|
|
|
throw Exception();
|
2019-03-10 04:38:53 -07:00
|
|
|
});
|
2020-01-30 13:08:18 -08:00
|
|
|
} on Exception {
|
|
|
|
// ignore
|
|
|
|
}
|
|
|
|
|
|
|
|
expect(stream, emitsDone);
|
2019-03-10 04:38:53 -07:00
|
|
|
});
|
|
|
|
|
2019-07-07 04:03:15 -07:00
|
|
|
test('nested transactions use the outer transaction', () async {
|
2019-09-13 12:04:15 -07:00
|
|
|
await db.transaction(() async {
|
|
|
|
await db.transaction(() async {
|
|
|
|
// todo how can we test that these are really equal?
|
2019-03-27 10:20:16 -07:00
|
|
|
});
|
2019-07-07 04:03:15 -07:00
|
|
|
|
|
|
|
// the outer callback has not completed yet, so shouldn't send
|
|
|
|
verifyNever(executor.transactions.send());
|
|
|
|
});
|
|
|
|
|
|
|
|
verify(executor.transactions.send());
|
|
|
|
});
|
|
|
|
|
|
|
|
test('code in callback uses transaction', () async {
|
|
|
|
// notice how we call .select on the database, but it should be called on
|
|
|
|
// transaction executor.
|
2019-09-13 12:04:15 -07:00
|
|
|
await db.transaction(() async {
|
2019-07-07 04:03:15 -07:00
|
|
|
await db.select(db.users).get();
|
|
|
|
});
|
|
|
|
|
|
|
|
verifyNever(executor.runSelect(any, any));
|
|
|
|
verify(executor.transactions.runSelect(any, any));
|
2019-03-27 10:20:16 -07:00
|
|
|
});
|
|
|
|
|
2019-07-23 00:06:02 -07:00
|
|
|
test('transactions rollback after errors', () async {
|
|
|
|
final exception = Exception('oh no');
|
2019-09-13 12:04:15 -07:00
|
|
|
final future = db.transaction(() async {
|
2019-07-23 00:06:02 -07:00
|
|
|
throw exception;
|
|
|
|
});
|
|
|
|
|
|
|
|
await expectLater(future, throwsA(exception));
|
|
|
|
|
|
|
|
verifyNever(executor.transactions.send());
|
|
|
|
verify(executor.transactions.rollback());
|
|
|
|
});
|
|
|
|
|
2019-03-10 04:38:53 -07:00
|
|
|
test('transactions notify about table udpates after completing', () async {
|
2019-03-10 07:08:29 -07:00
|
|
|
when(executor.transactions.runUpdate(any, any))
|
|
|
|
.thenAnswer((_) => Future.value(2));
|
2019-03-10 04:38:53 -07:00
|
|
|
|
2019-09-13 12:04:15 -07:00
|
|
|
await db.transaction(() async {
|
|
|
|
await db
|
2019-06-21 11:29:42 -07:00
|
|
|
.update(db.users)
|
|
|
|
.write(const UsersCompanion(name: Value('Updated name')));
|
2019-03-10 04:38:53 -07:00
|
|
|
|
|
|
|
// Even though we just wrote to users, this only happened inside the
|
|
|
|
// transaction, so the top level stream queries should not be updated.
|
2020-01-30 13:08:18 -08:00
|
|
|
verifyZeroInteractions(streamQueries);
|
2019-03-10 04:38:53 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
// After the transaction completes, the queries should be updated
|
2020-03-04 13:08:58 -08:00
|
|
|
verify(
|
|
|
|
streamQueries.handleTableUpdates(
|
|
|
|
{TableUpdate.fromTable(db.users, kind: UpdateKind.update)}),
|
|
|
|
).called(1);
|
2019-03-10 04:38:53 -07:00
|
|
|
verify(executor.transactions.send());
|
|
|
|
});
|
2019-03-30 06:56:55 -07:00
|
|
|
|
|
|
|
test('the database is opened before starting a transaction', () async {
|
2019-09-13 12:04:15 -07:00
|
|
|
await db.transaction(() async {
|
2019-03-30 06:56:55 -07:00
|
|
|
verify(executor.doWhenOpened(any));
|
|
|
|
});
|
|
|
|
});
|
2019-10-10 06:52:09 -07:00
|
|
|
|
|
|
|
test('transaction return value', () async {
|
|
|
|
final actual = await db.transaction(() async => 1);
|
|
|
|
expect(actual, 1);
|
|
|
|
});
|
2019-03-10 07:08:29 -07:00
|
|
|
}
|