mirror of
https://github.com/pawelorzech/MacTorn.git
synced 2026-01-29 19:54:27 +00:00
260 lines
7.6 KiB
Swift
260 lines
7.6 KiB
Swift
import XCTest
|
|
@testable import MacTorn
|
|
|
|
@MainActor
|
|
final class AppStateTests: XCTestCase {
|
|
|
|
var mockSession: MockNetworkSession!
|
|
var appState: AppState!
|
|
|
|
override func setUp() async throws {
|
|
try await super.setUp()
|
|
mockSession = MockNetworkSession()
|
|
appState = AppState(session: mockSession)
|
|
// Clear any persisted data
|
|
UserDefaults.standard.removeObject(forKey: "apiKey")
|
|
UserDefaults.standard.removeObject(forKey: "watchlist")
|
|
UserDefaults.standard.removeObject(forKey: "notificationRules")
|
|
}
|
|
|
|
override func tearDown() async throws {
|
|
appState.stopPolling()
|
|
appState = nil
|
|
mockSession = nil
|
|
try await super.tearDown()
|
|
}
|
|
|
|
// MARK: - API Key Validation Tests
|
|
|
|
func testFetchData_emptyAPIKey() async {
|
|
appState.apiKey = ""
|
|
|
|
appState.fetchData()
|
|
|
|
// Wait for async completion
|
|
try? await Task.sleep(nanoseconds: 100_000_000)
|
|
|
|
XCTAssertEqual(appState.errorMsg, "API Key required")
|
|
XCTAssertNil(appState.data)
|
|
}
|
|
|
|
func testFetchData_invalidAPIKey_HTTP403() async throws {
|
|
appState.apiKey = "invalid_key"
|
|
mockSession.setHTTPError(statusCode: 403)
|
|
|
|
appState.fetchData()
|
|
|
|
// Wait for async completion
|
|
try await Task.sleep(nanoseconds: 1_000_000_000)
|
|
|
|
XCTAssertEqual(appState.errorMsg, "Invalid API Key")
|
|
XCTAssertNil(appState.data)
|
|
}
|
|
|
|
func testFetchData_invalidAPIKey_HTTP404() async throws {
|
|
appState.apiKey = "invalid_key"
|
|
mockSession.setHTTPError(statusCode: 404)
|
|
|
|
appState.fetchData()
|
|
|
|
// Wait for async completion
|
|
try await Task.sleep(nanoseconds: 1_000_000_000)
|
|
|
|
XCTAssertEqual(appState.errorMsg, "Invalid API Key")
|
|
}
|
|
|
|
// MARK: - Fetch Success Tests
|
|
|
|
func testFetchData_success() async throws {
|
|
appState.apiKey = "valid_key"
|
|
try mockSession.setSuccessResponse(json: TornAPIFixtures.validFullResponse)
|
|
|
|
appState.fetchData()
|
|
|
|
// Wait for async completion
|
|
try await Task.sleep(nanoseconds: 1_000_000_000)
|
|
|
|
XCTAssertNotNil(appState.data)
|
|
XCTAssertEqual(appState.data?.name, "TestPlayer")
|
|
XCTAssertEqual(appState.data?.playerId, 123456)
|
|
XCTAssertNil(appState.errorMsg)
|
|
XCTAssertNotNil(appState.lastUpdated)
|
|
}
|
|
|
|
func testFetchData_parsesAllBars() async throws {
|
|
appState.apiKey = "valid_key"
|
|
try mockSession.setSuccessResponse(json: TornAPIFixtures.validFullResponse)
|
|
|
|
appState.fetchData()
|
|
|
|
try await Task.sleep(nanoseconds: 1_000_000_000)
|
|
|
|
XCTAssertNotNil(appState.data?.bars)
|
|
XCTAssertEqual(appState.data?.energy?.current, 100)
|
|
XCTAssertEqual(appState.data?.nerve?.current, 50)
|
|
XCTAssertEqual(appState.data?.life?.current, 7500)
|
|
XCTAssertEqual(appState.data?.happy?.current, 5000)
|
|
}
|
|
|
|
// MARK: - Torn API Error Tests
|
|
|
|
func testFetchData_tornAPIError() async throws {
|
|
appState.apiKey = "valid_key"
|
|
try mockSession.setTornAPIError(code: 2, message: "Incorrect Key")
|
|
|
|
appState.fetchData()
|
|
|
|
try await Task.sleep(nanoseconds: 1_000_000_000)
|
|
|
|
XCTAssertEqual(appState.errorMsg, "API Error: Incorrect Key")
|
|
XCTAssertNil(appState.data)
|
|
}
|
|
|
|
func testFetchData_tornAPIRateLimit() async throws {
|
|
appState.apiKey = "valid_key"
|
|
try mockSession.setTornAPIError(code: 5, message: "Too many requests")
|
|
|
|
appState.fetchData()
|
|
|
|
try await Task.sleep(nanoseconds: 1_000_000_000)
|
|
|
|
XCTAssertEqual(appState.errorMsg, "API Error: Too many requests")
|
|
}
|
|
|
|
// MARK: - Network Error Tests
|
|
|
|
func testFetchData_networkError() async throws {
|
|
appState.apiKey = "valid_key"
|
|
mockSession.setNetworkError(MockNetworkError.connectionFailed)
|
|
|
|
appState.fetchData()
|
|
|
|
try await Task.sleep(nanoseconds: 1_000_000_000)
|
|
|
|
XCTAssertNotNil(appState.errorMsg)
|
|
XCTAssertTrue(appState.errorMsg?.contains("Network error") ?? false)
|
|
}
|
|
|
|
// MARK: - HTTP Error Tests
|
|
|
|
func testFetchData_HTTP500() async throws {
|
|
appState.apiKey = "valid_key"
|
|
mockSession.setHTTPError(statusCode: 500)
|
|
|
|
appState.fetchData()
|
|
|
|
try await Task.sleep(nanoseconds: 1_000_000_000)
|
|
|
|
XCTAssertEqual(appState.errorMsg, "HTTP Error: 500")
|
|
}
|
|
|
|
func testFetchData_HTTP502() async throws {
|
|
appState.apiKey = "valid_key"
|
|
mockSession.setHTTPError(statusCode: 502)
|
|
|
|
appState.fetchData()
|
|
|
|
try await Task.sleep(nanoseconds: 1_000_000_000)
|
|
|
|
XCTAssertEqual(appState.errorMsg, "HTTP Error: 502")
|
|
}
|
|
|
|
// MARK: - Polling Tests
|
|
|
|
func testStartPolling_fetchesData() async throws {
|
|
appState.apiKey = "valid_key"
|
|
try mockSession.setSuccessResponse(json: TornAPIFixtures.validFullResponse)
|
|
|
|
appState.startPolling()
|
|
|
|
// Initial fetch should happen immediately
|
|
try await Task.sleep(nanoseconds: 1_000_000_000)
|
|
|
|
XCTAssertTrue(mockSession.requestedURLs.count >= 1)
|
|
XCTAssertNotNil(appState.data)
|
|
}
|
|
|
|
func testStopPolling_stopsTimer() {
|
|
appState.apiKey = "valid_key"
|
|
appState.startPolling()
|
|
|
|
appState.stopPolling()
|
|
|
|
// Timer should be cancelled
|
|
// No way to directly verify timer is nil, but we can verify no more requests happen
|
|
}
|
|
|
|
// MARK: - Loading State Tests
|
|
|
|
func testFetchData_setsLoadingState() async throws {
|
|
appState.apiKey = "valid_key"
|
|
try mockSession.setSuccessResponse(json: TornAPIFixtures.validFullResponse)
|
|
|
|
appState.fetchData()
|
|
|
|
// Wait for completion - fetchData is async so loading transitions happen inside the Task
|
|
try await Task.sleep(nanoseconds: 1_000_000_000)
|
|
|
|
// After completion, loading should be false
|
|
XCTAssertFalse(appState.isLoading)
|
|
// And we should have data
|
|
XCTAssertNotNil(appState.data)
|
|
}
|
|
|
|
// MARK: - Notification Rules Tests
|
|
|
|
func testLoadNotificationRules_defaults() {
|
|
// Clear existing rules
|
|
UserDefaults.standard.removeObject(forKey: "notificationRules")
|
|
|
|
let newAppState = AppState(session: mockSession)
|
|
|
|
XCTAssertFalse(newAppState.notificationRules.isEmpty)
|
|
// Should have default rules
|
|
}
|
|
|
|
func testSaveNotificationRules() {
|
|
let rule = NotificationRule(
|
|
id: "test_rule",
|
|
barType: .energy,
|
|
threshold: 80,
|
|
enabled: true,
|
|
soundName: "default"
|
|
)
|
|
appState.notificationRules = [rule]
|
|
appState.saveNotificationRules()
|
|
|
|
// Reload
|
|
appState.loadNotificationRules()
|
|
|
|
XCTAssertEqual(appState.notificationRules.count, 1)
|
|
XCTAssertEqual(appState.notificationRules.first?.id, "test_rule")
|
|
}
|
|
|
|
func testUpdateRule() {
|
|
appState.notificationRules = NotificationRule.defaults
|
|
|
|
var rule = appState.notificationRules.first!
|
|
rule.enabled = false
|
|
appState.updateRule(rule)
|
|
|
|
XCTAssertFalse(appState.notificationRules.first!.enabled)
|
|
}
|
|
|
|
// MARK: - Refresh Now Tests
|
|
|
|
func testRefreshNow_triggersFetch() async throws {
|
|
appState.apiKey = "valid_key"
|
|
try mockSession.setSuccessResponse(json: TornAPIFixtures.validFullResponse)
|
|
|
|
// refreshNow calls fetchData which is async
|
|
appState.refreshNow()
|
|
|
|
// Wait for async completion
|
|
try await Task.sleep(nanoseconds: 1_500_000_000)
|
|
|
|
// Verify request was made
|
|
XCTAssertGreaterThanOrEqual(mockSession.requestedURLs.count, 1)
|
|
XCTAssertNotNil(appState.data)
|
|
}
|
|
}
|