import Foundation import Testing @testable import Hutch struct SystemStatusServiceTests { @Test func parsesCurrentStatusHTMLIntoServicesAndActiveIncidents() throws { let snapshot = try SystemStatusService.parseSnapshotHTML(Self.sampleHTML, fetchedAt: Date(timeIntervalSince1970: 100)) #expect(snapshot.services.count == 3) #expect(snapshot.services[0].name == "git.sr.ht") #expect(snapshot.services[0].status == .degraded) #expect(snapshot.services[1].status == .operational) #expect(snapshot.hasDisruption) #expect(snapshot.activeIncidents.count == 1) #expect(snapshot.activeIncidents[0].title == "SourceHut disrupted due to DDoS attack") #expect(snapshot.activeIncidents[0].summary == "SourceHut was disrupted by a DDoS attack.") #expect(snapshot.activeIncidents[0].url?.absoluteString == "https://status.sr.ht/issues/2026-04-06-ddos-attack/") } @Test func parsesStatusHTMLWithClassOrderChangesAndTimeElements() throws { let snapshot = try SystemStatusService.parseSnapshotHTML(Self.variantHTML, fetchedAt: .now) #expect(snapshot.services.count == 2) #expect(snapshot.services[0].status == .operational) #expect(snapshot.services[1].status == .majorOutage) #expect(snapshot.activeIncidents.count == 1) #expect(snapshot.activeIncidents[0].title == "builds.sr.ht outage") #expect(snapshot.activeIncidents[0].publishedAt == ISO8601DateFormatter().date(from: "2026-04-07T12:00:00Z")) } @Test func parsesIncidentFeedRSS() async throws { let incidents = try await SystemStatusService.parseIncidentFeedXML(Data(Self.sampleRSS.utf8)) #expect(incidents.count == 2) #expect(incidents[0].title == "SourceHut disrupted due to DDoS attack") #expect(incidents[0].isActive == true) #expect(incidents[0].summary == "SourceHut was disrupted by a DDoS attack.") #expect(incidents[1].title == "Planned maintenance on all services") #expect(incidents[1].isActive == false) #expect(incidents[1].updatedAt != nil) } @Test func parsesIncidentFeedWithISO8601Dates() async throws { let incidents = try await SystemStatusService.parseIncidentFeedXML(Data(Self.variantRSS.utf8)) #expect(incidents.count == 1) #expect(incidents[0].title == "Status feed moved") #expect(incidents[0].publishedAt == ISO8601DateFormatter().date(from: "2026-04-07T15:30:00Z")) } @Test func bannerSummaryPrefersSpecificServiceThenCount() { let operational = SystemStatusSnapshot( services: [ StatusServiceState(id: "git.sr.ht", name: "git.sr.ht", slug: "git.sr.ht", status: .operational, description: nil) ], activeIncidents: [], lastUpdated: .now ) let oneDisrupted = SystemStatusSnapshot( services: [ StatusServiceState(id: "git.sr.ht", name: "git.sr.ht", slug: "git.sr.ht", status: .degraded, description: nil), StatusServiceState(id: "hg.sr.ht", name: "hg.sr.ht", slug: "hg.sr.ht", status: .operational, description: nil) ], activeIncidents: [], lastUpdated: .now ) let multipleDisrupted = SystemStatusSnapshot( services: [ StatusServiceState(id: "git.sr.ht", name: "git.sr.ht", slug: "git.sr.ht", status: .degraded, description: nil), StatusServiceState(id: "builds.sr.ht", name: "builds.sr.ht", slug: "builds.sr.ht", status: .majorOutage, description: nil) ], activeIncidents: [], lastUpdated: .now ) #expect(operational.hasDisruption == false) #expect(oneDisrupted.bannerSummary == "git.sr.ht disrupted") #expect(multipleDisrupted.bannerSummary == "2 services disrupted") } private static let sampleHTML = #"""

SourceHut disrupted due to DDoS attack →

git.sr.ht

SourceHut was disrupted by a DDoS attack.


git.sr.ht Disrupted
hg.sr.ht Operational
man.sr.ht Maintenance
April 5, 2026 at 10:00 AM UTC

SourceHut disrupted due to DDoS attack

▲ This issue is not resolved yet
"""# private static let variantHTML = #"""
meta.sr.ht All systems operational
builds.sr.ht
Outage

builds.sr.ht outage

Build jobs are currently failing.

builds.sr.ht outage

Investigating elevated failures
"""# private static let sampleRSS = #""" sr.ht status SourceHut disrupted due to DDoS attack https://status.sr.ht/issues/2026-04-06-ddos-attack/ Sun, 05 Apr 2026 10:00:00 +0000 https://status.sr.ht/issues/2026-04-06-ddos-attack/ <p><strong>SourceHut was disrupted by a DDoS attack</strong>.</p> [Resolved] Planned maintenance on all services https://status.sr.ht/issues/2025-10-22-planned-maintenance/ Wed, 22 Oct 2025 11:00:00 +0000 https://status.sr.ht/issues/2025-10-22-planned-maintenance/ 2025-10-22 12:25:00 <p><strong>The maintenance is complete</strong>.</p> """# private static let variantRSS = #""" sr.ht status Status feed moved https://status.sr.ht/issues/2026-04-07-feed-moved/ 2026-04-07T15:30:00Z https://status.sr.ht/issues/2026-04-07-feed-moved/ <p>Use the new feed endpoint.</p> """# }