krz/hutch

an ios client for sourcehut

clone: git clone https://gitbay.org/krz/hutch.git

v3.5.0: HutchTests/SystemStatusRepositoryTests.swift · raw

  1import Foundation
  2import Testing
  3@testable import Hutch
  4
  5struct SystemStatusRepositoryTests {
  6
  7    @Test
  8    func fallsBackToPersistedSnapshotWhenRefreshFails() async throws {
  9        let defaultsName = "SystemStatusRepositoryTests-\(UUID().uuidString)"
 10        let defaults = try #require(UserDefaults(suiteName: defaultsName))
 11        defaults.removePersistentDomain(forName: defaultsName)
 12        defer { defaults.removePersistentDomain(forName: defaultsName) }
 13
 14        let cachedSnapshot = SystemStatusSnapshot(
 15            services: [
 16                StatusServiceState(id: "git.sr.ht", name: "git.sr.ht", slug: "git.sr.ht", status: .degraded, description: nil)
 17            ],
 18            activeIncidents: [],
 19            lastUpdated: Date(timeIntervalSince1970: 120)
 20        )
 21        let cacheStore = SystemStatusCacheStore(defaults: defaults)
 22        let initialRepository = SystemStatusRepository(
 23            service: TestSystemStatusService(
 24                snapshotHTMLHandler: { Self.cachedSnapshotHTML },
 25                incidentsDataHandler: { Data(Self.emptyRSS.utf8) }
 26            ),
 27            cacheStore: cacheStore,
 28            now: { Date(timeIntervalSince1970: 120) }
 29        )
 30
 31        _ = try await initialRepository.snapshotResult(forceRefresh: true)
 32
 33        let fallbackRepository = SystemStatusRepository(
 34            service: TestSystemStatusService(
 35                snapshotHTMLHandler: { throw SRHTError.httpError(503) },
 36                incidentsDataHandler: { Data(Self.emptyRSS.utf8) }
 37            ),
 38            ttl: 0,
 39            cacheStore: cacheStore,
 40            now: { Date(timeIntervalSince1970: 180) }
 41        )
 42
 43        let result = try await fallbackRepository.snapshotResult(forceRefresh: true)
 44
 45        #expect(result.value == cachedSnapshot)
 46        #expect(result.isStale)
 47        #expect(result.lastSuccessfulAt == Date(timeIntervalSince1970: 120))
 48        #expect(result.refreshErrorMessage != nil)
 49    }
 50
 51    @Test
 52    func fallsBackToPersistedIncidentsWhenRefreshFails() async throws {
 53        let defaultsName = "SystemStatusRepositoryTests-\(UUID().uuidString)"
 54        let defaults = try #require(UserDefaults(suiteName: defaultsName))
 55        defaults.removePersistentDomain(forName: defaultsName)
 56        defer { defaults.removePersistentDomain(forName: defaultsName) }
 57
 58        let cachedIncidents = [
 59            StatusIncident(
 60                id: "incident-1",
 61                title: "builds.sr.ht outage",
 62                summary: "Builds are failing.",
 63                url: URL(string: "https://status.sr.ht/issues/1/"),
 64                publishedAt: Date(timeIntervalSince1970: 200),
 65                updatedAt: nil,
 66                isActive: true
 67            )
 68        ]
 69        let cacheStore = SystemStatusCacheStore(defaults: defaults)
 70        let initialRepository = SystemStatusRepository(
 71            service: TestSystemStatusService(
 72                snapshotHTMLHandler: { Self.cachedOperationalHTML },
 73                incidentsDataHandler: { Data(Self.cachedIncidentRSS.utf8) }
 74            ),
 75            cacheStore: cacheStore,
 76            now: { Date(timeIntervalSince1970: 220) }
 77        )
 78
 79        _ = try await initialRepository.recentIncidentsResult(forceRefresh: true)
 80
 81        let fallbackRepository = SystemStatusRepository(
 82            service: TestSystemStatusService(
 83                snapshotHTMLHandler: { Self.cachedOperationalHTML },
 84                incidentsDataHandler: { throw SRHTError.httpError(504) }
 85            ),
 86            ttl: 0,
 87            cacheStore: cacheStore,
 88            now: { Date(timeIntervalSince1970: 260) }
 89        )
 90
 91        let result = try await fallbackRepository.recentIncidentsResult(forceRefresh: true)
 92
 93        #expect(result.value == cachedIncidents)
 94        #expect(result.isStale)
 95        #expect(result.lastSuccessfulAt == Date(timeIntervalSince1970: 220))
 96        #expect(result.refreshErrorMessage != nil)
 97    }
 98}
 99
100private struct TestSystemStatusService: SystemStatusServing {
101    let snapshotHTMLHandler: @Sendable () async throws -> String
102    let incidentsDataHandler: @Sendable () async throws -> Data
103
104    func fetchSnapshotHTML() async throws -> String {
105        try await snapshotHTMLHandler()
106    }
107
108    func fetchIncidentFeedData() async throws -> Data {
109        try await incidentsDataHandler()
110    }
111}
112
113private extension SystemStatusRepositoryTests {
114    static let cachedSnapshotHTML = #"""
115    <div class="component" data-status="disrupted">
116      <a href="/affected/git.sr.ht/">git.sr.ht</a>
117      <span class="component-status">Disrupted</span>
118    </div>
119    """#
120
121    static let cachedOperationalHTML = #"""
122    <div class="component" data-status="ok">
123      <a href="/affected/meta.sr.ht/">meta.sr.ht</a>
124      <span class="component-status">Operational</span>
125    </div>
126    """#
127
128    static let cachedIncidentRSS = #"""
129    <rss version="2.0">
130      <channel>
131        <item>
132          <title>builds.sr.ht outage</title>
133          <link>https://status.sr.ht/issues/1/</link>
134          <pubDate>Thu, 01 Jan 1970 00:03:20 +0000</pubDate>
135          <guid>incident-1</guid>
136          <description>&lt;p&gt;Builds are failing.&lt;/p&gt;</description>
137        </item>
138      </channel>
139    </rss>
140    """#
141
142    static let emptyRSS = #"""
143    <rss version="2.0">
144      <channel></channel>
145    </rss>
146    """#
147}