From a6f7cc6ed077c6fc300091ceef343e1f7df15662 Mon Sep 17 00:00:00 2001 From: zin- Date: Tue, 6 Aug 2024 09:55:30 +0900 Subject: [PATCH] add: count & receive wip add: DatabaseTupleRepository wip #377 --- .../database_tuple_repository_tests.dart | 135 ++++++++++++++++++ .../framework/framework_test.dart | 3 + .../repository/database_tuple_repository.dart | 63 ++++++++ lib/framework/repository/entity.dart | 15 ++ lib/framework/repository/repository.dart | 2 + .../database_tuple_entity_test.dart | 12 +- test/framework/repository/entity_test.dart | 13 ++ 7 files changed, 237 insertions(+), 6 deletions(-) create mode 100644 integration_test/framework/database_tuple_repository_tests.dart create mode 100644 lib/framework/repository/database_tuple_repository.dart diff --git a/integration_test/framework/database_tuple_repository_tests.dart b/integration_test/framework/database_tuple_repository_tests.dart new file mode 100644 index 000000000..5f42602ea --- /dev/null +++ b/integration_test/framework/database_tuple_repository_tests.dart @@ -0,0 +1,135 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:mem/databases/table_definitions/base.dart'; +import 'package:mem/framework/database/definition/column/boolean_column_definition.dart'; +import 'package:mem/framework/database/definition/database_definition.dart'; +import 'package:mem/framework/database/definition/table_definition.dart'; +import 'package:mem/framework/database/factory.dart'; +import 'package:mem/framework/repository/condition/conditions.dart'; +import 'package:mem/framework/repository/database_tuple_repository.dart'; + +import '../../test/framework/repository/database_tuple_entity_test.dart'; +import '../../test/framework/repository/entity_test.dart'; + +const _name = "DatabaseTupleRepository tests"; + +final _defColA = BooleanColumnDefinition(TestObjectEntity.fieldNames[0]); +final _defTableTestObject = + TableDefinition('test_object', [_defColA, ...defColsBase]); +final _defDbTest = DatabaseDefinition('test_db', 1, [_defTableTestObject]); + +class _TestObjectRepository extends DatabaseTupleRepository { + _TestObjectRepository() : super(_defDbTest, _defTableTestObject); + + @override + TestObjectEntity pack(Map map) => + TestObjectDatabaseTupleEntity.fromMap(map); +} + +void main() => group( + _name, + () { + late String databasePath; + setUpAll( + () async { + databasePath = + await DatabaseFactory.buildDatabasePath(_defDbTest.name); + + await DatabaseFactory + // ignore: deprecated_member_use_from_same_package + .nativeFactory + .deleteDatabase(databasePath); + }, + ); + + test( + '#new', + () async { + _TestObjectRepository(); + + expect( + await DatabaseFactory + // ignore: deprecated_member_use_from_same_package + .nativeFactory + .databaseExists(databasePath), + false); + }, + ); + + group( + 'operations', + () { + final repository = _TestObjectRepository(); + + group( + '#count', + () { + setUpAll( + () async { + final falseSample = TestObjectEntity(false); + final trueSample = TestObjectEntity(true); + final now = DateTime.now(); + + final dbA = await DatabaseFactory.open(_defDbTest); + + await dbA.delete(_defTableTestObject); + await dbA.insert(_defTableTestObject, + falseSample.toMap..addAll({defColCreatedAt.name: now})); + await dbA.insert(_defTableTestObject, + falseSample.toMap..addAll({defColCreatedAt.name: now})); + await dbA.insert(_defTableTestObject, + trueSample.toMap..addAll({defColCreatedAt.name: now})); + }, + ); + + test( + ': all.', + () async { + final count = await repository.count(); + + expect(count, 3); + }, + ); + test( + ': condition.', + () async { + final count = await repository.count( + condition: Equals(_defColA.name, false)); + + expect(count, 2); + }, + ); + }, + ); + + group( + '#receive', + () { + setUpAll( + () async { + final dbA = await DatabaseFactory.open(_defDbTest); + + await dbA.delete(_defTableTestObject); + }, + ); + + test( + ': received.', + () async { + final now = DateTime.now(); + final entity = TestObjectEntity(false); + + final received = + await repository.receive(entity, createdAt: now); + + expect( + received, + equals(TestObjectDatabaseTupleEntity(entity.a).withMap( + {defPkId.name: 1, defColCreatedAt.name: now}))); + }, + ); + }, + ); + }, + ); + }, + ); diff --git a/integration_test/framework/framework_test.dart b/integration_test/framework/framework_test.dart index 8d7e82e29..d609890ad 100644 --- a/integration_test/framework/framework_test.dart +++ b/integration_test/framework/framework_test.dart @@ -6,6 +6,8 @@ import 'database_factory_tests.dart' as database_factory_tests; import 'database_repository_tests.dart' as database_repository_tests; import 'database_tuple_repository_tests_v1.dart' as database_tuple_repository_tests_v1; +import 'database_tuple_repository_tests.dart' + as database_tuple_repository_tests; const _name = "Framework test"; @@ -23,5 +25,6 @@ void main() => group( database_repository_tests.main(); database_tuple_repository_tests_v1.main(); + database_tuple_repository_tests.main(); }, ); diff --git a/lib/framework/repository/database_tuple_repository.dart b/lib/framework/repository/database_tuple_repository.dart new file mode 100644 index 000000000..168fb1903 --- /dev/null +++ b/lib/framework/repository/database_tuple_repository.dart @@ -0,0 +1,63 @@ +import 'package:mem/databases/table_definitions/base.dart'; +import 'package:mem/framework/database/accessor.dart'; +import 'package:mem/framework/database/definition/database_definition.dart'; +import 'package:mem/framework/database/definition/table_definition.dart'; +import 'package:mem/framework/repository/condition/conditions.dart'; +import 'package:mem/framework/repository/database_repository.dart'; +import 'package:mem/framework/repository/entity.dart'; +import 'package:mem/framework/repository/repository.dart'; +import 'package:mem/logger/log_service.dart'; + +// FIXME byIdの引数の型のためにSavedEntityの型以外にIが必要になっている +// Rにidの型情報が含まれているのに改めて渡す必要があるのはおかしい +// DatabaseTupleに型情報を付与することでズレは発生しなくなった +// ただ、これだと未保存のDatabaseTupleが +// FIXME SavedEntityはSavedDatabaseTupleをmixinしている必要があるが型制約を定義できていない +abstract class DatabaseTupleRepository extends Repository { + final DatabaseDefinition _databaseDefinition; + final TableDefinition _tableDefinition; + + DatabaseTupleRepository(this._databaseDefinition, this._tableDefinition); + + DatabaseAccessor? _databaseAccessor; + + late final Future _dbA = (() async => _databaseAccessor ??= + await DatabaseRepository().receive(_databaseDefinition))(); + + Future count({ + Condition? condition, + }) => + v( + () async => (await _dbA).count( + _tableDefinition, + where: condition?.where(), + whereArgs: condition?.whereArgs(), + ), + { + 'condition': condition, + }, + ); + + E pack(Map map); + + Future receive(E entity, {DateTime? createdAt}) => v( + () async { + final entityMap = entity.toMap; + + entityMap[defColCreatedAt.name] = createdAt ?? DateTime.now(); + + final id = await _databaseAccessor!.insert( + _tableDefinition, + entityMap, + ); + + entityMap[defPkId.name] = id; + + return pack(entityMap); + }, + { + 'entity': entity, + 'createdAt': createdAt, + }, + ); +} diff --git a/lib/framework/repository/entity.dart b/lib/framework/repository/entity.dart index 57c2e1f0c..2ae9fa1bd 100644 --- a/lib/framework/repository/entity.dart +++ b/lib/framework/repository/entity.dart @@ -10,6 +10,21 @@ abstract class EntityV1 {} mixin Entity { Map get toMap; + + @override + String toString() => "${super.toString()}: $toMap"; + + @override + int get hashCode => toMap.entries.fold( + 0, + (value, element) => + value ^ element.key.hashCode ^ element.value.hashCode, + ); + + @override + bool operator ==(Object other) => + identical(this, other) || + (runtimeType == other.runtimeType && hashCode == other.hashCode); } // memo // - view, domain, dataのそれぞれの領域で似た内容でも型が変わることになるはず diff --git a/lib/framework/repository/repository.dart b/lib/framework/repository/repository.dart index 0611218cb..eb08f12b7 100644 --- a/lib/framework/repository/repository.dart +++ b/lib/framework/repository/repository.dart @@ -34,3 +34,5 @@ mixin Discarder on RepositoryV2 { abstract class RepositoryV1 { Future receive(E entity); } + +abstract class Repository {} diff --git a/test/framework/repository/database_tuple_entity_test.dart b/test/framework/repository/database_tuple_entity_test.dart index c8e1a7b41..536d42ea9 100644 --- a/test/framework/repository/database_tuple_entity_test.dart +++ b/test/framework/repository/database_tuple_entity_test.dart @@ -6,11 +6,11 @@ import 'entity_test.dart'; const _name = 'DatabaseTupleEntity test'; -class _TestObjectDatabaseTupleEntity extends TestObjectEntity +class TestObjectDatabaseTupleEntity extends TestObjectEntity with DatabaseTupleEntity { - _TestObjectDatabaseTupleEntity(super.a); + TestObjectDatabaseTupleEntity(super.a); - _TestObjectDatabaseTupleEntity.fromMap(Map map) + TestObjectDatabaseTupleEntity.fromMap(Map map) : super.fromMap(map) { withMap(map); } @@ -24,7 +24,7 @@ void main() => group( () { const a = false; - final testObject = _TestObjectDatabaseTupleEntity(a); + final testObject = TestObjectDatabaseTupleEntity(a); expect(testObject.a, equals(a)); }, @@ -41,7 +41,7 @@ void main() => group( defColArchivedAt.name: null }; - final testObject = _TestObjectDatabaseTupleEntity.fromMap(map); + final testObject = TestObjectDatabaseTupleEntity.fromMap(map); expect(testObject.a, map[TestObjectEntity.fieldNames[0]]); }, @@ -58,7 +58,7 @@ void main() => group( defColArchivedAt.name: null }; - final testObject = _TestObjectDatabaseTupleEntity.fromMap(map); + final testObject = TestObjectDatabaseTupleEntity.fromMap(map); expect(testObject.toMap, map); }, diff --git a/test/framework/repository/entity_test.dart b/test/framework/repository/entity_test.dart index 1c798a0b0..47bc819b6 100644 --- a/test/framework/repository/entity_test.dart +++ b/test/framework/repository/entity_test.dart @@ -57,5 +57,18 @@ void main() => group( testObject.toMap, equals({TestObjectEntity.fieldNames[0]: a})); }, ); + + test( + '#==', + () { + const a = false; + const b = false; + + final testObjectA = TestObjectEntity(a); + final testObjectB = TestObjectEntity(b); + + expect(testObjectA, equals(testObjectB)); + }, + ); }, );