learning_ai_clock/ios/ChronoMindTests/CascadeTests.swift

190 lines
6.6 KiB
Swift

// Cascade Tests
// XCTest unit tests mirroring web cascade.test.ts
import XCTest
@testable import ChronoMind
final class CascadeTests: XCTestCase {
// MARK: - Preset Intervals
func testAggressivePreset() {
let intervals = CascadePreset.aggressive.defaultIntervals
XCTAssertEqual(intervals, [240, 180, 120, 90, 60, 30, 15, 5, 1])
}
func testStandardPreset() {
let intervals = CascadePreset.standard.defaultIntervals
XCTAssertEqual(intervals, [120, 60, 30, 15, 5])
}
func testLightPreset() {
let intervals = CascadePreset.light.defaultIntervals
XCTAssertEqual(intervals, [60, 15, 5])
}
func testMinimalPreset() {
let intervals = CascadePreset.minimal.defaultIntervals
XCTAssertEqual(intervals, [15])
}
func testNonePreset() {
let intervals = CascadePreset.none.defaultIntervals
XCTAssertTrue(intervals.isEmpty)
}
// MARK: - Calculate Cascade Warnings
func testCalculateWarnings() {
let now = Date()
let targetTime = now.addingTimeInterval(3600) // 1h from now
let intervals = [60, 30, 15, 5]
let warnings = calculateCascadeWarnings(targetTime: targetTime, intervals: intervals, now: now)
XCTAssertEqual(warnings.count, 4)
// Should be sorted largest first
XCTAssertEqual(warnings[0].minutesBefore, 60)
XCTAssertEqual(warnings[1].minutesBefore, 30)
XCTAssertEqual(warnings[2].minutesBefore, 15)
XCTAssertEqual(warnings[3].minutesBefore, 5)
}
func testWarningsInPastAreFired() {
let now = Date()
let targetTime = now.addingTimeInterval(1800) // 30 min from now
let intervals = [120, 60, 15, 5] // 2h and 1h are in the past
let warnings = calculateCascadeWarnings(targetTime: targetTime, intervals: intervals, now: now)
// 120min and 60min warnings should be marked as fired (they're in the past)
let fired = warnings.filter(\.fired)
XCTAssertEqual(fired.count, 2)
XCTAssertTrue(warnings[0].fired) // 120m in the past
XCTAssertTrue(warnings[1].fired) // 60m in the past
XCTAssertFalse(warnings[2].fired) // 15m still in the future
XCTAssertFalse(warnings[3].fired) // 5m still in the future
}
func testWarningScheduledTimes() {
let now = Date()
let targetTime = now.addingTimeInterval(7200) // 2h from now
let intervals = [60, 30]
let warnings = calculateCascadeWarnings(targetTime: targetTime, intervals: intervals, now: now)
// 60min warning should be at targetTime - 60min
let expected60 = targetTime.addingTimeInterval(-3600)
XCTAssertEqual(warnings[0].scheduledTime.timeIntervalSince1970,
expected60.timeIntervalSince1970, accuracy: 1.0)
// 30min warning should be at targetTime - 30min
let expected30 = targetTime.addingTimeInterval(-1800)
XCTAssertEqual(warnings[1].scheduledTime.timeIntervalSince1970,
expected30.timeIntervalSince1970, accuracy: 1.0)
}
func testEmptyIntervals() {
let now = Date()
let targetTime = now.addingTimeInterval(3600)
let warnings = calculateCascadeWarnings(targetTime: targetTime, intervals: [], now: now)
XCTAssertTrue(warnings.isEmpty)
}
// MARK: - Get Next Warning
func testGetNextWarning() {
let now = Date()
let targetTime = now.addingTimeInterval(7200) // 2h from now
let warnings = calculateCascadeWarnings(
targetTime: targetTime,
intervals: [60, 30, 15, 5],
now: now
)
let next = getNextWarning(warnings)
XCTAssertNotNil(next)
// All warnings are in the future (earliest at targetTime - 60m = 1h from now)
XCTAssertEqual(next?.minutesBefore, 60)
}
func testGetNextWarningAllFired() {
let now = Date()
let targetTime = now.addingTimeInterval(60) // 1 min from now
var warnings = calculateCascadeWarnings(
targetTime: targetTime,
intervals: [120, 60, 30],
now: now
)
// Mark all as fired
for i in warnings.indices { warnings[i].fired = true }
let next = getNextWarning(warnings)
XCTAssertNil(next)
}
// MARK: - Check Warnings
func testCheckWarnings() {
let now = Date()
let targetTime = now.addingTimeInterval(7200) // 2h from now
var warnings = calculateCascadeWarnings(
targetTime: targetTime,
intervals: [60, 30, 15, 5],
now: now
)
// All warnings should be unfired initially
XCTAssertTrue(warnings.allSatisfy { !$0.fired })
// Check at a time when 60m warning should fire (targetTime - 60min)
let checkTime = targetTime.addingTimeInterval(-3599) // 1 second after 60m mark
let fired = checkWarnings(&warnings, now: checkTime)
// The 60m warning should fire
XCTAssertEqual(fired.count, 1)
XCTAssertTrue(warnings[0].fired) // 60m warning fired
XCTAssertFalse(warnings[1].fired) // 30m warning not yet
}
func testCheckWarningsNoneFire() {
let now = Date()
let targetTime = now.addingTimeInterval(7200) // 2h from now
var warnings = calculateCascadeWarnings(
targetTime: targetTime,
intervals: [60, 30, 15],
now: now
)
// Check at current time no warnings should fire yet (earliest is in 1h)
let fired = checkWarnings(&warnings, now: now)
XCTAssertTrue(fired.isEmpty)
}
// MARK: - Get Cascade Intervals
func testGetCascadeIntervalsPreset() {
let config = CascadeConfig(preset: .standard, intervals: [])
let intervals = getCascadeIntervals(config)
XCTAssertEqual(intervals, [120, 60, 30, 15, 5])
}
func testGetCascadeIntervalsCustom() {
let config = CascadeConfig(preset: .custom, intervals: [10, 45, 5, 30])
let intervals = getCascadeIntervals(config)
XCTAssertEqual(intervals, [45, 30, 10, 5]) // sorted descending
}
// MARK: - Format Minutes Before
func testFormatMinutesBefore() {
XCTAssertEqual(formatMinutesBefore(5), "5m")
XCTAssertEqual(formatMinutesBefore(15), "15m")
XCTAssertEqual(formatMinutesBefore(60), "1h")
XCTAssertEqual(formatMinutesBefore(90), "1h 30m")
XCTAssertEqual(formatMinutesBefore(120), "2h")
XCTAssertEqual(formatMinutesBefore(240), "4h")
}
}