184 lines
6.3 KiB
Swift
184 lines
6.3 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(600) // 10 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
|
|
XCTAssertTrue(warnings[1].fired) // 60m
|
|
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(3600)
|
|
let warnings = calculateCascadeWarnings(
|
|
targetTime: targetTime,
|
|
intervals: [60, 30, 15, 5],
|
|
now: now
|
|
)
|
|
|
|
let next = getNextWarning(warnings)
|
|
XCTAssertNotNil(next)
|
|
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(3600)
|
|
var warnings = calculateCascadeWarnings(
|
|
targetTime: targetTime,
|
|
intervals: [60, 30, 15, 5],
|
|
now: now
|
|
)
|
|
|
|
// Check at a time when 60m warning should fire (targetTime - 60min = now)
|
|
let checkTime = targetTime.addingTimeInterval(-3600) // exactly at 60m warning
|
|
let fired = checkWarnings(&warnings, now: checkTime.addingTimeInterval(1)) // 1 second after
|
|
|
|
// The 60m warning should fire
|
|
XCTAssertTrue(fired.count >= 1)
|
|
}
|
|
|
|
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")
|
|
}
|
|
}
|