import Testing import Foundation import GRDB @testable import Music @MainActor struct DBBackupFTS5Tests { // Pins down the root cause: does DatabaseService.backup() produce a copy whose // FTS5 `tracks_ft` table is functional? GRDB's ValueObservation introspects the // whole schema on start, and that introspection throws "no such table: tracks_ft" // if the FTS5 shadow tables didn't survive the copy. @Test func backupCopyHasFunctionalFTS5Table() throws { // 1. Build a source DB (DatabasePool/WAL, like the running app) with 3 tracks. let tempDir = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString) try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true) defer { try? FileManager.default.removeItem(at: tempDir) } let srcPath = tempDir.appendingPathComponent("src.sqlite").path let src = try DatabaseService(path: srcPath) for i in 1...3 { var t = Track.fixture(fileURL: "/s\(i).mp3", title: "Song \(i)") try src.insert(&t) } // 2. Copy the DB exactly as the host serves it today. let copyPath = tempDir.appendingPathComponent("copy.sqlite").path try src.backup(to: copyPath) // 3. Open the copy and run the same schema-introspection query GRDB runs when // a ValueObservation starts. If FTS5 didn't transfer, this throws. let copy = try DatabaseService(path: copyPath) try copy.dbPool.read { db in _ = try Row.fetchAll( db, sql: "SELECT rootpage, sql FROM sqlite_master WHERE (type = 'table' OR type = 'view')" ) // And an actual FTS5 query must work. _ = try Row.fetchAll(db, sql: "SELECT * FROM tracks_ft LIMIT 1") } } }