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:
126
OpenAppLockTests/RuleEnforcerTests.swift
Normal file
126
OpenAppLockTests/RuleEnforcerTests.swift
Normal file
@@ -0,0 +1,126 @@
|
||||
//
|
||||
// RuleEnforcerTests.swift
|
||||
// OpenAppLockTests
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Testing
|
||||
|
||||
@testable import OpenAppLock
|
||||
|
||||
@MainActor
|
||||
@Suite("Rule enforcement → shields")
|
||||
struct RuleEnforcerTests {
|
||||
let mondayDuringWork = date(2025, 1, 6, 10, 0)
|
||||
let mondayEvening = date(2025, 1, 6, 19, 0)
|
||||
|
||||
@Test("Active schedule rules are shielded; inactive ones are not")
|
||||
func shieldsActiveRules() {
|
||||
let shields = MockShieldController()
|
||||
let enforcer = RuleEnforcer(shields: shields)
|
||||
let active = BlockingRule(name: "Work Time")
|
||||
let weekendOnly = BlockingRule(name: "Weekend Zen", days: Weekday.weekends)
|
||||
|
||||
enforcer.refresh(rules: [active, weekendOnly], at: mondayDuringWork, calendar: utc)
|
||||
|
||||
#expect(shields.shieldedRuleIDs == [active.id])
|
||||
#expect(enforcer.blockingRuleIDs == [active.id])
|
||||
}
|
||||
|
||||
@Test("Disabled rules are never shielded")
|
||||
func skipsDisabledRules() {
|
||||
let shields = MockShieldController()
|
||||
let enforcer = RuleEnforcer(shields: shields)
|
||||
let rule = BlockingRule(name: "Work Time", isEnabled: false)
|
||||
|
||||
enforcer.refresh(rules: [rule], at: mondayDuringWork, calendar: utc)
|
||||
|
||||
#expect(shields.shieldedRuleIDs.isEmpty)
|
||||
}
|
||||
|
||||
@Test("Paused rules are not shielded")
|
||||
func skipsPausedRules() {
|
||||
let shields = MockShieldController()
|
||||
let enforcer = RuleEnforcer(shields: shields)
|
||||
let rule = BlockingRule(name: "Work Time")
|
||||
RulePolicy.unblock(rule, at: mondayDuringWork, calendar: utc)
|
||||
|
||||
enforcer.refresh(rules: [rule], at: mondayDuringWork, calendar: utc)
|
||||
|
||||
#expect(shields.shieldedRuleIDs.isEmpty)
|
||||
}
|
||||
|
||||
@Test("Time-limit rules are not schedule-shielded")
|
||||
func skipsTimeLimitRules() {
|
||||
let shields = MockShieldController()
|
||||
let enforcer = RuleEnforcer(shields: shields)
|
||||
let rule = BlockingRule(name: "Time Keeper", kind: .timeLimit)
|
||||
|
||||
enforcer.refresh(rules: [rule], at: mondayDuringWork, calendar: utc)
|
||||
|
||||
#expect(shields.shieldedRuleIDs.isEmpty)
|
||||
}
|
||||
|
||||
@Test("Shields are cleared when a window ends")
|
||||
func clearsShieldAfterWindow() {
|
||||
let shields = MockShieldController()
|
||||
let enforcer = RuleEnforcer(shields: shields)
|
||||
let rule = BlockingRule(name: "Work Time")
|
||||
|
||||
enforcer.refresh(rules: [rule], at: mondayDuringWork, calendar: utc)
|
||||
#expect(shields.shieldedRuleIDs == [rule.id])
|
||||
|
||||
enforcer.refresh(rules: [rule], at: mondayEvening, calendar: utc)
|
||||
#expect(shields.shieldedRuleIDs.isEmpty)
|
||||
#expect(enforcer.blockingRuleIDs.isEmpty)
|
||||
}
|
||||
|
||||
@Test("Shields are cleared when a rule is deleted")
|
||||
func clearsShieldAfterDeletion() {
|
||||
let shields = MockShieldController()
|
||||
let enforcer = RuleEnforcer(shields: shields)
|
||||
let rule = BlockingRule(name: "Work Time")
|
||||
|
||||
enforcer.refresh(rules: [rule], at: mondayDuringWork, calendar: utc)
|
||||
enforcer.refresh(rules: [], at: mondayDuringWork, calendar: utc)
|
||||
|
||||
#expect(shields.shieldedRuleIDs.isEmpty)
|
||||
}
|
||||
|
||||
@Test("Expired pauses are cleaned up during refresh")
|
||||
func clearsExpiredPause() {
|
||||
let shields = MockShieldController()
|
||||
let enforcer = RuleEnforcer(shields: shields)
|
||||
let rule = BlockingRule(name: "Work Time")
|
||||
rule.pausedUntil = date(2025, 1, 6, 9, 30)
|
||||
|
||||
enforcer.refresh(rules: [rule], at: mondayDuringWork, calendar: utc)
|
||||
|
||||
#expect(rule.pausedUntil == nil)
|
||||
#expect(shields.shieldedRuleIDs == [rule.id])
|
||||
}
|
||||
|
||||
@Test("The selection mode is forwarded to the shield layer")
|
||||
func forwardsSelectionMode() {
|
||||
let shields = MockShieldController()
|
||||
let enforcer = RuleEnforcer(shields: shields)
|
||||
let rule = BlockingRule(name: "Focus", selectionMode: .allowOnly)
|
||||
|
||||
enforcer.refresh(rules: [rule], at: mondayDuringWork, calendar: utc)
|
||||
|
||||
#expect(shields.appliedModes[rule.id] == .allowOnly)
|
||||
}
|
||||
|
||||
@Test("The adult-content flag is forwarded to the shield layer")
|
||||
func forwardsAdultContentFlag() {
|
||||
let shields = MockShieldController()
|
||||
let enforcer = RuleEnforcer(shields: shields)
|
||||
let filtered = BlockingRule(name: "Clean Mode", blockAdultContent: true)
|
||||
let unfiltered = BlockingRule(name: "Plain")
|
||||
|
||||
enforcer.refresh(rules: [filtered, unfiltered], at: mondayDuringWork, calendar: utc)
|
||||
|
||||
#expect(shields.appliedAdultContentFlags[filtered.id] == true)
|
||||
#expect(shields.appliedAdultContentFlags[unfiltered.id] == false)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user