import 'package:nc_photos/db/entity_converter.dart'; import 'package:nc_photos/di_container.dart'; import 'package:nc_photos/entity/file.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("SqliteDbExtension", () { group("insertAccountOf", () { test("first", _insertAccountFirst); test("same server", _insertAccountSameServer); test("same account", _insertAccountSameAccount); }); group("deleteAccountOf", () { test("normal", _deleteAccount); test("same server", _deleteAccountSameServer); test("same server shared file", _deleteAccountSameServerSharedFile); }); test("truncate", _truncate); }); } /// Insert an Account to a empty db /// /// Expect: Account and Server inserted Future _insertAccountFirst() async { final account = util.buildAccount(); final c = DiContainer( npDb: util.buildTestDb(), ); addTearDown(() => c.sqliteDb.close()); await c.sqliteDb.use((db) async { await db.insertAccounts([account.toDb()]); }); expect( await util.listSqliteDbServerAccounts(c.sqliteDb), { const util.SqlAccountWithServer( compat.Server(rowId: 1, address: "http://example.com"), compat.Account(rowId: 1, server: 1, userId: "admin"), ), }, ); } /// Insert an Account with Server already exists in db /// /// Expect: Account and Server inserted Future _insertAccountSameServer() async { final account = util.buildAccount(); final user1Account = util.buildAccount(userId: "user1"); final c = DiContainer( npDb: util.buildTestDb(), ); addTearDown(() => c.sqliteDb.close()); await c.sqliteDb.transaction(() async { await c.sqliteDb.insertAccounts([account.toDb()]); }); await c.sqliteDb.use((db) async { await db.insertAccounts([user1Account.toDb()]); }); expect( await util.listSqliteDbServerAccounts(c.sqliteDb), { const util.SqlAccountWithServer( compat.Server(rowId: 1, address: "http://example.com"), compat.Account(rowId: 1, server: 1, userId: "admin"), ), const util.SqlAccountWithServer( compat.Server(rowId: 1, address: "http://example.com"), compat.Account(rowId: 2, server: 1, userId: "user1"), ), }, ); } /// Insert an Account with the same info as another entry /// /// Expect: Account not inserted Future _insertAccountSameAccount() async { final account = util.buildAccount(); final account2 = util.buildAccount(); final c = DiContainer( npDb: util.buildTestDb(), ); addTearDown(() => c.sqliteDb.close()); await c.sqliteDb.transaction(() async { await c.sqliteDb.insertAccounts([account.toDb()]); }); await c.sqliteDb.use((db) async { await db.insertAccounts([account2.toDb()]); }); expect( await util.listSqliteDbServerAccounts(c.sqliteDb), { const util.SqlAccountWithServer( compat.Server(rowId: 1, address: "http://example.com"), compat.Account(rowId: 1, server: 1, userId: "admin"), ), }, ); } /// Delete Account /// /// Expect: Account deleted; /// Server deleted; /// Associated Files deleted Future _deleteAccount() 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 c.sqliteDb.use((db) async { await db.deleteAccount(account.toDb()); }); expect( await util.listSqliteDbServerAccounts(c.sqliteDb), {}, ); expect( await util.listSqliteDbFiles(c.sqliteDb), {}, ); } /// Delete an Account having the same Server as other Accounts /// /// Expect: Account deleted; /// Server remained; /// Associated Files deleted Future _deleteAccountSameServer() async { final account = util.buildAccount(); final user1Account = util.buildAccount(userId: "user1"); final files = (util.FilesBuilder() ..addDir("admin") ..addJpeg("admin/test1.jpg")) .build(); final user1Files = (util.FilesBuilder(initialFileId: files.length) ..addDir("user1", ownerId: "user1") ..addJpeg("user1/test2.jpg", ownerId: "user1")) .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[1]]); await util.insertFiles(c.sqliteDb, user1Account, user1Files); await util.insertDirRelation( c.sqliteDb, user1Account, user1Files[0], [user1Files[1]]); }); await c.sqliteDb.use((db) async { await db.deleteAccount(account.toDb()); }); expect( await util.listSqliteDbServerAccounts(c.sqliteDb), { const util.SqlAccountWithServer( compat.Server(rowId: 1, address: "http://example.com"), compat.Account(rowId: 2, server: 1, userId: "user1"), ), }, ); expect( await util.listSqliteDbFiles(c.sqliteDb), {...user1Files}, ); } /// Delete an Account having the same Server as other Accounts and with files /// shared between them (i.e., 1 Files to many AccountFiles) /// /// Expect: Account deleted; /// Server remained; /// Associated Shared Files not deleted; Future _deleteAccountSameServerSharedFile() async { final account = util.buildAccount(); final user1Account = util.buildAccount(userId: "user1"); final files = (util.FilesBuilder() ..addDir("admin") ..addJpeg("admin/test1.jpg")) .build(); final user1Files = (util.FilesBuilder(initialFileId: files.length) ..addDir("user1", ownerId: "user1")) .build(); user1Files .add(files[0].copyWith(path: "remote.php/dav/files/user1/test1.jpg")); 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[1]]); await util.insertFiles(c.sqliteDb, user1Account, user1Files); await util.insertDirRelation( c.sqliteDb, user1Account, user1Files[0], [user1Files[1]]); }); await c.sqliteDb.use((db) async { await db.deleteAccount(account.toDb()); }); expect( await util.listSqliteDbServerAccounts(c.sqliteDb), { const util.SqlAccountWithServer( compat.Server(rowId: 1, address: "http://example.com"), compat.Account(rowId: 2, server: 1, userId: "user1"), ), }, ); expect( await util.listSqliteDbFiles(c.sqliteDb), {...user1Files}, ); } /// Truncate the db /// /// Expect: All tables emptied; /// Auto-increment counters reset to 0 Future _truncate() 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 c.sqliteDb.use((db) async { await db.truncate(); }); await c.sqliteDb.use((db) async { final tables = await db .customSelect( "SELECT name FROM sqlite_schema WHERE type='table' AND name NOT LIKE 'sqlite_%';") .map((r) => r.read("name")) .get(); // this check is to make sure that we are testing all tables expect(tables.toSet(), { "servers", "accounts", "files", "images", "image_locations", "trashes", "account_files", "dir_files", "albums", "album_shares", "tags", "face_recognition_persons", "nc_albums", "nc_album_items", "recognize_faces", "recognize_face_items", }); for (final t in tables) { expect( await db .customSelect("SELECT COUNT(*) AS c FROM $t;") .map((r) => r.read("c")) .getSingle(), 0, reason: "Table '$t' is not empty", ); } expect( (await db .customSelect("SELECT seq FROM sqlite_sequence;") .map((r) => r.read("seq")) .get()) .every((e) => e == 0), true, ); }); }