'sqlite', 'database.connections.sqlite.database' => ':memory:']); DB::purge(); DB::reconnect(); DB::connection()->getPdo()->sqliteCreateFunction('UNIX_TIMESTAMP', static fn (?string $value): int => strtotime((string) $value)); $this->createTables(); $this->seedSettings(); } public function test_retention_cleanup_deletes_parts_binaries_and_collections_without_fk_cascades(): void { DB::table('collections')->insert([ 'id' => 100, 'subject' => 'Retention.Release', 'fromname' => 'poster@example.com', 'date' => now()->subHours(10)->format('Y-m-d H:i:s'), 'dateadded' => now()->subHours(10)->format('Y-m-d H:i:s'), 'added' => now()->subHours(10)->format('Y-m-d H:i:s'), 'xref' => 'alt.test:123', 'groups_id' => 1, 'totalfiles' => 1, 'filesize' => 500, 'filecheck' => CollectionFileCheckStatus::Sized->value, 'collectionhash' => 'retention-hash', 'collection_regexes_id' => 0, 'releases_id' => null, 'noise' => '', ]); DB::table('binaries')->insert([ 'id' => 1000, 'name' => 'Retention.Release.par2', 'collections_id' => 100, 'totalparts' => 1, ]); DB::table('parts')->insert([ 'binaries_id' => 1000, 'number' => 1, 'messageid' => '', 'partnumber' => 1, 'size' => 10, ]); app(CollectionCleanupService::class)->deleteFinishedAndOrphans(false); $this->assertSame(0, DB::table('parts')->count()); $this->assertSame(0, DB::table('binaries')->count()); $this->assertSame(0, DB::table('collections')->count()); } public function test_nzb_creation_cleans_up_collection_binary_and_parts_explicitly(): void { DB::table('releases')->insert([ 'id' => 1, 'name' => 'Nzb.Release', 'searchname' => 'Nzb.Release', 'totalpart' => 1, 'groups_id' => 1, 'adddate' => now()->format('Y-m-d H:i:s'), 'guid' => str_repeat('a', 36), 'leftguid' => 'a', 'postdate' => now()->format('Y-m-d H:i:s'), 'fromname' => 'poster@example.com', 'size' => 500, 'passwordstatus' => 0, 'haspreview' => -1, 'categories_id' => 1, 'nfostatus' => -1, 'nzbstatus' => NzbService::NZB_NONE, 'isrenamed' => 1, 'iscategorized' => 1, 'predb_id' => 0, 'source' => null, ]); DB::table('collections')->insert([ 'id' => 200, 'subject' => 'Nzb.Release', 'fromname' => 'poster@example.com', 'date' => now()->subHours(1)->format('Y-m-d H:i:s'), 'dateadded' => now()->subHours(1)->format('Y-m-d H:i:s'), 'added' => now()->subHours(1)->format('Y-m-d H:i:s'), 'xref' => 'alt.test:200', 'groups_id' => 1, 'totalfiles' => 1, 'filesize' => 500, 'filecheck' => CollectionFileCheckStatus::Inserted->value, 'collectionhash' => 'nzb-hash', 'collection_regexes_id' => 0, 'releases_id' => 1, 'noise' => '', ]); DB::table('binaries')->insert([ 'id' => 2000, 'name' => 'Nzb.Release yEnc', 'collections_id' => 200, 'totalparts' => 1, ]); DB::table('parts')->insert([ 'binaries_id' => 2000, 'number' => 1, 'messageid' => '', 'partnumber' => 1, 'size' => 10, ]); $release = Release::query()->findOrFail(1); $release->setRelation('category', (object) ['title' => 'Misc', 'parent' => (object) ['title' => 'Other']]); $written = app(NzbService::class)->writeNzbForReleaseId($release); $this->assertTrue($written); $this->assertSame(0, DB::table('parts')->count()); $this->assertSame(0, DB::table('binaries')->count()); $this->assertSame(0, DB::table('collections')->count()); $this->assertSame(1, (int) DB::table('releases')->where('id', 1)->value('nzbstatus')); } public function test_duplicate_release_path_cleans_up_collection_binary_and_parts(): void { DB::table('releases')->insert([ 'id' => 2, 'name' => 'Duplicate.Release', 'searchname' => 'Duplicate.Release', 'totalpart' => 1, 'groups_id' => 1, 'adddate' => now()->format('Y-m-d H:i:s'), 'guid' => str_repeat('b', 36), 'leftguid' => 'b', 'postdate' => now()->format('Y-m-d H:i:s'), 'fromname' => 'poster@example.com', 'size' => 1000, 'passwordstatus' => 0, 'haspreview' => -1, 'categories_id' => 1, 'nfostatus' => -1, 'nzbstatus' => NzbService::NZB_NONE, 'isrenamed' => 1, 'iscategorized' => 1, 'predb_id' => 0, 'source' => null, ]); DB::table('collections')->insert([ 'id' => 300, 'subject' => 'Duplicate.Release', 'fromname' => 'poster@example.com', 'date' => now()->subHours(1)->format('Y-m-d H:i:s'), 'dateadded' => now()->subHours(1)->format('Y-m-d H:i:s'), 'added' => now()->subHours(1)->format('Y-m-d H:i:s'), 'xref' => 'alt.test:300', 'groups_id' => 1, 'totalfiles' => 1, 'filesize' => 1000, 'filecheck' => CollectionFileCheckStatus::Sized->value, 'collectionhash' => 'duplicate-hash', 'collection_regexes_id' => 0, 'releases_id' => null, 'noise' => '', ]); DB::table('binaries')->insert([ 'id' => 3000, 'name' => 'Duplicate.Release yEnc', 'collections_id' => 300, 'totalparts' => 1, ]); DB::table('parts')->insert([ 'binaries_id' => 3000, 'number' => 1, 'messageid' => '', 'partnumber' => 1, 'size' => 10, ]); $service = new ReleaseCreationService( app(ReleaseCleaningService::class), app(CollectionCleanupService::class) ); $result = $service->createReleases(null, 10, false); $this->assertSame(['added' => 0, 'dupes' => 1], $result); $this->assertSame(0, DB::table('parts')->count()); $this->assertSame(0, DB::table('binaries')->count()); $this->assertSame(0, DB::table('collections')->count()); } private function seedSettings(): void { $settings = [ 'partretentionhours' => '1', 'nzbsplitlevel' => '1', 'check_passworded_rars' => '0', 'categorizeforeign' => '1', 'catwebdl' => '1', ]; foreach ($settings as $name => $value) { DB::table('settings')->insert(['name' => $name, 'value' => $value]); } } private function createTables(): void { DB::statement('CREATE TABLE settings (name VARCHAR(255) PRIMARY KEY, value TEXT)'); DB::statement('CREATE TABLE usenet_groups (id INTEGER PRIMARY KEY, name VARCHAR(255))'); DB::statement('CREATE TABLE categories (id INTEGER PRIMARY KEY, title VARCHAR(255), parent_categories_id INTEGER NULL)'); DB::statement('CREATE TABLE releases ( id INTEGER PRIMARY KEY, name VARCHAR(255), searchname VARCHAR(255), totalpart INTEGER, groups_id INTEGER, adddate DATETIME NULL, guid VARCHAR(64), leftguid VARCHAR(1), postdate DATETIME NULL, fromname VARCHAR(255), size INTEGER, passwordstatus INTEGER, haspreview INTEGER, categories_id INTEGER, nfostatus INTEGER, nzbstatus INTEGER, isrenamed INTEGER, iscategorized INTEGER, predb_id INTEGER, source VARCHAR(255) NULL )'); DB::statement('CREATE TABLE collections ( id INTEGER PRIMARY KEY, subject VARCHAR(255), fromname VARCHAR(255), date DATETIME NULL, dateadded DATETIME NULL, added DATETIME NULL, xref TEXT, groups_id INTEGER, totalfiles INTEGER, filesize INTEGER, filecheck INTEGER, collectionhash VARCHAR(255), collection_regexes_id INTEGER, releases_id INTEGER NULL, noise VARCHAR(64) )'); DB::statement('CREATE TABLE binaries ( id INTEGER PRIMARY KEY, name VARCHAR(255), collections_id INTEGER, totalparts INTEGER )'); DB::statement('CREATE TABLE parts ( binaries_id INTEGER, number INTEGER, messageid VARCHAR(255), partnumber INTEGER, size INTEGER )'); DB::table('usenet_groups')->insert(['id' => 1, 'name' => 'alt.test']); DB::table('categories')->insert(['id' => 1, 'title' => 'Misc', 'parent_categories_id' => null]); } }