// // IntegrationTests.swift // ReflectTests // // Integration tests verifying full lifecycle pipelines. // import XCTest import SwiftData @testable import Reflect @MainActor final class IntegrationTests: XCTestCase { var sut: DataController! override func setUp() { super.setUp() let schema = Schema([MoodEntryModel.self]) let config = ModelConfiguration(schema: schema, isStoredInMemoryOnly: true) sut = DataController(container: try! ModelContainer(for: schema, configurations: [config])) } override func tearDown() { sut = nil super.tearDown() } // MARK: - Phase 8: Integration Pipelines func test_fullCRUD_lifecycle() { let date = makeDate(2024, 6, 15) // Create sut.add(mood: .good, forDate: date, entryType: .listView) XCTAssertNotNil(sut.getEntry(byDate: date)) // Read let entry = sut.getEntry(byDate: date) XCTAssertEqual(entry?.mood, .good) // Update let updated = sut.update(entryDate: date, withMood: .great) XCTAssertTrue(updated) XCTAssertEqual(sut.getEntry(byDate: date)?.mood, .great) // Update notes sut.updateNotes(forDate: date, notes: "Lifecycle test") XCTAssertEqual(sut.getEntry(byDate: date)?.notes, "Lifecycle test") // Delete sut.deleteAllEntries(forDate: date) XCTAssertNil(sut.getEntry(byDate: date)) } func test_streak_throughLifecycle() { let today = Calendar.current.startOfDay(for: Date()) let yesterday = Calendar.current.date(byAdding: .day, value: -1, to: today)! // Add today → streak=1 sut.add(mood: .good, forDate: today, entryType: .listView) XCTAssertEqual(sut.calculateStreak(from: today).streak, 1) // Add yesterday → streak=2 sut.add(mood: .great, forDate: yesterday, entryType: .listView) XCTAssertEqual(sut.calculateStreak(from: today).streak, 2) // Update today's mood → streak still 2 sut.update(entryDate: today, withMood: .average) XCTAssertEqual(sut.calculateStreak(from: today).streak, 2) // Delete today → streak=1 (counting from yesterday) sut.deleteAllEntries(forDate: today) XCTAssertEqual(sut.calculateStreak(from: today).streak, 1) } func test_voteStatus_throughLifecycle() { let onboarding = OnboardingData() onboarding.inputDay = .Today // Set unlock time to 00:01 so we're always "after" unlock var components = Calendar.current.dateComponents([.year, .month, .day], from: Date()) components.hour = 0 components.minute = 1 onboarding.date = Calendar.current.date(from: components)! let votingDate = ShowBasedOnVoteLogics.getCurrentVotingDate(onboardingData: onboarding) // Helper: check vote status using testable DataController func isVoteNeeded() -> Bool { let entry = sut.getEntry(byDate: votingDate.startOfDay) return entry == nil || entry?.mood == .missing } // No entry → vote needed XCTAssertTrue(isVoteNeeded()) // Add entry → no vote needed sut.add(mood: .great, forDate: votingDate, entryType: .listView) XCTAssertFalse(isVoteNeeded()) // Delete entry → vote needed again sut.deleteAllEntries(forDate: votingDate) XCTAssertTrue(isVoteNeeded()) } func test_duplicateRemoval() { let date = makeDate(2024, 6, 15) // Manually insert 3 entries for same date (simulating CloudKit conflict) let entry1 = MoodEntryModel(forDate: date, mood: .great, entryType: .listView) entry1.timestamp = makeDate(2024, 6, 15, hour: 10) sut.modelContext.insert(entry1) let entry2 = MoodEntryModel(forDate: date, mood: .good, entryType: .listView) entry2.timestamp = makeDate(2024, 6, 15, hour: 8) sut.modelContext.insert(entry2) let entry3 = MoodEntryModel(forDate: date, mood: .missing, entryType: .filledInMissing) entry3.timestamp = makeDate(2024, 6, 15, hour: 12) sut.modelContext.insert(entry3) sut.save() let removed = sut.removeDuplicates() XCTAssertEqual(removed, 2) XCTAssertEqual(sut.getAllEntries(byDate: date).count, 1) } func test_dataListeners_fireOnMutation() { var listenerCallCount = 0 sut.addNewDataListener { listenerCallCount += 1 } // add() calls saveAndRunDataListeners internally sut.add(mood: .good, forDate: makeDate(2024, 6, 15), entryType: .listView) // add() does: delete-save (if existing) + insert + saveAndRunDataListeners // For a fresh add with no existing entry, listener fires once from saveAndRunDataListeners XCTAssertGreaterThanOrEqual(listenerCallCount, 1, "Listener should fire at least once on mutation") } }