import 'package:nc_photos/db/entity_converter.dart'; import 'package:nc_photos/di_container.dart'; import 'package:nc_photos/entity/file.dart'; import 'package:nc_photos/entity/file/data_source2.dart'; import 'package:nc_photos/entity/file_util.dart' as file_util; import 'package:np_collection/np_collection.dart'; import 'package:np_common/or_null.dart'; import 'package:np_db_sqlite/np_db_sqlite_compat.dart' as compat; import 'package:test/test.dart'; import '../../test_util.dart' as util; void main() { group("FileNpDbDataSource", () { group("getFileDescriptors", () { test("normal", _getFileDescriptors); test("multiple account", _getFileDescriptorsMultipleAccount); test("share folder", _getFileDescriptorsShareFolder); test("extra share folder", _getFileDescriptorsExtraShareFolder); }); group("remove", () { test("file", _removeFile); test("empty dir", _removeEmptyDir); test("dir w/ file", _removeDir); test("dir w/ sub dir", _removeDirWithSubDir); }); group("updateProperty", () { test("file properties", _updateFileProperty); test("update metadata", _updateMetadata); test("add metadata", _updateAddMetadata); test("delete metadata", _updateDeleteMetadata); }); }); } /// Return files of an account /// /// Files: admin/test1.jpg, admin/test/test2.jpg /// Expect: admin/test1.jpg, admin/test/test2.jpg Future _getFileDescriptors() async { final account = util.buildAccount(); final files = (util.FilesBuilder() ..addDir("admin") ..addJpeg("admin/test1.jpg") ..addDir("admin/test") ..addJpeg("admin/test/test2.jpg")) .build(); final c = DiContainer( npDb: util.buildTestDb(), ); addTearDown(() => c.sqliteDb.close()); await c.sqliteDb.transaction(() async { await c.sqliteDb.insertAccounts([account.toDb()]); await util.insertFiles(c.sqliteDb, account, files); await util.insertDirRelation( c.sqliteDb, account, files[0], files.slice(1, 3)); await util.insertDirRelation(c.sqliteDb, account, files[2], [files[3]]); }); final src = FileNpDbDataSource(c.npDb); expect( await src.getFileDescriptors(account, file_util.unstripPath(account, "")), [files[3].toDescriptor(), files[1].toDescriptor()], ); } /// Return files of an account /// /// Files: admin/test1.jpg, admin/test/test2.jpg, user1/test3.jpg /// Expect: admin/test1.jpg, admin/test/test2.jpg Future _getFileDescriptorsMultipleAccount() async { final account = util.buildAccount(); final user1Account = util.buildAccount(userId: "user1"); final files = (util.FilesBuilder() ..addDir("admin") ..addJpeg("admin/test1.jpg") ..addDir("admin/test") ..addJpeg("admin/test/test2.jpg")) .build(); final user1Files = (util.FilesBuilder(initialFileId: files.length) ..addDir("user1", ownerId: "user1") ..addJpeg("user1/test3.jpg")) .build(); final c = DiContainer( npDb: util.buildTestDb(), ); addTearDown(() => c.sqliteDb.close()); await c.sqliteDb.transaction(() async { await c.sqliteDb.insertAccounts([account.toDb()]); await c.sqliteDb.insertAccounts([user1Account.toDb()]); await util.insertFiles(c.sqliteDb, account, files); await util.insertDirRelation( c.sqliteDb, account, files[0], files.slice(1, 3)); await util.insertDirRelation(c.sqliteDb, account, files[2], [files[3]]); await util.insertFiles(c.sqliteDb, user1Account, user1Files); await util.insertDirRelation( c.sqliteDb, user1Account, user1Files[0], [user1Files[1]]); }); final src = FileNpDbDataSource(c.npDb); expect( await src.getFileDescriptors(account, file_util.unstripPath(account, "")), [files[3].toDescriptor(), files[1].toDescriptor()], ); expect( await src.getFileDescriptors( user1Account, file_util.unstripPath(user1Account, "")), [user1Files[1].toDescriptor()], ); } /// Return files of an account /// /// Files: admin/test1/test1.jpg, admin/test2/test2.jpg /// Expect: admin/test1/test1.jpg Future _getFileDescriptorsShareFolder() async { final account = util.buildAccount(roots: ["test1"]); final files = (util.FilesBuilder() ..addDir("admin") ..addDir("admin/test1") ..addJpeg("admin/test1/test1.jpg") ..addDir("admin/test2") ..addJpeg("admin/test2/test2.jpg")) .build(); final c = DiContainer( npDb: util.buildTestDb(), ); addTearDown(() => c.sqliteDb.close()); await c.sqliteDb.transaction(() async { await c.sqliteDb.insertAccounts([account.toDb()]); await util.insertFiles(c.sqliteDb, account, files); await util .insertDirRelation(c.sqliteDb, account, files[0], [files[1], files[3]]); await util.insertDirRelation(c.sqliteDb, account, files[1], [files[2]]); await util.insertDirRelation(c.sqliteDb, account, files[3], [files[4]]); }); final src = FileNpDbDataSource(c.npDb); expect( await src.getFileDescriptors( account, file_util.unstripPath(account, "test1")), [files[2].toDescriptor()], ); } /// Return files of an account /// /// Files: admin/test1/test1.jpg, admin/test2/test2.jpg /// Expect: admin/test1/test1.jpg, admin/test2/test2.jpg Future _getFileDescriptorsExtraShareFolder() async { final account = util.buildAccount(roots: ["test1"]); final files = (util.FilesBuilder() ..addDir("admin") ..addDir("admin/test1") ..addJpeg("admin/test1/test1.jpg") ..addDir("admin/test2") ..addJpeg("admin/test2/test2.jpg")) .build(); final c = DiContainer( npDb: util.buildTestDb(), ); addTearDown(() => c.sqliteDb.close()); await c.sqliteDb.transaction(() async { await c.sqliteDb.insertAccounts([account.toDb()]); await util.insertFiles(c.sqliteDb, account, files); await util .insertDirRelation(c.sqliteDb, account, files[0], [files[1], files[3]]); await util.insertDirRelation(c.sqliteDb, account, files[1], [files[2]]); await util.insertDirRelation(c.sqliteDb, account, files[3], [files[4]]); }); final src = FileNpDbDataSource(c.npDb); expect( await src.getFileDescriptors( account, file_util.unstripPath(account, "test2")), [files[4].toDescriptor(), files[2].toDescriptor()], ); } /// Remove a file /// /// Expect: entry removed from Files table Future _removeFile() async { final account = util.buildAccount(); final files = (util.FilesBuilder() ..addDir("admin") ..addJpeg("admin/test1.jpg")) .build(); final c = DiContainer( npDb: util.buildTestDb(), ); addTearDown(() => c.sqliteDb.close()); await c.sqliteDb.transaction(() async { await c.sqliteDb.insertAccounts([account.toDb()]); await util.insertFiles(c.sqliteDb, account, files); await util.insertDirRelation(c.sqliteDb, account, files[0], [files[1]]); }); final src = FileNpDbDataSource(c.npDb); await src.remove(account, files[1]); expect( await util.listSqliteDbFiles(c.sqliteDb), {files[0]}, ); } /// Remove an empty dir /// /// Expect: entry removed from DirFiles table Future _removeEmptyDir() async { final account = util.buildAccount(); final files = (util.FilesBuilder() ..addDir("admin") ..addDir("admin/test")) .build(); final c = DiContainer( npDb: util.buildTestDb(), ); addTearDown(() => c.sqliteDb.close()); await c.sqliteDb.transaction(() async { await c.sqliteDb.insertAccounts([account.toDb()]); await util.insertFiles(c.sqliteDb, account, files); await util.insertDirRelation(c.sqliteDb, account, files[0], [files[1]]); await util.insertDirRelation(c.sqliteDb, account, files[1], const []); }); final src = FileNpDbDataSource(c.npDb); await src.remove(account, files[1]); // parent dir is not updated, parent dir is only updated when syncing with // remote expect( await util.listSqliteDbDirs(c.sqliteDb), { files[0]: {files[0]}, }, ); } /// Remove a dir with file /// /// Expect: file entries under the dir removed from Files table Future _removeDir() async { final account = util.buildAccount(); final files = (util.FilesBuilder() ..addDir("admin") ..addDir("admin/test") ..addJpeg("admin/test/test1.jpg")) .build(); final c = DiContainer( npDb: util.buildTestDb(), ); addTearDown(() => c.sqliteDb.close()); await c.sqliteDb.transaction(() async { await c.sqliteDb.insertAccounts([account.toDb()]); await util.insertFiles(c.sqliteDb, account, files); await util.insertDirRelation(c.sqliteDb, account, files[0], [files[1]]); await util.insertDirRelation(c.sqliteDb, account, files[1], [files[2]]); }); final src = FileNpDbDataSource(c.npDb); await src.remove(account, files[1]); expect( await util.listSqliteDbFiles(c.sqliteDb), {files[0]}, ); } /// Remove a dir with file /// /// Expect: file entries under the dir removed from Files table Future _removeDirWithSubDir() async { final account = util.buildAccount(); final files = (util.FilesBuilder() ..addDir("admin") ..addDir("admin/test") ..addDir("admin/test/test2") ..addJpeg("admin/test/test2/test3.jpg")) .build(); final c = DiContainer( npDb: util.buildTestDb(), ); addTearDown(() => c.sqliteDb.close()); await c.sqliteDb.transaction(() async { await c.sqliteDb.insertAccounts([account.toDb()]); await util.insertFiles(c.sqliteDb, account, files); await util.insertDirRelation(c.sqliteDb, account, files[0], [files[1]]); await util.insertDirRelation(c.sqliteDb, account, files[1], [files[2]]); await util.insertDirRelation(c.sqliteDb, account, files[2], [files[3]]); }); final src = FileNpDbDataSource(c.npDb); await src.remove(account, files[1]); expect( await util.listSqliteDbDirs(c.sqliteDb), { files[0]: {files[0]} }, ); expect( await util.listSqliteDbFiles(c.sqliteDb), {files[0]}, ); } /// Update the properties of a file /// /// Expect: file's property updated in Files table Future _updateFileProperty() async { final account = util.buildAccount(); final files = (util.FilesBuilder() ..addDir("admin") ..addJpeg("admin/test1.jpg")) .build(); final c = DiContainer( npDb: util.buildTestDb(), ); addTearDown(() => c.sqliteDb.close()); await c.sqliteDb.transaction(() async { await c.sqliteDb.insertAccounts([account.toDb()]); await util.insertFiles(c.sqliteDb, account, files); await util.insertDirRelation(c.sqliteDb, account, files[0], [files[1]]); }); final src = FileNpDbDataSource(c.npDb); await src.updateProperty( account, files[1], isArchived: const OrNull(true), overrideDateTime: OrNull(DateTime.utc(2020, 1, 2, 3, 4, 5)), ); final expectFile = files[1].copyWith( isArchived: const OrNull(true), overrideDateTime: OrNull(DateTime.utc(2020, 1, 2, 3, 4, 5)), ); expect( await util.listSqliteDbFiles(c.sqliteDb), {files[0], expectFile}, ); } /// Update metadata of a file /// /// Expect: Metadata updated in Images table Future _updateMetadata() async { final account = util.buildAccount(); final files = (util.FilesBuilder() ..addDir("admin") ..addJpeg("admin/test1.jpg")) .build(); files[1] = files[1].copyWith( metadata: OrNull(Metadata( lastUpdated: DateTime.utc(2020, 1, 2, 3, 4, 5), imageWidth: 123, )), ); final c = DiContainer( npDb: util.buildTestDb(), ); addTearDown(() => c.sqliteDb.close()); await c.sqliteDb.transaction(() async { await c.sqliteDb.insertAccounts([account.toDb()]); await util.insertFiles(c.sqliteDb, account, files); await util.insertDirRelation(c.sqliteDb, account, files[0], [files[1]]); }); final src = FileNpDbDataSource(c.npDb); await src.updateProperty( account, files[1], metadata: OrNull(Metadata( lastUpdated: DateTime.utc(2021, 1, 2, 3, 4, 5), imageWidth: 321, imageHeight: 123, )), ); final expectFile = files[1].copyWith( metadata: OrNull(Metadata( lastUpdated: DateTime.utc(2021, 1, 2, 3, 4, 5), imageWidth: 321, imageHeight: 123, )), ); expect( await util.listSqliteDbFiles(c.sqliteDb), {files[0], expectFile}, ); } /// Add metadata to a file /// /// Expect: Metadata added to Images table Future _updateAddMetadata() async { final account = util.buildAccount(); final files = (util.FilesBuilder() ..addDir("admin") ..addJpeg("admin/test1.jpg")) .build(); final c = DiContainer( npDb: util.buildTestDb(), ); addTearDown(() => c.sqliteDb.close()); await c.sqliteDb.transaction(() async { await c.sqliteDb.insertAccounts([account.toDb()]); await util.insertFiles(c.sqliteDb, account, files); await util.insertDirRelation(c.sqliteDb, account, files[0], [files[1]]); }); final src = FileNpDbDataSource(c.npDb); await src.updateProperty( account, files[1], metadata: OrNull(Metadata( lastUpdated: DateTime.utc(2020, 1, 2, 3, 4, 5), imageWidth: 123, )), ); final expectFile = files[1].copyWith( metadata: OrNull(Metadata( lastUpdated: DateTime.utc(2020, 1, 2, 3, 4, 5), imageWidth: 123, )), ); expect( await util.listSqliteDbFiles(c.sqliteDb), {files[0], expectFile}, ); } /// Delete metadata of a file /// /// Expect: Metadata deleted from Images table Future _updateDeleteMetadata() async { final account = util.buildAccount(); final files = (util.FilesBuilder() ..addDir("admin") ..addJpeg("admin/test1.jpg")) .build(); files[1] = files[1].copyWith( metadata: OrNull(Metadata( lastUpdated: DateTime.utc(2020, 1, 2, 3, 4, 5), imageWidth: 123, )), ); final c = DiContainer( npDb: util.buildTestDb(), ); addTearDown(() => c.sqliteDb.close()); await c.sqliteDb.transaction(() async { await c.sqliteDb.insertAccounts([account.toDb()]); await util.insertFiles(c.sqliteDb, account, files); await util.insertDirRelation(c.sqliteDb, account, files[0], [files[1]]); }); final src = FileNpDbDataSource(c.npDb); await src.updateProperty( account, files[1], metadata: const OrNull(null), ); final expectFile = files[1].copyWith( metadata: const OrNull(null), ); expect( await util.listSqliteDbFiles(c.sqliteDb), {files[0], expectFile}, ); }