From bea001bb16260d4e3db24bcd11dae44eadcb57c3 Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Tue, 12 Nov 2019 21:16:48 +0100 Subject: [PATCH] Improve test coverage for query generation --- moor/build.yaml | 3 +- moor/example/example.g.dart | 19 ++++++++++++ .../runtime/query_builder/expressions/in.dart | 2 +- moor/test/data/tables/custom_tables.g.dart | 23 +++++++++++++++ moor/test/data/tables/todos.g.dart | 29 +++++++++++++++++++ moor/test/data_class_test.dart | 19 ++++++++++++ moor/test/database_test.dart | 19 ++++++++++++ moor/test/expressions/algebra_test.dart | 19 +++++------- moor/test/expressions/bools_test.dart | 23 +++++++++++++++ moor/test/expressions/expression_test.dart | 26 +++++++++++++++++ moor/test/schema_test.dart | 20 +++++++++++++ 11 files changed, 189 insertions(+), 13 deletions(-) create mode 100644 moor/test/data_class_test.dart create mode 100644 moor/test/expressions/bools_test.dart create mode 100644 moor/test/expressions/expression_test.dart diff --git a/moor/build.yaml b/moor/build.yaml index ec53a530..657e705a 100644 --- a/moor/build.yaml +++ b/moor/build.yaml @@ -5,4 +5,5 @@ targets: options: override_hash_and_equals_in_result_sets: true use_column_name_as_json_key_when_defined_in_moor_file: true - generate_connect_constructor: true \ No newline at end of file + generate_connect_constructor: true + write_from_json_string_constructor: true \ No newline at end of file diff --git a/moor/example/example.g.dart b/moor/example/example.g.dart index e64e665a..89a46a1f 100644 --- a/moor/example/example.g.dart +++ b/moor/example/example.g.dart @@ -29,6 +29,11 @@ class Category extends DataClass implements Insertable { description: serializer.fromJson(json['description']), ); } + factory Category.fromJsonString(String encodedJson, + {ValueSerializer serializer = const ValueSerializer.defaults()}) => + Category.fromJson( + DataClass.parseJson(encodedJson) as Map, + serializer: serializer); @override Map toJson( {ValueSerializer serializer = const ValueSerializer.defaults()}) { @@ -204,6 +209,10 @@ class Recipe extends DataClass implements Insertable { category: serializer.fromJson(json['category']), ); } + factory Recipe.fromJsonString(String encodedJson, + {ValueSerializer serializer = const ValueSerializer.defaults()}) => + Recipe.fromJson(DataClass.parseJson(encodedJson) as Map, + serializer: serializer); @override Map toJson( {ValueSerializer serializer = const ValueSerializer.defaults()}) { @@ -438,6 +447,11 @@ class Ingredient extends DataClass implements Insertable { caloriesPer100g: serializer.fromJson(json['caloriesPer100g']), ); } + factory Ingredient.fromJsonString(String encodedJson, + {ValueSerializer serializer = const ValueSerializer.defaults()}) => + Ingredient.fromJson( + DataClass.parseJson(encodedJson) as Map, + serializer: serializer); @override Map toJson( {ValueSerializer serializer = const ValueSerializer.defaults()}) { @@ -644,6 +658,11 @@ class IngredientInRecipe extends DataClass amountInGrams: serializer.fromJson(json['amountInGrams']), ); } + factory IngredientInRecipe.fromJsonString(String encodedJson, + {ValueSerializer serializer = const ValueSerializer.defaults()}) => + IngredientInRecipe.fromJson( + DataClass.parseJson(encodedJson) as Map, + serializer: serializer); @override Map toJson( {ValueSerializer serializer = const ValueSerializer.defaults()}) { diff --git a/moor/lib/src/runtime/query_builder/expressions/in.dart b/moor/lib/src/runtime/query_builder/expressions/in.dart index ffe65d07..764e1f34 100644 --- a/moor/lib/src/runtime/query_builder/expressions/in.dart +++ b/moor/lib/src/runtime/query_builder/expressions/in.dart @@ -6,7 +6,7 @@ part of '../query_builder.dart'; Expression isIn, T>( Expression expression, Iterable values, {bool not = false}) { - if (not == false) { + if (not == true) { return expression.isNotIn(values); } else { return expression.isIn(values); diff --git a/moor/test/data/tables/custom_tables.g.dart b/moor/test/data/tables/custom_tables.g.dart index 171a0371..3333c734 100644 --- a/moor/test/data/tables/custom_tables.g.dart +++ b/moor/test/data/tables/custom_tables.g.dart @@ -25,6 +25,10 @@ class NoId extends DataClass implements Insertable { payload: serializer.fromJson(json['payload']), ); } + factory NoId.fromJsonString(String encodedJson, + {ValueSerializer serializer = const ValueSerializer.defaults()}) => + NoId.fromJson(DataClass.parseJson(encodedJson) as Map, + serializer: serializer); @override Map toJson( {ValueSerializer serializer = const ValueSerializer.defaults()}) { @@ -156,6 +160,11 @@ class WithDefault extends DataClass implements Insertable { b: serializer.fromJson(json['b']), ); } + factory WithDefault.fromJsonString(String encodedJson, + {ValueSerializer serializer = const ValueSerializer.defaults()}) => + WithDefault.fromJson( + DataClass.parseJson(encodedJson) as Map, + serializer: serializer); @override Map toJson( {ValueSerializer serializer = const ValueSerializer.defaults()}) { @@ -315,6 +324,11 @@ class WithConstraint extends DataClass implements Insertable { c: serializer.fromJson(json['c']), ); } + factory WithConstraint.fromJsonString(String encodedJson, + {ValueSerializer serializer = const ValueSerializer.defaults()}) => + WithConstraint.fromJson( + DataClass.parseJson(encodedJson) as Map, + serializer: serializer); @override Map toJson( {ValueSerializer serializer = const ValueSerializer.defaults()}) { @@ -499,6 +513,10 @@ class Config extends DataClass implements Insertable { configValue: serializer.fromJson(json['config_value']), ); } + factory Config.fromJsonString(String encodedJson, + {ValueSerializer serializer = const ValueSerializer.defaults()}) => + Config.fromJson(DataClass.parseJson(encodedJson) as Map, + serializer: serializer); @override Map toJson( {ValueSerializer serializer = const ValueSerializer.defaults()}) { @@ -674,6 +692,11 @@ class MytableData extends DataClass implements Insertable { somedate: serializer.fromJson(json['somedate']), ); } + factory MytableData.fromJsonString(String encodedJson, + {ValueSerializer serializer = const ValueSerializer.defaults()}) => + MytableData.fromJson( + DataClass.parseJson(encodedJson) as Map, + serializer: serializer); @override Map toJson( {ValueSerializer serializer = const ValueSerializer.defaults()}) { diff --git a/moor/test/data/tables/todos.g.dart b/moor/test/data/tables/todos.g.dart index b20f2ca1..77075f17 100644 --- a/moor/test/data/tables/todos.g.dart +++ b/moor/test/data/tables/todos.g.dart @@ -47,6 +47,11 @@ class TodoEntry extends DataClass implements Insertable { category: serializer.fromJson(json['category']), ); } + factory TodoEntry.fromJsonString(String encodedJson, + {ValueSerializer serializer = const ValueSerializer.defaults()}) => + TodoEntry.fromJson( + DataClass.parseJson(encodedJson) as Map, + serializer: serializer); @override Map toJson( {ValueSerializer serializer = const ValueSerializer.defaults()}) { @@ -318,6 +323,11 @@ class Category extends DataClass implements Insertable { description: serializer.fromJson(json['description']), ); } + factory Category.fromJsonString(String encodedJson, + {ValueSerializer serializer = const ValueSerializer.defaults()}) => + Category.fromJson( + DataClass.parseJson(encodedJson) as Map, + serializer: serializer); @override Map toJson( {ValueSerializer serializer = const ValueSerializer.defaults()}) { @@ -497,6 +507,10 @@ class User extends DataClass implements Insertable { creationTime: serializer.fromJson(json['creationTime']), ); } + factory User.fromJsonString(String encodedJson, + {ValueSerializer serializer = const ValueSerializer.defaults()}) => + User.fromJson(DataClass.parseJson(encodedJson) as Map, + serializer: serializer); @override Map toJson( {ValueSerializer serializer = const ValueSerializer.defaults()}) { @@ -768,6 +782,11 @@ class SharedTodo extends DataClass implements Insertable { user: serializer.fromJson(json['user']), ); } + factory SharedTodo.fromJsonString(String encodedJson, + {ValueSerializer serializer = const ValueSerializer.defaults()}) => + SharedTodo.fromJson( + DataClass.parseJson(encodedJson) as Map, + serializer: serializer); @override Map toJson( {ValueSerializer serializer = const ValueSerializer.defaults()}) { @@ -943,6 +962,11 @@ class TableWithoutPKData extends DataClass custom: serializer.fromJson(json['custom']), ); } + factory TableWithoutPKData.fromJsonString(String encodedJson, + {ValueSerializer serializer = const ValueSerializer.defaults()}) => + TableWithoutPKData.fromJson( + DataClass.parseJson(encodedJson) as Map, + serializer: serializer); @override Map toJson( {ValueSerializer serializer = const ValueSerializer.defaults()}) { @@ -1151,6 +1175,11 @@ class PureDefault extends DataClass implements Insertable { txt: serializer.fromJson(json['txt']), ); } + factory PureDefault.fromJsonString(String encodedJson, + {ValueSerializer serializer = const ValueSerializer.defaults()}) => + PureDefault.fromJson( + DataClass.parseJson(encodedJson) as Map, + serializer: serializer); @override Map toJson( {ValueSerializer serializer = const ValueSerializer.defaults()}) { diff --git a/moor/test/data_class_test.dart b/moor/test/data_class_test.dart new file mode 100644 index 00000000..0023d13e --- /dev/null +++ b/moor/test/data_class_test.dart @@ -0,0 +1,19 @@ +import 'package:test/test.dart'; + +import 'data/tables/todos.dart'; + +void main() { + test('data classes can be serialized', () { + final entry = TodoEntry( + id: 13, + title: 'Title', + content: 'Content', + targetDate: DateTime.now(), + ); + + final serialized = entry.toJsonString(); + final deserialized = TodoEntry.fromJsonString(serialized); + + expect(deserialized, equals(deserialized)); + }); +} diff --git a/moor/test/database_test.dart b/moor/test/database_test.dart index 7d13427e..b74f46bc 100644 --- a/moor/test/database_test.dart +++ b/moor/test/database_test.dart @@ -1,6 +1,7 @@ import 'package:test/test.dart'; import 'package:moor/moor.dart'; +import 'data/tables/todos.dart'; import 'data/utils/mocks.dart'; class _FakeDb extends GeneratedDatabase { @@ -66,4 +67,22 @@ void main() { verify(executor.runSelect('opened: 3 to 4', [])); }); }); + + test('creates and attaches daos', () async { + final executor = MockExecutor(); + final db = TodoDb(executor); + + await db.someDao.todosForUser(1); + + verify(executor.runSelect(argThat(contains('SELECT t.* FROM todos')), [1])); + }); + + test('closing the database closes the executor', () async { + final executor = MockExecutor(); + final db = TodoDb(executor); + + await db.close(); + + verify(executor.close()); + }); } diff --git a/moor/test/expressions/algebra_test.dart b/moor/test/expressions/algebra_test.dart index d64b4d04..0d42ad16 100644 --- a/moor/test/expressions/algebra_test.dart +++ b/moor/test/expressions/algebra_test.dart @@ -1,7 +1,7 @@ import 'package:moor/moor.dart'; import 'package:test/test.dart'; -import '../data/tables/todos.dart'; +import '../data/utils/expect_generated.dart'; void main() { final i1 = GeneratedIntColumn('i1', 'tbl', true); @@ -10,18 +10,15 @@ void main() { final s2 = GeneratedTextColumn('s2', 'tbl', true); test('arithmetic test', () { - _expectSql(i1 + i2 * i1, 'i1 + i2 * i1'); - _expectSql((i1 + i2) * i1, '(i1 + i2) * i1'); + (i1 + i2 * i1).expectGenerates('i1 + i2 * i1'); + (i1 + i2 * i1).expectGenerates('i1 + i2 * i1'); + ((i1 + i2) * i1).expectGenerates('(i1 + i2) * i1'); + (i1 - i2).expectGenerates('i1 - i2'); + (i1 - -i2).expectGenerates('i1 - -i2'); + (i1 / i2).expectGenerates('i1 / i2'); }); test('string concatenation', () { - _expectSql(s1 + s2, 's1 || s2'); + (s1 + s2).expectGenerates('s1 || s2'); }); } - -void _expectSql(Expression e, String expected) { - final ctx = GenerationContext.fromDb(TodoDb(null)); - e.writeInto(ctx); - - expect(ctx.sql, expected); -} diff --git a/moor/test/expressions/bools_test.dart b/moor/test/expressions/bools_test.dart new file mode 100644 index 00000000..60cf5ec3 --- /dev/null +++ b/moor/test/expressions/bools_test.dart @@ -0,0 +1,23 @@ +import 'package:moor/moor.dart'; +import 'package:test/test.dart'; + +import '../data/utils/expect_generated.dart'; + +// ignore_for_file: deprecated_member_use_from_same_package + +void main() { + final a = GeneratedBoolColumn('a', 'tbl', false); + final b = GeneratedBoolColumn('b', 'tbl', false); + + test('boolean expressions via operators', () { + (a | b).expectGenerates('a OR b'); + (a & b).expectGenerates('a AND b'); + a.not().expectGenerates('NOT a'); + }); + + test('boolean expressions via top-level methods', () { + or(a, b).expectGenerates('a OR b'); + and(a, b).expectGenerates('a AND b'); + not(a).expectGenerates('NOT a'); + }); +} diff --git a/moor/test/expressions/expression_test.dart b/moor/test/expressions/expression_test.dart new file mode 100644 index 00000000..1bb342a0 --- /dev/null +++ b/moor/test/expressions/expression_test.dart @@ -0,0 +1,26 @@ +import 'package:test/test.dart'; + +import 'package:moor/moor.dart'; +import '../data/utils/expect_generated.dart'; + +class _UnknownExpr extends Expression { + @override + void writeInto(GenerationContext context) { + context.buffer.write('???'); + } +} + +void main() { + test('precedence ordering', () { + expect(Precedence.plusMinus < Precedence.mulDivide, isTrue); + expect(Precedence.unary <= Precedence.unary, isTrue); + expect(Precedence.postfix >= Precedence.bitwise, isTrue); + expect(Precedence.postfix > Precedence.primary, isFalse); + }); + + test('puts parentheses around expressions with unknown precedence', () { + final expr = _UnknownExpr().equalsExp(_UnknownExpr()); + + expr.expectGenerates('(???) = (???)'); + }); +} diff --git a/moor/test/schema_test.dart b/moor/test/schema_test.dart index bd60c527..6b989f57 100644 --- a/moor/test/schema_test.dart +++ b/moor/test/schema_test.dart @@ -96,4 +96,24 @@ void main() { await db.customStatement('some custom statement'); verify(mockExecutor.runCustom('some custom statement')); }); + + test('upgrading a database without schema migration throws', () async { + final db = _DefaultDb(MockExecutor()); + expect( + () => db.handleDatabaseVersionChange( + executor: MockQueryExecutor(), from: 1, to: 2), + throwsA(const TypeMatcher())); + }); +} + +class _DefaultDb extends GeneratedDatabase { + _DefaultDb(QueryExecutor executor) + // ignore: prefer_const_constructors + : super(SqlTypeSystem.withDefaults(), executor); + + @override + List> get allTables => []; + + @override + int get schemaVersion => 2; }