import XCTest /// Pre-suite backend data seeding. /// /// Runs before all other suites (alphabetically `Suite00` < `Suite0_`). /// Makes direct API calls via `TestAccountAPIClient` — no app launch needed. /// Every step is idempotent: existing data is reused, missing data is created. final class Suite00_SeedTests: XCTestCase { override func setUpWithError() throws { try super.setUpWithError() continueAfterFailure = false } // MARK: - 1. Gate Check func test01_backendIsReachable() throws { guard TestAccountAPIClient.isBackendReachable() else { throw XCTSkip("Local backend is not reachable at \(TestAccountAPIClient.baseURL). Start the server and re-run.") } } // MARK: - 2. Seed Test User Account func test02_seedTestUserAccount() throws { let u = SeededTestData.TestUser.self // Try logging in first (account may already exist and be verified) if let auth = TestAccountAPIClient.login(username: u.username, password: u.password) { SeededTestData.testUserToken = auth.token return } // Account doesn't exist or password is wrong — register + verify + login guard let session = TestAccountAPIClient.createVerifiedAccount( username: u.username, email: u.email, password: u.password ) else { XCTFail("Failed to create verified test user account '\(u.username)'") return } SeededTestData.testUserToken = session.token } // MARK: - 3. Seed Admin Account func test03_seedAdminAccount() throws { let u = SeededTestData.AdminUser.self if let auth = TestAccountAPIClient.login(username: u.username, password: u.password) { SeededTestData.adminUserToken = auth.token return } guard let session = TestAccountAPIClient.createVerifiedAccount( username: u.username, email: u.email, password: u.password ) else { XCTFail("Failed to create verified admin account '\(u.username)'") return } SeededTestData.adminUserToken = session.token } // MARK: - 4. Seed Baseline Residence func test04_seedBaselineResidence() throws { let token = try requireTestUserToken() // Check if "Seed Home" already exists if let residences = TestAccountAPIClient.listResidences(token: token), let existing = residences.first(where: { $0.name == SeededTestData.Residence.name }) { SeededTestData.Residence.id = existing.id return } // Create it guard let residence = TestAccountAPIClient.createResidence( token: token, name: SeededTestData.Residence.name ) else { XCTFail("Failed to create seed residence '\(SeededTestData.Residence.name)'") return } SeededTestData.Residence.id = residence.id } // MARK: - 5. Seed Baseline Task func test05_seedBaselineTask() throws { let token = try requireTestUserToken() let residenceId = try requireResidenceId() // Check if "Seed Task" already exists in the residence if let tasks = TestAccountAPIClient.listTasksByResidence(token: token, residenceId: residenceId), let existing = tasks.first(where: { $0.title == SeededTestData.Task.title }) { SeededTestData.Task.id = existing.id return } guard let task = TestAccountAPIClient.createTask( token: token, residenceId: residenceId, title: SeededTestData.Task.title ) else { XCTFail("Failed to create seed task '\(SeededTestData.Task.title)'") return } SeededTestData.Task.id = task.id } // MARK: - 6. Seed Baseline Contractor func test06_seedBaselineContractor() throws { let token = try requireTestUserToken() if let contractors = TestAccountAPIClient.listContractors(token: token), let existing = contractors.first(where: { $0.name == SeededTestData.Contractor.name }) { SeededTestData.Contractor.id = existing.id return } guard let contractor = TestAccountAPIClient.createContractor( token: token, name: SeededTestData.Contractor.name ) else { XCTFail("Failed to create seed contractor '\(SeededTestData.Contractor.name)'") return } SeededTestData.Contractor.id = contractor.id } // MARK: - 7. Seed Baseline Document func test07_seedBaselineDocument() throws { let token = try requireTestUserToken() let residenceId = try requireResidenceId() if let documents = TestAccountAPIClient.listDocuments(token: token), let existing = documents.first(where: { $0.title == SeededTestData.Document.title }) { SeededTestData.Document.id = existing.id return } guard let document = TestAccountAPIClient.createDocument( token: token, residenceId: residenceId, title: SeededTestData.Document.title ) else { XCTFail("Failed to create seed document '\(SeededTestData.Document.title)'") return } SeededTestData.Document.id = document.id } // MARK: - 8. Verification func test08_verifySeedingComplete() { XCTAssertNotNil(SeededTestData.testUserToken, "testuser token should be set") XCTAssertNotNil(SeededTestData.adminUserToken, "admin token should be set") XCTAssertNotEqual(SeededTestData.Residence.id, -1, "Seed residence ID should be populated") XCTAssertNotEqual(SeededTestData.Task.id, -1, "Seed task ID should be populated") XCTAssertNotEqual(SeededTestData.Contractor.id, -1, "Seed contractor ID should be populated") XCTAssertNotEqual(SeededTestData.Document.id, -1, "Seed document ID should be populated") XCTAssertTrue(SeededTestData.isSeeded, "All seeded data should be present") } // MARK: - Helpers private func requireTestUserToken(file: StaticString = #filePath, line: UInt = #line) throws -> String { guard let token = SeededTestData.testUserToken else { throw XCTSkip("testuser token not available — earlier seed step likely failed") } return token } private func requireResidenceId(file: StaticString = #filePath, line: UInt = #line) throws -> Int { guard SeededTestData.Residence.id != -1 else { throw XCTSkip("Seed residence not available — test04 likely failed") } return SeededTestData.Residence.id } }