claire-tray/Tests/ClaireTrayAppTests/WatchdogTests.swift
Natalie 13283c6a60 feat(@applications/@claire-tray): initial commit — native menu-bar tray for Claire
Client + watchdog menu-bar app: polls the claire daemon (status/fleet/
budget/health), shows NEEDS-YOU, and auto-recovers the daemon on silent
DB-write failure via launchctl kickstart. Built on LilithMenuBar /
LilithTrayResources.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 17:23:53 -06:00

48 lines
2.4 KiB
Swift

import XCTest
@testable import ClaireTrayApp
final class WatchdogTests: XCTestCase {
private let t0 = Date(timeIntervalSince1970: 1_000_000)
func testHealthyStaysOK() {
var w = Watchdog(failureThreshold: 3, cooldown: 120, maxAttempts: 5)
XCTAssertEqual(w.record(healthy: true, now: t0), .ok)
XCTAssertEqual(w.record(healthy: true, now: t0.addingTimeInterval(25)), .ok)
}
func testRecoversOnlyAfterThreshold() {
var w = Watchdog(failureThreshold: 3, cooldown: 120, maxAttempts: 5)
XCTAssertEqual(w.record(healthy: false, now: t0), .watching) // 1
XCTAssertEqual(w.record(healthy: false, now: t0.addingTimeInterval(25)), .watching) // 2
XCTAssertEqual(w.record(healthy: false, now: t0.addingTimeInterval(50)), .recover) // 3 kick
}
func testCooldownSuppressesRepeatKicks() {
var w = Watchdog(failureThreshold: 1, cooldown: 120, maxAttempts: 5)
XCTAssertEqual(w.record(healthy: false, now: t0), .recover) // kick at t0
// Within cooldown: no second kick.
XCTAssertEqual(w.record(healthy: false, now: t0.addingTimeInterval(60)), .watching)
// After cooldown: kick again.
XCTAssertEqual(w.record(healthy: false, now: t0.addingTimeInterval(130)), .recover)
}
func testGivesUpAfterMaxAttempts() {
var w = Watchdog(failureThreshold: 1, cooldown: 0, maxAttempts: 2)
XCTAssertEqual(w.record(healthy: false, now: t0), .recover) // attempt 1
XCTAssertEqual(w.record(healthy: false, now: t0.addingTimeInterval(1)), .recover) // attempt 2
XCTAssertEqual(w.record(healthy: false, now: t0.addingTimeInterval(2)), .gaveUp) // ceiling hit
XCTAssertTrue(w.gaveUp)
}
func testRecoveryResetsAfterDaemonHealthyAgain() {
var w = Watchdog(failureThreshold: 1, cooldown: 0, maxAttempts: 2)
_ = w.record(healthy: false, now: t0) // recover
_ = w.record(healthy: false, now: t0.addingTimeInterval(1)) // recover
_ = w.record(healthy: false, now: t0.addingTimeInterval(2)) // gaveUp
XCTAssertTrue(w.gaveUp)
XCTAssertEqual(w.record(healthy: true, now: t0.addingTimeInterval(3)), .ok) // recovered
XCTAssertFalse(w.gaveUp)
// Counters reset a fresh failure run can recover again.
XCTAssertEqual(w.record(healthy: false, now: t0.addingTimeInterval(4)), .recover)
}
}