refactor: rename app from Severed to OpenAppLock

Full rename ahead of App Store submission: project, targets, bundle
identifiers (dev.bchen.Severed -> dev.bchen.OpenAppLock), entitlements
file, app entry point, user-facing onboarding strings, test helpers,
and docs. Verified: all 62 unit tests pass.
This commit is contained in:
2026-06-12 18:05:06 -04:00
parent af919c1585
commit e91d8344a9
42 changed files with 145 additions and 145 deletions

View File

@@ -0,0 +1,112 @@
//
// RuleStatusTests.swift
// OpenAppLockTests
//
import Foundation
import Testing
@testable import OpenAppLock
@MainActor
@Suite("Rule status derivation and labels")
struct RuleStatusTests {
/// 09:0017:00 weekdays schedule rule, like the video's "Work Time".
func workTime(hardMode: Bool = false) -> BlockingRule {
BlockingRule(name: "Work Time", hardMode: hardMode)
}
@Test("Disabled rules report disabled regardless of the clock")
func disabled() {
let rule = workTime()
rule.isEnabled = false
#expect(rule.status(at: date(2025, 1, 6, 10, 0), calendar: utc) == .disabled)
}
@Test("Enabled rule inside its window is active until the window ends")
func active() {
let status = workTime().status(at: date(2025, 1, 6, 10, 0), calendar: utc)
#expect(status == .active(until: date(2025, 1, 6, 17, 0)))
#expect(status.isActive)
}
@Test("Enabled rule outside its window is upcoming")
func upcoming() {
let status = workTime().status(at: date(2025, 1, 6, 18, 0), calendar: utc)
#expect(status == .upcoming(startsAt: date(2025, 1, 7, 9, 0)))
#expect(!status.isActive)
}
@Test("A rule with no days is dormant")
func dormant() {
let rule = workTime()
rule.days = []
#expect(rule.status(at: date(2025, 1, 6, 10, 0), calendar: utc) == .dormant)
}
@Test("A paused rule reports paused until the window ends")
func paused() {
let rule = workTime()
rule.pausedUntil = date(2025, 1, 6, 17, 0)
let status = rule.status(at: date(2025, 1, 6, 10, 0), calendar: utc)
#expect(status == .paused(until: date(2025, 1, 6, 17, 0)))
#expect(!status.isActive)
}
@Test("An expired pause no longer affects status")
func expiredPause() {
let rule = workTime()
rule.pausedUntil = date(2025, 1, 6, 9, 30)
let status = rule.status(at: date(2025, 1, 6, 10, 0), calendar: utc)
#expect(status == .active(until: date(2025, 1, 6, 17, 0)))
}
@Test("Time-limit rules are never schedule-active")
func timeLimitNeverActive() {
let rule = BlockingRule(name: "Time Keeper", kind: .timeLimit)
let status = rule.status(at: date(2025, 1, 6, 10, 0), calendar: utc)
#expect(!status.isActive)
if case .upcoming = status {} else {
Issue.record("Expected upcoming, got \(status)")
}
}
@Test("Active label matches the reference style: hours round up")
func activeLabel() {
// 11:28 17:00 is 5h32m; the reference app shows "6h left".
let status = workTime().status(at: date(2025, 1, 6, 11, 28), calendar: utc)
#expect(status.label(relativeTo: date(2025, 1, 6, 11, 28)) == "6h left")
}
@Test("Upcoming label matches the reference style")
func upcomingLabel() {
// Friday 11:28 Saturday 09:00 is 21h32m; the reference shows "Starts in 22h".
let weekend = BlockingRule(name: "Weekend Zen", days: Weekday.weekends)
let friday = date(2025, 1, 10, 11, 28)
#expect(weekend.status(at: friday, calendar: utc).label(relativeTo: friday) == "Starts in 22h")
}
@Test("Countdown formatting tiers", arguments: [
(30, "30m"), (59, "59m"), (60, "1h"), (90, "2h"),
(6 * 60, "6h"), (47 * 60, "47h"), (49 * 60, "2d"), (75 * 60, "3d"),
])
func countdownTiers(minutes: Int, expected: String) {
let now = date(2025, 1, 6, 0, 0)
let target = utc.date(byAdding: .minute, value: minutes, to: now)!
#expect(RuleStatus.countdown(from: now, to: target) == expected)
}
@Test("Countdown never reads below one minute")
func countdownFloor() {
let now = date(2025, 1, 6, 0, 0)
#expect(RuleStatus.countdown(from: now, to: now.addingTimeInterval(5)) == "1m")
}
@Test("Static labels")
func staticLabels() {
let now = date(2025, 1, 6, 0, 0)
#expect(RuleStatus.disabled.label(relativeTo: now) == "Disabled")
#expect(RuleStatus.dormant.label(relativeTo: now) == "No days selected")
#expect(RuleStatus.paused(until: now).label(relativeTo: now) == "Paused")
}
}