summaryrefslogtreecommitdiffstats
path: root/SolverVTests/Models/SolsticeDataTests.swift
blob: a687b6f2df0e7658a51e98f3ebdb8e4474b06a1b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
import XCTest
@testable import Solverv

class SolsticeDataTests: XCTestCase {

    var sut: SolsticeData!

    override func setUp() {
        super.setUp()
        sut = SolsticeData.shared
    }

    override func tearDown() {
        sut = nil
        super.tearDown()
    }

    // MARK: - nextEvent() Tests

    func testNextEventReturnsValidEvent() {
        let nextEvent = sut.nextEvent()

        // Should return a valid event
        XCTAssertNotNil(nextEvent, "nextEvent() should return a valid event")

        // The next event should be in the future
        if let nextEvent = nextEvent {
            XCTAssertGreaterThan(nextEvent.date, Date(), "Next event should be in the future")
        }
    }

    func testNextEventDateIsAfterCurrentDate() {
        guard let nextEvent = sut.nextEvent() else {
            XCTFail("nextEvent() should not be nil")
            return
        }

        let now = Date()
        XCTAssertGreaterThan(nextEvent.date, now, "Next event date should be after current date")
    }

    // MARK: - upcomingEvents(count:) Tests

    func testUpcomingEventsReturnsCorrectCount() {
        let count = 3
        let upcomingEvents = sut.upcomingEvents(count: count)

        XCTAssertLessThanOrEqual(upcomingEvents.count, count, "Should return at most \(count) events")
    }

    func testUpcomingEventsReturnsEventsInOrder() {
        let upcomingEvents = sut.upcomingEvents(count: 5)

        // All events should be in the future
        let now = Date()
        for event in upcomingEvents {
            XCTAssertGreaterThan(event.date, now, "All upcoming events should be in the future")
        }

        // Events should be in chronological order
        for i in 1..<upcomingEvents.count {
            XCTAssertLessThan(upcomingEvents[i - 1].date, upcomingEvents[i].date,
                            "Events should be in chronological order")
        }
    }

    func testUpcomingEventsWithZeroCount() {
        let upcomingEvents = sut.upcomingEvents(count: 0)
        XCTAssertEqual(upcomingEvents.count, 0, "Should return empty array when count is 0")
    }

    func testUpcomingEventsWithLargeCount() {
        let upcomingEvents = sut.upcomingEvents(count: 100)

        // Should return events but not more than available
        XCTAssertGreaterThan(upcomingEvents.count, 0, "Should return some events")
        XCTAssertLessThanOrEqual(upcomingEvents.count, 100, "Should not exceed requested count")
    }

    // MARK: - progressToNextEvent() Tests

    func testProgressToNextEventReturnsValidRatio() {
        guard let progress = sut.progressToNextEvent() else {
            XCTFail("progressToNextEvent() should return a valid progress")
            return
        }

        // Elapsed should be >= 0
        XCTAssertGreaterThanOrEqual(progress.elapsed, 0, "Elapsed days should be >= 0")

        // Total should be > 0
        XCTAssertGreaterThan(progress.total, 0, "Total days should be > 0")

        // Elapsed should be <= total
        XCTAssertLessThanOrEqual(progress.elapsed, progress.total, "Elapsed should not exceed total")
    }

    func testProgressBarCalculationExample() {
        // This tests the "31/89" example from the specification
        guard let progress = sut.progressToNextEvent() else {
            XCTFail("progressToNextEvent() should return a valid progress")
            return
        }

        // Verify the format is correct: (elapsed, total)
        let elapsedStr = String(progress.elapsed)
        let totalStr = String(progress.total)
        let progressStr = "\(elapsedStr)/\(totalStr)"

        // Just verify the format is correct
        XCTAssertFalse(progressStr.isEmpty, "Progress string should not be empty")
        XCTAssertTrue(progressStr.contains("/"), "Progress string should contain '/'")
    }

    // MARK: - Data Integrity Tests

    func testAllEventsAreUniqueAndOrdered() {
        let allEvents = sut.upcomingEvents(count: 1000) // Get as many as available

        // Check that events are in order
        for i in 1..<allEvents.count {
            XCTAssertLessThan(allEvents[i - 1].date, allEvents[i].date,
                            "Events should be in chronological order")
        }
    }

    func testEventsHaveValidSeasons() {
        let allEvents = sut.upcomingEvents(count: 1000)

        for event in allEvents {
            // Season should match the date
            let month = Calendar.current.component(.month, from: event.date)
            switch month {
            case 3, 4, 5:
                XCTAssertEqual(event.season, .spring, "March-May should be spring")
            case 6, 7, 8:
                XCTAssertEqual(event.season, .summer, "June-August should be summer")
            case 9, 10, 11:
                XCTAssertEqual(event.season, .autumn, "September-November should be autumn")
            default:
                XCTAssertEqual(event.season, .winter, "December, January, February should be winter")
            }
        }
    }

    func testSingletonPattern() {
        let instance1 = SolsticeData.shared
        let instance2 = SolsticeData.shared

        // Both should reference the same object
        XCTAssertTrue(instance1 === instance2, "Singleton should return the same instance")
    }

    func testEventNamesAreNotEmpty() {
        let allEvents = sut.upcomingEvents(count: 1000)

        for event in allEvents {
            XCTAssertFalse(event.name.isEmpty, "Event name should not be empty")
        }
    }

    func testEventDatesAreValid() {
        let allEvents = sut.upcomingEvents(count: 1000)

        for event in allEvents {
            // Event should have a valid date
            let year = Calendar.current.component(.year, from: event.date)
            XCTAssertGreaterThanOrEqual(year, 2025, "Event year should be 2025 or later")
            XCTAssertLessThanOrEqual(year, 2030, "Event year should be 2030 or earlier")
        }
    }

}