Rebrand entire project from Feels to Reflect
Complete rename across all bundle IDs, App Groups, CloudKit containers, StoreKit product IDs, data store filenames, URL schemes, logger subsystems, Swift identifiers, user-facing strings (7 languages), file names, directory names, Xcode project, schemes, assets, and documentation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
141
ReflectTests/IntegrationTests.swift
Normal file
141
ReflectTests/IntegrationTests.swift
Normal file
@@ -0,0 +1,141 @@
|
||||
//
|
||||
// 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")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user