mirror of https://github.com/AMT-Cheif/drift.git
239 lines
7.0 KiB
Dart
239 lines
7.0 KiB
Dart
import 'package:mockito/mockito.dart';
|
|
import 'package:moor/moor.dart';
|
|
import 'package:test/test.dart';
|
|
|
|
import '../data/tables/converter.dart';
|
|
import '../data/tables/custom_tables.dart';
|
|
import '../data/utils/mocks.dart';
|
|
|
|
const _createNoIds =
|
|
'CREATE TABLE IF NOT EXISTS no_ids (payload BLOB NOT NULL PRIMARY KEY) '
|
|
'WITHOUT ROWID;';
|
|
|
|
const _createWithDefaults = 'CREATE TABLE IF NOT EXISTS with_defaults ('
|
|
"a TEXT DEFAULT 'something', b INTEGER UNIQUE);";
|
|
|
|
const _createWithConstraints = 'CREATE TABLE IF NOT EXISTS with_constraints ('
|
|
'a TEXT, b INTEGER NOT NULL, c REAL, '
|
|
'FOREIGN KEY (a, b) REFERENCES with_defaults (a, b)'
|
|
');';
|
|
|
|
const _createConfig = 'CREATE TABLE IF NOT EXISTS config ('
|
|
'config_key TEXT not null primary key, '
|
|
'config_value TEXT, '
|
|
'sync_state INTEGER, '
|
|
'sync_state_implicit INTEGER);';
|
|
|
|
const _createMyTable = 'CREATE TABLE IF NOT EXISTS mytable ('
|
|
'someid INTEGER NOT NULL, '
|
|
'sometext TEXT, '
|
|
'is_inserting INTEGER, '
|
|
'somedate INTEGER, '
|
|
'PRIMARY KEY (someid DESC)'
|
|
');';
|
|
|
|
const _createEmail = 'CREATE VIRTUAL TABLE IF NOT EXISTS email USING '
|
|
'fts5(sender, title, body);';
|
|
|
|
const _createMyTrigger =
|
|
'CREATE TRIGGER my_trigger AFTER INSERT ON config BEGIN '
|
|
'INSERT INTO with_defaults '
|
|
'VALUES (new.config_key, LENGTH(new.config_value));'
|
|
'END';
|
|
|
|
const _createValueIndex =
|
|
'CREATE INDEX IF NOT EXISTS value_idx ON config (config_value)';
|
|
|
|
const _createMyView =
|
|
'CREATE VIEW my_view AS SELECT * FROM config WHERE sync_state = 2';
|
|
|
|
const _defaultInsert = 'INSERT INTO config (config_key, config_value) '
|
|
"VALUES ('key', 'values')";
|
|
|
|
void main() {
|
|
// see ../data/tables/tables.moor
|
|
late MockExecutor mock;
|
|
late CustomTablesDb db;
|
|
|
|
setUp(() {
|
|
mock = MockExecutor();
|
|
db = CustomTablesDb(mock);
|
|
});
|
|
|
|
tearDown(() => db.close());
|
|
|
|
test('creates everything as specified in .moor files', () async {
|
|
await db.createMigrator().createAll();
|
|
|
|
verify(mock.runCustom(_createNoIds, []));
|
|
verify(mock.runCustom(_createWithDefaults, []));
|
|
verify(mock.runCustom(_createWithConstraints, []));
|
|
verify(mock.runCustom(_createConfig, []));
|
|
verify(mock.runCustom(_createMyTable, []));
|
|
verify(mock.runCustom(_createEmail, []));
|
|
verify(mock.runCustom(_createMyTrigger, []));
|
|
verify(mock.runCustom(_createMyView, []));
|
|
verify(mock.runCustom(_createValueIndex, []));
|
|
verify(mock.runCustom(_defaultInsert, []));
|
|
});
|
|
|
|
test('can create trigger manually', () async {
|
|
await db.createMigrator().createTrigger(db.myTrigger);
|
|
verify(mock.runCustom(_createMyTrigger, []));
|
|
});
|
|
|
|
test('can create index manually', () async {
|
|
await db.createMigrator().createIndex(db.valueIdx);
|
|
verify(mock.runCustom(_createValueIndex, []));
|
|
});
|
|
|
|
test('infers primary keys correctly', () async {
|
|
expect(db.noIds.primaryKey, [db.noIds.payload]);
|
|
expect(db.withDefaults.primaryKey, isEmpty);
|
|
expect(db.config.primaryKey, [db.config.configKey]);
|
|
expect(db.mytable.primaryKey, [db.mytable.someid]);
|
|
});
|
|
|
|
test('supports absent values for primary key integers', () async {
|
|
// regression test for #112: https://github.com/simolus3/moor/issues/112
|
|
|
|
await db.into(db.mytable).insert(const MytableCompanion());
|
|
verify(mock.runInsert('INSERT INTO mytable DEFAULT VALUES', []));
|
|
});
|
|
|
|
test('runs queries with arrays and Dart templates', () async {
|
|
await db.readMultiple(['a', 'b'],
|
|
clause: (config) =>
|
|
OrderBy([OrderingTerm(expression: config.configKey)])).get();
|
|
|
|
verify(mock.runSelect(
|
|
'SELECT * FROM config WHERE config_key IN (?1, ?2) '
|
|
'ORDER BY config_key ASC',
|
|
['a', 'b'],
|
|
));
|
|
});
|
|
|
|
test('runs query with variables from template', () async {
|
|
final mockResponse = {'config_key': 'key', 'config_value': 'value'};
|
|
when(mock.runSelect(any, any))
|
|
.thenAnswer((_) => Future.value([mockResponse]));
|
|
|
|
final parsed = await db
|
|
.readDynamic(predicate: (config) => config.configKey.equals('key'))
|
|
.getSingle();
|
|
|
|
verify(
|
|
mock.runSelect('SELECT * FROM config WHERE config_key = ?', ['key']));
|
|
expect(parsed, Config(configKey: 'key', configValue: 'value'));
|
|
});
|
|
|
|
test('applies default parameter expressions when not set', () async {
|
|
await db.readDynamic().getSingleOrNull();
|
|
|
|
verify(mock.runSelect('SELECT * FROM config WHERE (TRUE)', []));
|
|
});
|
|
|
|
test('columns use table names in queries with multiple tables', () async {
|
|
await db.multiple(predicate: (d, c) => d.a.equals('foo')).get();
|
|
|
|
verify(mock.runSelect(argThat(contains('d.a = ?')), any));
|
|
});
|
|
|
|
test('order by-params are ignored by default', () async {
|
|
await db.readMultiple(['foo']).get();
|
|
verify(mock.runSelect(argThat(isNot(contains('with_defaults.a'))), any));
|
|
});
|
|
|
|
test('runs queries with nested results', () async {
|
|
const row = {
|
|
'a': 'text for a',
|
|
'b': 42,
|
|
'nested_0.a': 'text',
|
|
'nested_0.b': 1337,
|
|
'nested_0.c': 18.7,
|
|
};
|
|
|
|
when(mock.runSelect(any, any)).thenAnswer((_) {
|
|
return Future.value([row]);
|
|
});
|
|
|
|
final result = await db
|
|
.multiple(predicate: (_, __) => const Constant(true))
|
|
.getSingle();
|
|
|
|
expect(
|
|
result,
|
|
MultipleResult(
|
|
row: QueryRow(row, db),
|
|
a: 'text for a',
|
|
b: 42,
|
|
c: WithConstraint(a: 'text', b: 1337, c: 18.7),
|
|
),
|
|
);
|
|
});
|
|
|
|
test('runs queries with nested results that are null', () async {
|
|
const row = {
|
|
'a': 'text for a',
|
|
'b': 42,
|
|
'nested_0.a': 'text',
|
|
'nested_0.b': null, // note: with_constraints.b is NOT NULL in the db
|
|
'nested_0.c': 18.7,
|
|
};
|
|
|
|
when(mock.runSelect(any, any)).thenAnswer((_) {
|
|
return Future.value([row]);
|
|
});
|
|
|
|
final result = await db
|
|
.multiple(predicate: (_, __) => const Constant(true))
|
|
.getSingle();
|
|
|
|
expect(
|
|
result,
|
|
MultipleResult(
|
|
row: QueryRow(row, db),
|
|
a: 'text for a',
|
|
b: 42,
|
|
// Since a non-nullable column in c was null, table should be null
|
|
c: null,
|
|
),
|
|
);
|
|
});
|
|
|
|
test('applies column name mapping when needed', () async {
|
|
when(mock.runSelect(any, any)).thenAnswer((_) async {
|
|
return [
|
|
{
|
|
'ck': 'key',
|
|
'cf': 'value',
|
|
'cs1': 1,
|
|
'cs2': 1,
|
|
}
|
|
];
|
|
});
|
|
|
|
final entry = await db.readConfig('key').getSingle();
|
|
expect(
|
|
entry,
|
|
Config(
|
|
configKey: 'key',
|
|
configValue: 'value',
|
|
syncState: SyncType.locallyUpdated,
|
|
syncStateImplicit: SyncType.locallyUpdated,
|
|
),
|
|
);
|
|
});
|
|
|
|
test('applies type converters to variables', () async {
|
|
when(mock.runSelect(any, any)).thenAnswer((_) => Future.value([]));
|
|
await db.typeConverterVar(SyncType.locallyCreated,
|
|
[SyncType.locallyUpdated, SyncType.synchronized]).get();
|
|
|
|
verify(mock.runSelect(
|
|
'SELECT config_key FROM config WHERE (TRUE) AND(sync_state = ? '
|
|
'OR sync_state_implicit IN (?2, ?3))',
|
|
[0, 1, 2]));
|
|
});
|
|
}
|