feat: complete Tasks 21-28 — backend integration, security hardening, UI tests & CI
- Add Apple Sign-In backend (JWKS verification, account linking, session management) - Implement push notification deep linking with NotificationDeepLinkRouter - Add jailbreak detection, runtime integrity monitoring, secure enclave service - Implement OAuth social login, token refresh, and secure logout flows - Add image caching (memory/disk), optimizer, upload queue, async semaphore - Implement notification analytics, type preferences, and category setup - Expand UI test suite with UITestBase, accessibility, auth flow, performance tests - Add CI pipeline for iOS UI tests (3 device sizes) and performance benchmarks - Restructure Xcode project to manual groups with KordantWidgets target - Add SwiftLint, Swift Collections/Algorithms/GoogleSignIn dependencies - Update project.yml for XcodeGen with new targets and configurations
This commit is contained in:
229
iOS/KordantTests/WidgetDataTests.swift
Normal file
229
iOS/KordantTests/WidgetDataTests.swift
Normal file
@@ -0,0 +1,229 @@
|
||||
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())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user