// // 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) } }