Migrate tests to companion

This commit is contained in:
Simon Binder 2019-06-21 20:29:42 +02:00
parent 0616fb7082
commit 62c5106e7d
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
11 changed files with 124 additions and 96 deletions

View File

@ -45,8 +45,10 @@ class Database extends _$Database {
int get schemaVersion => 1;
@override
// todo migrate
MigrationStrategy get migration => MigrationStrategy(onFinished: () async {
// populate data
await into(categories).insert(Category(description: 'Sweets'));
await into(categories)
.insert(const CategoriesCompanion(description: Value('Sweets')));
});
}

View File

@ -10,7 +10,7 @@ part of 'example.dart';
class Category extends DataClass implements Insertable<Category> {
final int id;
final String description;
Category({this.id, this.description});
Category({@required this.id, this.description});
factory Category.fromData(Map<String, dynamic> data, GeneratedDatabase db,
{String prefix}) {
final effectivePrefix = prefix ?? '';
@ -41,10 +41,10 @@ class Category extends DataClass implements Insertable<Category> {
@override
T createCompanion<T extends UpdateCompanion<Category>>(bool nullToAbsent) {
return CategoriesCompanion(
id: id == null && nullToAbsent ? const Value.absent() : Value.use(id),
id: id == null && nullToAbsent ? const Value.absent() : Value(id),
description: description == null && nullToAbsent
? const Value.absent()
: Value.use(description),
: Value(description),
) as T;
}
@ -162,7 +162,11 @@ class Recipe extends DataClass implements Insertable<Recipe> {
final String title;
final String instructions;
final int category;
Recipe({this.id, this.title, this.instructions, this.category});
Recipe(
{@required this.id,
@required this.title,
@required this.instructions,
this.category});
factory Recipe.fromData(Map<String, dynamic> data, GeneratedDatabase db,
{String prefix}) {
final effectivePrefix = prefix ?? '';
@ -201,16 +205,15 @@ class Recipe extends DataClass implements Insertable<Recipe> {
@override
T createCompanion<T extends UpdateCompanion<Recipe>>(bool nullToAbsent) {
return RecipesCompanion(
id: id == null && nullToAbsent ? const Value.absent() : Value.use(id),
title: title == null && nullToAbsent
? const Value.absent()
: Value.use(title),
id: id == null && nullToAbsent ? const Value.absent() : Value(id),
title:
title == null && nullToAbsent ? const Value.absent() : Value(title),
instructions: instructions == null && nullToAbsent
? const Value.absent()
: Value.use(instructions),
: Value(instructions),
category: category == null && nullToAbsent
? const Value.absent()
: Value.use(category),
: Value(category),
) as T;
}
@ -382,7 +385,8 @@ class Ingredient extends DataClass implements Insertable<Ingredient> {
final int id;
final String name;
final int caloriesPer100g;
Ingredient({this.id, this.name, this.caloriesPer100g});
Ingredient(
{@required this.id, @required this.name, @required this.caloriesPer100g});
factory Ingredient.fromData(Map<String, dynamic> data, GeneratedDatabase db,
{String prefix}) {
final effectivePrefix = prefix ?? '';
@ -416,12 +420,11 @@ class Ingredient extends DataClass implements Insertable<Ingredient> {
@override
T createCompanion<T extends UpdateCompanion<Ingredient>>(bool nullToAbsent) {
return IngredientsCompanion(
id: id == null && nullToAbsent ? const Value.absent() : Value.use(id),
name:
name == null && nullToAbsent ? const Value.absent() : Value.use(name),
id: id == null && nullToAbsent ? const Value.absent() : Value(id),
name: name == null && nullToAbsent ? const Value.absent() : Value(name),
caloriesPer100g: caloriesPer100g == null && nullToAbsent
? const Value.absent()
: Value.use(caloriesPer100g),
: Value(caloriesPer100g),
) as T;
}
@ -570,7 +573,10 @@ class IngredientInRecipe extends DataClass
final int recipe;
final int ingredient;
final int amountInGrams;
IngredientInRecipe({this.recipe, this.ingredient, this.amountInGrams});
IngredientInRecipe(
{@required this.recipe,
@required this.ingredient,
@required this.amountInGrams});
factory IngredientInRecipe.fromData(
Map<String, dynamic> data, GeneratedDatabase db,
{String prefix}) {
@ -606,15 +612,14 @@ class IngredientInRecipe extends DataClass
T createCompanion<T extends UpdateCompanion<IngredientInRecipe>>(
bool nullToAbsent) {
return IngredientInRecipesCompanion(
recipe: recipe == null && nullToAbsent
? const Value.absent()
: Value.use(recipe),
recipe:
recipe == null && nullToAbsent ? const Value.absent() : Value(recipe),
ingredient: ingredient == null && nullToAbsent
? const Value.absent()
: Value.use(ingredient),
: Value(ingredient),
amountInGrams: amountInGrams == null && nullToAbsent
? const Value.absent()
: Value.use(amountInGrams),
: Value(amountInGrams),
) as T;
}

View File

@ -3,6 +3,9 @@ library moor;
// needed for the generated code that generates data classes with an Uint8List
// field.
export 'dart:typed_data' show Uint8List;
// needed for generated code which provides an @required parameter hint where
// appropriate
export 'package:meta/meta.dart' show required;
export 'package:moor/src/dsl/table.dart';
export 'package:moor/src/dsl/columns.dart';

View File

@ -57,7 +57,7 @@ class Value<T> {
final bool present;
final T value;
const Value.use(this.value) : present = true;
const Value(this.value) : present = true;
const Value.absent()
: value = null,
present = false;

View File

@ -14,7 +14,11 @@ class TodoEntry extends DataClass implements Insertable<TodoEntry> {
final DateTime targetDate;
final int category;
TodoEntry(
{this.id, this.title, this.content, this.targetDate, this.category});
{@required this.id,
this.title,
@required this.content,
this.targetDate,
this.category});
factory TodoEntry.fromData(Map<String, dynamic> data, GeneratedDatabase db,
{String prefix}) {
final effectivePrefix = prefix ?? '';
@ -58,19 +62,18 @@ class TodoEntry extends DataClass implements Insertable<TodoEntry> {
@override
T createCompanion<T extends UpdateCompanion<TodoEntry>>(bool nullToAbsent) {
return TodosTableCompanion(
id: id == null && nullToAbsent ? const Value.absent() : Value.use(id),
title: title == null && nullToAbsent
? const Value.absent()
: Value.use(title),
id: id == null && nullToAbsent ? const Value.absent() : Value(id),
title:
title == null && nullToAbsent ? const Value.absent() : Value(title),
content: content == null && nullToAbsent
? const Value.absent()
: Value.use(content),
: Value(content),
targetDate: targetDate == null && nullToAbsent
? const Value.absent()
: Value.use(targetDate),
: Value(targetDate),
category: category == null && nullToAbsent
? const Value.absent()
: Value.use(category),
: Value(category),
) as T;
}
@ -273,7 +276,7 @@ class $TodosTableTable extends TodosTable
class Category extends DataClass implements Insertable<Category> {
final int id;
final String description;
Category({this.id, this.description});
Category({@required this.id, @required this.description});
factory Category.fromData(Map<String, dynamic> data, GeneratedDatabase db,
{String prefix}) {
final effectivePrefix = prefix ?? '';
@ -304,10 +307,10 @@ class Category extends DataClass implements Insertable<Category> {
@override
T createCompanion<T extends UpdateCompanion<Category>>(bool nullToAbsent) {
return CategoriesCompanion(
id: id == null && nullToAbsent ? const Value.absent() : Value.use(id),
id: id == null && nullToAbsent ? const Value.absent() : Value(id),
description: description == null && nullToAbsent
? const Value.absent()
: Value.use(description),
: Value(description),
) as T;
}
@ -424,11 +427,11 @@ class User extends DataClass implements Insertable<User> {
final Uint8List profilePicture;
final DateTime creationTime;
User(
{this.id,
this.name,
this.isAwesome,
this.profilePicture,
this.creationTime});
{@required this.id,
@required this.name,
@required this.isAwesome,
@required this.profilePicture,
@required this.creationTime});
factory User.fromData(Map<String, dynamic> data, GeneratedDatabase db,
{String prefix}) {
final effectivePrefix = prefix ?? '';
@ -473,18 +476,17 @@ class User extends DataClass implements Insertable<User> {
@override
T createCompanion<T extends UpdateCompanion<User>>(bool nullToAbsent) {
return UsersCompanion(
id: id == null && nullToAbsent ? const Value.absent() : Value.use(id),
name:
name == null && nullToAbsent ? const Value.absent() : Value.use(name),
id: id == null && nullToAbsent ? const Value.absent() : Value(id),
name: name == null && nullToAbsent ? const Value.absent() : Value(name),
isAwesome: isAwesome == null && nullToAbsent
? const Value.absent()
: Value.use(isAwesome),
: Value(isAwesome),
profilePicture: profilePicture == null && nullToAbsent
? const Value.absent()
: Value.use(profilePicture),
: Value(profilePicture),
creationTime: creationTime == null && nullToAbsent
? const Value.absent()
: Value.use(creationTime),
: Value(creationTime),
) as T;
}
@ -690,7 +692,7 @@ class $UsersTable extends Users with TableInfo<$UsersTable, User> {
class SharedTodo extends DataClass implements Insertable<SharedTodo> {
final int todo;
final int user;
SharedTodo({this.todo, this.user});
SharedTodo({@required this.todo, @required this.user});
factory SharedTodo.fromData(Map<String, dynamic> data, GeneratedDatabase db,
{String prefix}) {
final effectivePrefix = prefix ?? '';
@ -719,10 +721,8 @@ class SharedTodo extends DataClass implements Insertable<SharedTodo> {
@override
T createCompanion<T extends UpdateCompanion<SharedTodo>>(bool nullToAbsent) {
return SharedTodosCompanion(
todo:
todo == null && nullToAbsent ? const Value.absent() : Value.use(todo),
user:
user == null && nullToAbsent ? const Value.absent() : Value.use(user),
todo: todo == null && nullToAbsent ? const Value.absent() : Value(todo),
user: user == null && nullToAbsent ? const Value.absent() : Value(user),
) as T;
}
@ -842,7 +842,7 @@ class TableWithoutPKData extends DataClass
implements Insertable<TableWithoutPKData> {
final int notReallyAnId;
final double someFloat;
TableWithoutPKData({this.notReallyAnId, this.someFloat});
TableWithoutPKData({@required this.notReallyAnId, @required this.someFloat});
factory TableWithoutPKData.fromData(
Map<String, dynamic> data, GeneratedDatabase db,
{String prefix}) {
@ -878,10 +878,10 @@ class TableWithoutPKData extends DataClass
return TableWithoutPKCompanion(
notReallyAnId: notReallyAnId == null && nullToAbsent
? const Value.absent()
: Value.use(notReallyAnId),
: Value(notReallyAnId),
someFloat: someFloat == null && nullToAbsent
? const Value.absent()
: Value.use(someFloat),
: Value(someFloat),
) as T;
}

View File

@ -6,13 +6,16 @@ import 'data/utils/mocks.dart';
// the content is set to non-null and the title must be between 4 and 16 chars
// long
final nullContent = const TodosTableCompanion(
title: Value.use('Test'), content: Value.use(null));
final absentContent = const TodosTableCompanion(
title: Value.use('Test'), content: Value.absent());
final shortTitle = TodoEntry(title: 'A', content: 'content');
final longTitle = TodoEntry(title: 'A ${'very' * 5} long title', content: 'hi');
final valid = TodoEntry(title: 'Test', content: 'Some content');
const nullContent =
TodosTableCompanion(title: Value('Test'), content: Value(null));
const absentContent =
TodosTableCompanion(title: Value('Test'), content: Value.absent());
final shortTitle =
const TodosTableCompanion(title: Value('A'), content: Value('content'));
final longTitle = TodosTableCompanion(
title: Value('A ${'very' * 5} long title'), content: const Value('hi'));
const valid =
TodosTableCompanion(title: Value('Test'), content: Value('Some content'));
void main() {
TodoDb db;

View File

@ -16,8 +16,8 @@ void main() {
});
test('generates insert statements', () async {
await db.into(db.todosTable).insert(TodoEntry(
content: 'Implement insert statements',
await db.into(db.todosTable).insert(const TodosTableCompanion(
content: Value('Implement insert statements'),
));
verify(executor.runInsert('INSERT INTO todos (content) VALUES (?)',
@ -50,10 +50,10 @@ void main() {
});
test('runs bulk inserts', () async {
await db.into(db.todosTable).insertAll([
TodoEntry(content: 'a'),
TodoEntry(title: 'title', content: 'b'),
TodoEntry(title: 'title', content: 'c'),
await db.into(db.todosTable).insertAll(const [
TodosTableCompanion(content: Value('a')),
TodosTableCompanion(title: Value('title'), content: Value('b')),
TodosTableCompanion(title: Value('title'), content: Value('c')),
]);
final insertSimple = 'INSERT INTO todos (content) VALUES (?)';
@ -73,10 +73,10 @@ void main() {
});
test('runs bulk inserts with OR REPLACE', () async {
await db.into(db.todosTable).insertAll([
TodoEntry(content: 'a'),
TodoEntry(title: 'title', content: 'b'),
TodoEntry(title: 'title', content: 'c'),
await db.into(db.todosTable).insertAll(const [
TodosTableCompanion(content: Value('a')),
TodosTableCompanion(title: Value('title'), content: Value('b')),
TodosTableCompanion(title: Value('title'), content: Value('c')),
], orReplace: true);
final insertSimple = 'INSERT OR REPLACE INTO todos (content) VALUES (?)';
@ -97,10 +97,10 @@ void main() {
});
test('notifies stream queries on inserts', () async {
await db.into(db.users).insert(User(
name: 'User McUserface',
isAwesome: true,
profilePicture: Uint8List(0),
await db.into(db.users).insert(UsersCompanion(
name: const Value('User McUserface'),
isAwesome: const Value(true),
profilePicture: Value(Uint8List(0)),
));
verify(streamQueries.handleTableUpdates({db.users}));
@ -109,8 +109,9 @@ void main() {
test('enforces data integrity', () {
expect(
db.into(db.todosTable).insert(
TodoEntry(
content: null, // not declared as nullable in table definition
const TodosTableCompanion(
// not declared as nullable in table definition
content: Value(null),
),
),
throwsA(const TypeMatcher<InvalidDataException>()),
@ -120,7 +121,11 @@ void main() {
test('reports auto-increment id', () async {
when(executor.runInsert(any, any)).thenAnswer((_) => Future.value(42));
expect(db.into(db.todosTable).insert(TodoEntry(content: 'Bottom text')),
completion(42));
expect(
db
.into(db.todosTable)
.insert(const TodosTableCompanion(content: Value('Bottom text'))),
completion(42),
);
});
}

View File

@ -1,4 +1,5 @@
import 'package:test_api/test_api.dart';
import 'package:moor/moor.dart';
import 'data/tables/todos.dart';
import 'data/utils/mocks.dart';
@ -39,7 +40,9 @@ void main() {
.thenAnswer((_) => Future.value(2));
await db.transaction((t) async {
await t.update(db.users).write(User(name: 'Updated name'));
await t
.update(db.users)
.write(const UsersCompanion(name: Value('Updated name')));
// Even though we just wrote to users, this only happened inside the
// transaction, so the top level stream queries should not be updated.

View File

@ -19,9 +19,10 @@ void main() {
group('generates update statements', () {
test('for entire table', () async {
await db
.update(db.todosTable)
.write(TodoEntry(title: 'Updated title', category: 3));
await db.update(db.todosTable).write(const TodosTableCompanion(
title: Value('Updated title'),
category: Value(3),
));
verify(executor.runUpdate(
'UPDATE todos SET title = ?, category = ?;', ['Updated title', 3]));
@ -30,7 +31,7 @@ void main() {
test('with a WHERE clause', () async {
await (db.update(db.todosTable)
..where((t) => t.id.isSmallerThanValue(50)))
.write(TodoEntry(title: 'Changed title'));
.write(const TodosTableCompanion(title: Value('Changed title')));
verify(executor.runUpdate(
'UPDATE todos SET title = ? WHERE id < ?;', ['Changed title', 50]));
@ -55,7 +56,9 @@ void main() {
// The length of a title must be between 4 and 16 chars
expect(() async {
await db.update(db.todosTable).write(TodoEntry(title: 'lol'));
await db
.update(db.todosTable)
.write(const TodosTableCompanion(title: Value('lol')));
}, throwsA(const TypeMatcher<InvalidDataException>()));
});
@ -63,8 +66,8 @@ void main() {
test('are issued when data was changed', () async {
when(executor.runUpdate(any, any)).thenAnswer((_) => Future.value(3));
await db.update(db.todosTable).write(TodoEntry(
content: 'Updated content',
await db.update(db.todosTable).write(const TodosTableCompanion(
content: Value('Updated content'),
));
verify(streamQueries.handleTableUpdates({db.todosTable}));
@ -73,7 +76,7 @@ void main() {
test('are not issued when no data was changed', () async {
when(executor.runDelete(any, any)).thenAnswer((_) => Future.value(0));
await db.update(db.todosTable).write(TodoEntry());
await db.update(db.todosTable).write(const TodosTableCompanion());
verifyNever(streamQueries.handleTableUpdates(any));
});

View File

@ -52,16 +52,16 @@ class TodoEntry extends DataClass implements Insertable<TodoEntry> {
@override
T createCompanion<T extends UpdateCompanion<TodoEntry>>(bool nullToAbsent) {
return TodosCompanion(
id: id == null && nullToAbsent ? const Value.absent() : Value.use(id),
id: id == null && nullToAbsent ? const Value.absent() : Value(id),
content: content == null && nullToAbsent
? const Value.absent()
: Value.use(content),
: Value(content),
targetDate: targetDate == null && nullToAbsent
? const Value.absent()
: Value.use(targetDate),
: Value(targetDate),
category: category == null && nullToAbsent
? const Value.absent()
: Value.use(category),
: Value(category),
) as T;
}
@ -262,10 +262,10 @@ class Category extends DataClass implements Insertable<Category> {
@override
T createCompanion<T extends UpdateCompanion<Category>>(bool nullToAbsent) {
return CategoriesCompanion(
id: id == null && nullToAbsent ? const Value.absent() : Value.use(id),
id: id == null && nullToAbsent ? const Value.absent() : Value(id),
description: description == null && nullToAbsent
? const Value.absent()
: Value.use(description),
: Value(description),
) as T;
}

View File

@ -24,9 +24,13 @@ class DataClassWriter {
buffer
..write(table.dartTypeName)
..write('({')
..write(table.columns
.map((column) => 'this.${column.dartGetterName}')
.join(', '))
..write(table.columns.map((column) {
if (column.nullable) {
return 'this.${column.dartGetterName}';
} else {
return '@required this.${column.dartGetterName}';
}
}).join(', '))
..write('});');
// Also write parsing factory