Files
Reflect/FeelsTests/VoteLogicsTests.swift
Trey t 7639f881da Add debug bypass subscription toggle, tests, and data layer improvements
- Add runtime toggle in Settings (DEBUG only) to bypass subscription/hide trial banner
- IAPManager.bypassSubscription is now a @Published var persisted via UserDefaults
- Hide upgrade banner in SettingsTabView and trial warnings when bypass is enabled
- Add FeelsTests directory with integration tests
- Update DataController, DataControllerGET, DataControllerUPDATE
- Update Xcode project and scheme configuration
- Update localization strings and App Store screen docs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 17:12:56 -06:00

158 lines
6.5 KiB
Swift

//
// VoteLogicsTests.swift
// FeelsTests
//
// Tests for ShowBasedOnVoteLogics vote status and timing.
//
import XCTest
import SwiftData
@testable import Feels
@MainActor
final class VoteLogicsTests: XCTestCase {
// MARK: - Phase 7: passedTodaysVotingUnlock Tests
func test_passedVotingUnlock_beforeTime() {
// Voting unlock set to 6:00 PM, current time is 2:00 PM
let voteDate = makeDateWithTime(hour: 18, minute: 0)
let now = makeDateWithTime(hour: 14, minute: 0)
let result = ShowBasedOnVoteLogics.passedTodaysVotingUnlock(voteDate: voteDate, now: now)
XCTAssertFalse(result, "Should not have passed unlock when current time is before vote time")
}
func test_passedVotingUnlock_afterTime() {
// Voting unlock set to 6:00 PM, current time is 8:00 PM
let voteDate = makeDateWithTime(hour: 18, minute: 0)
let now = makeDateWithTime(hour: 20, minute: 0)
let result = ShowBasedOnVoteLogics.passedTodaysVotingUnlock(voteDate: voteDate, now: now)
XCTAssertTrue(result, "Should have passed unlock when current time is after vote time")
}
func test_passedVotingUnlock_exactTime() {
// Voting unlock set to 6:00 PM, current time is exactly 6:00 PM
let voteDate = makeDateWithTime(hour: 18, minute: 0)
let now = makeDateWithTime(hour: 18, minute: 0)
let result = ShowBasedOnVoteLogics.passedTodaysVotingUnlock(voteDate: voteDate, now: now)
XCTAssertTrue(result, "Should have passed unlock at exact vote time (uses >=)")
}
// MARK: - Phase 7: getCurrentVotingDate Tests
func test_votingDate_today_beforeTime() {
let onboarding = OnboardingData()
onboarding.inputDay = .Today
onboarding.date = makeDateWithTime(hour: 18, minute: 0)
let now = makeDateWithTime(hour: 14, minute: 0) // before 6pm
let result = ShowBasedOnVoteLogics.getCurrentVotingDate(onboardingData: onboarding, now: now)
let yesterday = Calendar.current.date(byAdding: .day, value: -1, to: now)!
XCTAssertTrue(Calendar.current.isDate(result, inSameDayAs: yesterday),
"Today mode, before unlock: should return yesterday")
}
func test_votingDate_today_afterTime() {
let onboarding = OnboardingData()
onboarding.inputDay = .Today
onboarding.date = makeDateWithTime(hour: 18, minute: 0)
let now = makeDateWithTime(hour: 20, minute: 0) // after 6pm
let result = ShowBasedOnVoteLogics.getCurrentVotingDate(onboardingData: onboarding, now: now)
XCTAssertTrue(Calendar.current.isDate(result, inSameDayAs: now),
"Today mode, after unlock: should return today")
}
func test_votingDate_previous_beforeTime() {
let onboarding = OnboardingData()
onboarding.inputDay = .Previous
onboarding.date = makeDateWithTime(hour: 18, minute: 0)
let now = makeDateWithTime(hour: 14, minute: 0) // before 6pm
let result = ShowBasedOnVoteLogics.getCurrentVotingDate(onboardingData: onboarding, now: now)
let twoDaysAgo = Calendar.current.date(byAdding: .day, value: -2, to: now)!
XCTAssertTrue(Calendar.current.isDate(result, inSameDayAs: twoDaysAgo),
"Previous mode, before unlock: should return 2 days ago")
}
func test_votingDate_previous_afterTime() {
let onboarding = OnboardingData()
onboarding.inputDay = .Previous
onboarding.date = makeDateWithTime(hour: 18, minute: 0)
let now = makeDateWithTime(hour: 20, minute: 0) // after 6pm
let result = ShowBasedOnVoteLogics.getCurrentVotingDate(onboardingData: onboarding, now: now)
let yesterday = Calendar.current.date(byAdding: .day, value: -1, to: now)!
XCTAssertTrue(Calendar.current.isDate(result, inSameDayAs: yesterday),
"Previous mode, after unlock: should return yesterday")
}
// MARK: - Phase 7: Vote Needed Tests (exercising the same logic as isMissingCurrentVote)
// Note: isMissingCurrentVote uses DataController.shared (singleton) internally,
// so we test the equivalent logic using a testable DataController instance.
func test_voteNeeded_noEntry() {
let sut = makeTestDataController()
let onboarding = OnboardingData()
onboarding.inputDay = .Today
onboarding.date = makeDateWithTime(hour: 0, minute: 1)
let votingDate = ShowBasedOnVoteLogics.getCurrentVotingDate(onboardingData: onboarding)
let entry = sut.getEntry(byDate: votingDate.startOfDay)
let isMissing = entry == nil || entry?.mood == .missing
XCTAssertTrue(isMissing, "Should need a vote when no entry exists")
}
func test_voteNeeded_validEntry() {
let sut = makeTestDataController()
let onboarding = OnboardingData()
onboarding.inputDay = .Today
onboarding.date = makeDateWithTime(hour: 0, minute: 1)
let votingDate = ShowBasedOnVoteLogics.getCurrentVotingDate(onboardingData: onboarding)
sut.add(mood: .great, forDate: votingDate, entryType: .listView)
let entry = sut.getEntry(byDate: votingDate.startOfDay)
let isMissing = entry == nil || entry?.mood == .missing
XCTAssertFalse(isMissing, "Should not need a vote when valid entry exists")
}
func test_voteNeeded_missingEntry() {
let sut = makeTestDataController()
let onboarding = OnboardingData()
onboarding.inputDay = .Today
onboarding.date = makeDateWithTime(hour: 0, minute: 1)
let votingDate = ShowBasedOnVoteLogics.getCurrentVotingDate(onboardingData: onboarding)
sut.add(mood: .missing, forDate: votingDate, entryType: .filledInMissing)
let entry = sut.getEntry(byDate: votingDate.startOfDay)
let isMissing = entry == nil || entry?.mood == .missing
XCTAssertTrue(isMissing, "Should need a vote when entry is .missing")
}
// MARK: - Helpers
private func makeTestDataController() -> DataController {
let schema = Schema([MoodEntryModel.self])
let config = ModelConfiguration(schema: schema, isStoredInMemoryOnly: true)
return DataController(container: try! ModelContainer(for: schema, configurations: [config]))
}
private func makeDateWithTime(hour: Int, minute: Int) -> Date {
let calendar = Calendar.current
var components = calendar.dateComponents([.year, .month, .day], from: Date())
components.hour = hour
components.minute = minute
components.second = 0
return calendar.date(from: components)!
}
}