import Foundation
|
import XCTest
|
@testable import AlignerCore
|
|
final class EntitlementPolicyTests: XCTestCase {
|
private let now = Date(timeIntervalSince1970: 1_800_000_000)
|
|
func testTrialIsAllowedBeforeThirtyDayBoundary() {
|
let startedAt = now.addingTimeInterval(-29 * 24 * 60 * 60 - 23 * 60 * 60 - 59 * 60)
|
let snapshot = EntitlementSnapshot(
|
trialState: EntitlementPolicy.trialStarted(at: startedAt),
|
licenseState: .none,
|
validationFreshness: .neverChecked
|
)
|
|
XCTAssertEqual(
|
EntitlementPolicy.evaluate(snapshot: snapshot, feature: .windowShortcutActivation, now: now),
|
.proAllowed(reason: .trialActive)
|
)
|
}
|
|
func testTrialExpiresAtThirtyDayBoundary() {
|
let startedAt = now.addingTimeInterval(-EntitlementPolicy.defaultTrialDuration)
|
let snapshot = EntitlementSnapshot(
|
trialState: EntitlementPolicy.trialStarted(at: startedAt),
|
licenseState: .none,
|
validationFreshness: .neverChecked
|
)
|
|
XCTAssertEqual(
|
EntitlementPolicy.evaluate(snapshot: snapshot, feature: .windowShortcutActivation, now: now),
|
.proDenied(reason: .trialExpired)
|
)
|
}
|
|
func testInvalidTrialRecordIsDeniedWhenNoLicenseExists() {
|
let snapshot = EntitlementSnapshot(
|
trialState: .active(startedAt: now, expiresAt: now.addingTimeInterval(-1)),
|
licenseState: .none,
|
validationFreshness: .neverChecked
|
)
|
|
XCTAssertEqual(
|
EntitlementPolicy.evaluate(snapshot: snapshot, feature: .windowShortcutActivation, now: now),
|
.proDenied(reason: .invalidLocalRecord)
|
)
|
}
|
|
func testValidLicenseAllowsProAfterTrialExpired() {
|
let snapshot = EntitlementSnapshot(
|
trialState: expiredTrial(),
|
licenseState: activeLicense(),
|
validationFreshness: .valid(
|
lastValidAt: now.addingTimeInterval(-60),
|
nextCheckAt: now.addingTimeInterval(60)
|
)
|
)
|
|
XCTAssertEqual(
|
EntitlementPolicy.evaluate(snapshot: snapshot, feature: .windowShortcutActivation, now: now),
|
.proAllowed(reason: .licenseValid)
|
)
|
}
|
|
func testOfflineGraceAllowsProBeforeFourteenDayBoundary() {
|
let lastAttemptAt = now.addingTimeInterval(-13 * 24 * 60 * 60 - 23 * 60 * 60 - 59 * 60)
|
let snapshot = EntitlementSnapshot(
|
trialState: expiredTrial(),
|
licenseState: activeLicense(),
|
validationFreshness: EntitlementPolicy.offlineGrace(lastAttemptAt: lastAttemptAt)
|
)
|
|
XCTAssertEqual(
|
EntitlementPolicy.evaluate(snapshot: snapshot, feature: .windowShortcutActivation, now: now),
|
.proAllowed(reason: .offlineGrace)
|
)
|
}
|
|
func testOfflineGraceExpiresAtFourteenDayBoundary() {
|
let lastAttemptAt = now.addingTimeInterval(-EntitlementPolicy.defaultOfflineGraceDuration)
|
let snapshot = EntitlementSnapshot(
|
trialState: expiredTrial(),
|
licenseState: activeLicense(),
|
validationFreshness: EntitlementPolicy.offlineGrace(lastAttemptAt: lastAttemptAt)
|
)
|
|
XCTAssertEqual(
|
EntitlementPolicy.evaluate(snapshot: snapshot, feature: .windowShortcutActivation, now: now),
|
.proDenied(reason: .graceExpired)
|
)
|
}
|
|
func testHardFailureIsDeniedWhenTrialIsExpired() {
|
let snapshot = EntitlementSnapshot(
|
trialState: expiredTrial(),
|
licenseState: activeLicense(),
|
validationFreshness: .hardFailure(reason: .revoked)
|
)
|
|
XCTAssertEqual(
|
EntitlementPolicy.evaluate(snapshot: snapshot, feature: .windowShortcutActivation, now: now),
|
.proDenied(reason: .hardFailure)
|
)
|
}
|
|
func testActiveTrialStillAllowsWhenLicenseVerificationFails() {
|
let snapshot = EntitlementSnapshot(
|
trialState: EntitlementPolicy.trialStarted(at: now.addingTimeInterval(-60)),
|
licenseState: activeLicense(),
|
validationFreshness: .hardFailure(reason: .serverRejected)
|
)
|
|
XCTAssertEqual(
|
EntitlementPolicy.evaluate(snapshot: snapshot, feature: .windowShortcutActivation, now: now),
|
.proAllowed(reason: .trialActive)
|
)
|
}
|
|
func testDeactivatedLicenseIsDeniedAfterTrialExpired() {
|
let snapshot = EntitlementSnapshot(
|
trialState: expiredTrial(),
|
licenseState: .deactivatedLocal,
|
validationFreshness: .neverChecked
|
)
|
|
XCTAssertEqual(
|
EntitlementPolicy.evaluate(snapshot: snapshot, feature: .windowShortcutActivation, now: now),
|
.proDenied(reason: .licenseDeactivated)
|
)
|
}
|
|
func testDisabledLicenseIsDeniedAfterTrialExpired() {
|
let snapshot = EntitlementSnapshot(
|
trialState: expiredTrial(),
|
licenseState: .activated(fingerprint: "abc123", instanceID: "instance-1", status: .disabled),
|
validationFreshness: .valid(
|
lastValidAt: now.addingTimeInterval(-60),
|
nextCheckAt: now.addingTimeInterval(60)
|
)
|
)
|
|
XCTAssertEqual(
|
EntitlementPolicy.evaluate(snapshot: snapshot, feature: .windowShortcutActivation, now: now),
|
.proDenied(reason: .licenseDisabled)
|
)
|
}
|
|
func testProductMismatchIsDeniedAfterTrialExpired() {
|
let snapshot = EntitlementSnapshot(
|
trialState: expiredTrial(),
|
licenseState: .mismatch(reason: .product),
|
validationFreshness: .neverChecked
|
)
|
|
XCTAssertEqual(
|
EntitlementPolicy.evaluate(snapshot: snapshot, feature: .windowShortcutActivation, now: now),
|
.proDenied(reason: .licenseMismatch)
|
)
|
}
|
|
func testRemoteRevokedLicenseIsDeniedAfterTrialExpired() {
|
let snapshot = EntitlementSnapshot(
|
trialState: expiredTrial(),
|
licenseState: .revokedOrExpiredRemote(reason: .revoked),
|
validationFreshness: .neverChecked
|
)
|
|
XCTAssertEqual(
|
EntitlementPolicy.evaluate(snapshot: snapshot, feature: .windowShortcutActivation, now: now),
|
.proDenied(reason: .licenseRevokedOrExpired)
|
)
|
}
|
|
func testFixtureProviderCoversRound015NamedStates() {
|
let expectations: [EntitlementFixtureState: EffectiveEntitlement] = [
|
.trialActive: .proAllowed(reason: .trialActive),
|
.trialExpired: .proDenied(reason: .trialExpired),
|
.proUnlocked: .proAllowed(reason: .licenseValid),
|
.offlineGrace: .proAllowed(reason: .offlineGrace),
|
.verifyFailed: .proDenied(reason: .hardFailure)
|
]
|
|
for (fixtureState, expectedEntitlement) in expectations {
|
let provider = FixtureEntitlementProvider(fixtureState: fixtureState, now: now)
|
XCTAssertEqual(
|
provider.entitlement(for: .windowShortcutActivation, at: now),
|
expectedEntitlement,
|
"fixture state \(fixtureState.rawValue)"
|
)
|
}
|
}
|
|
private func activeLicense() -> LicenseState {
|
.activated(fingerprint: "abc123", instanceID: "instance-1", status: .active)
|
}
|
|
private func expiredTrial() -> TrialState {
|
.expired(
|
startedAt: now.addingTimeInterval(-31 * 24 * 60 * 60),
|
expiredAt: now.addingTimeInterval(-24 * 60 * 60)
|
)
|
}
|
}
|