import XCTest @testable import Kordant final class WidgetDataTests: XCTestCase { // MARK: - Encoding / Decoding func testWidgetDataEncodingDecoding() throws { let original = WidgetData( threatScore: 0.42, recentAlerts: [ WidgetAlert( id: "test-1", title: "Test Alert", message: "Test message", severity: "high", type: "breach", createdAt: Date() ) ], alertCount: 1, unreadCount: 1, criticalCount: 0, exposureCount: 2, lastUpdated: Date() ) let encoded = try JSONEncoder().encode(original) let decoded = try JSONDecoder().decode(WidgetData.self, from: encoded) XCTAssertEqual(original, decoded) XCTAssertEqual(decoded.threatScore, 0.42) XCTAssertEqual(decoded.alertCount, 1) XCTAssertEqual(decoded.unreadCount, 1) XCTAssertEqual(decoded.criticalCount, 0) XCTAssertEqual(decoded.exposureCount, 2) XCTAssertEqual(decoded.recentAlerts.count, 1) XCTAssertEqual(decoded.recentAlerts[0].id, "test-1") XCTAssertEqual(decoded.recentAlerts[0].title, "Test Alert") XCTAssertEqual(decoded.recentAlerts[0].severity, "high") } // MARK: - Threat Level func testThreatLevelLow() { let data = WidgetData( threatScore: 0.15, recentAlerts: [], alertCount: 0, unreadCount: 0, criticalCount: 0, exposureCount: 0, lastUpdated: Date() ) XCTAssertEqual(data.threatLevel, .low) } func testThreatLevelMedium() { let data = WidgetData( threatScore: 0.25, recentAlerts: [], alertCount: 0, unreadCount: 0, criticalCount: 0, exposureCount: 0, lastUpdated: Date() ) XCTAssertEqual(data.threatLevel, .medium) } func testThreatLevelHigh() { let data = WidgetData( threatScore: 0.45, recentAlerts: [], alertCount: 0, unreadCount: 0, criticalCount: 0, exposureCount: 0, lastUpdated: Date() ) XCTAssertEqual(data.threatLevel, .high) } func testThreatLevelCritical() { let data = WidgetData( threatScore: 0.72, recentAlerts: [], alertCount: 0, unreadCount: 0, criticalCount: 0, exposureCount: 0, lastUpdated: Date() ) XCTAssertEqual(data.threatLevel, .critical) } func testThreatLevelBoundaries() { // Exactly 0.2 is medium let mediumLow = WidgetData(threatScore: 0.2, recentAlerts: [], alertCount: 0, unreadCount: 0, criticalCount: 0, exposureCount: 0, lastUpdated: Date()) XCTAssertEqual(mediumLow.threatLevel, .medium) // Exactly 0.4 is high let highLow = WidgetData(threatScore: 0.4, recentAlerts: [], alertCount: 0, unreadCount: 0, criticalCount: 0, exposureCount: 0, lastUpdated: Date()) XCTAssertEqual(highLow.threatLevel, .high) // Exactly 0.7 is critical let criticalLow = WidgetData(threatScore: 0.7, recentAlerts: [], alertCount: 0, unreadCount: 0, criticalCount: 0, exposureCount: 0, lastUpdated: Date()) XCTAssertEqual(criticalLow.threatLevel, .critical) } // MARK: - Threat Percentage func testThreatPercentage() { let data = WidgetData(threatScore: 0.35, recentAlerts: [], alertCount: 0, unreadCount: 0, criticalCount: 0, exposureCount: 0, lastUpdated: Date()) XCTAssertEqual(data.threatPercentage, 35) let data2 = WidgetData(threatScore: 1.0, recentAlerts: [], alertCount: 0, unreadCount: 0, criticalCount: 0, exposureCount: 0, lastUpdated: Date()) XCTAssertEqual(data2.threatPercentage, 100) let data3 = WidgetData(threatScore: 0.0, recentAlerts: [], alertCount: 0, unreadCount: 0, criticalCount: 0, exposureCount: 0, lastUpdated: Date()) XCTAssertEqual(data3.threatPercentage, 0) } // MARK: - WidgetAlert func testWidgetAlertSeverityEnum() { let critical = WidgetAlert(id: "1", title: "Test", message: "Msg", severity: "critical", type: "breach", createdAt: nil) XCTAssertEqual(critical.severityEnum, .critical) let high = WidgetAlert(id: "2", title: "Test", message: "Msg", severity: "high", type: "exposure", createdAt: nil) XCTAssertEqual(high.severityEnum, .high) let medium = WidgetAlert(id: "3", title: "Test", message: "Msg", severity: "medium", type: "voiceMatch", createdAt: nil) XCTAssertEqual(medium.severityEnum, .medium) let low = WidgetAlert(id: "4", title: "Test", message: "Msg", severity: "low", type: "removal", createdAt: nil) XCTAssertEqual(low.severityEnum, .low) // Unknown severity defaults to low let unknown = WidgetAlert(id: "5", title: "Test", message: "Msg", severity: "unknown", type: "login", createdAt: nil) XCTAssertEqual(unknown.severityEnum, .low) } func testWidgetAlertTypeEnum() { let breach = WidgetAlert(id: "1", title: "Test", message: "Msg", severity: "low", type: "breach", createdAt: nil) XCTAssertEqual(breach.typeEnum, .breach) let exposure = WidgetAlert(id: "2", title: "Test", message: "Msg", severity: "low", type: "exposure", createdAt: nil) XCTAssertEqual(exposure.typeEnum, .exposure) } func testWidgetAlertDeepLink() { let alert = WidgetAlert(id: "abc-123", title: "Test", message: "Msg", severity: "high", type: "breach", createdAt: nil) let url = alert.deepLink XCTAssertEqual(url.absoluteString, "kordant://alerts/abc-123") } // MARK: - Placeholder & Unavailable func testPlaceholderData() { let placeholder = WidgetData.placeholder XCTAssertEqual(placeholder.threatScore, 0.25) XCTAssertEqual(placeholder.alertCount, 5) XCTAssertEqual(placeholder.recentAlerts.count, 3) } func testUnavailableData() { let unavailable = WidgetData.unavailable XCTAssertEqual(unavailable.threatScore, 0) XCTAssertEqual(unavailable.alertCount, 0) XCTAssertEqual(unavailable.recentAlerts.count, 0) } func testPlaceholderAlerts() { let alerts = WidgetAlert.placeholders XCTAssertEqual(alerts.count, 3) XCTAssertEqual(alerts[0].title, "Data Breach Detected") XCTAssertEqual(alerts[0].severity, "critical") XCTAssertEqual(alerts[1].severity, "high") XCTAssertEqual(alerts[2].severity, "medium") } // MARK: - Severity Filter func testSeverityFilterAll() { let filter = AlertSeverityFilter.all XCTAssertTrue(filter.matches(severity: "low")) XCTAssertTrue(filter.matches(severity: "medium")) XCTAssertTrue(filter.matches(severity: "high")) XCTAssertTrue(filter.matches(severity: "critical")) } func testSeverityFilterCritical() { let filter = AlertSeverityFilter.critical XCTAssertFalse(filter.matches(severity: "low")) XCTAssertFalse(filter.matches(severity: "medium")) XCTAssertFalse(filter.matches(severity: "high")) XCTAssertTrue(filter.matches(severity: "critical")) } func testSeverityFilterHigh() { let filter = AlertSeverityFilter.high XCTAssertFalse(filter.matches(severity: "low")) XCTAssertFalse(filter.matches(severity: "medium")) XCTAssertTrue(filter.matches(severity: "high")) XCTAssertTrue(filter.matches(severity: "critical")) } // MARK: - Data Manager (integration) func testWidgetDataManagerSaveAndLoad() { let manager = WidgetDataManager.shared let original = WidgetData.placeholder // Clear any existing data manager.clear() XCTAssertNil(manager.load()) // Save and reload manager.save(original) let loaded = manager.load() XCTAssertNotNil(loaded) XCTAssertEqual(loaded, original) XCTAssertEqual(loaded?.threatScore, 0.25) // Clean up manager.clear() XCTAssertNil(manager.load()) } }