import XCTest @testable import SharedCore final class TokenSecurityTests: XCTestCase { func testSanitizeTokenRejectsEmptyAndRedactedValues() { XCTAssertNil(TokenSecurity.sanitizeToken(nil)) XCTAssertNil(TokenSecurity.sanitizeToken(" ")) XCTAssertNil(TokenSecurity.sanitizeToken("REDACTED_TOKEN")) XCTAssertEqual(TokenSecurity.sanitizeToken(" abc123 "), "abc123") } func testSanitizeTokenNormalizesAuthorizationPrefixes() { XCTAssertEqual(TokenSecurity.sanitizeToken("Token abc123"), "abc123") XCTAssertEqual(TokenSecurity.sanitizeToken("bearer xyz789"), "xyz789") XCTAssertNil(TokenSecurity.sanitizeToken("Token ")) } func testContainsPotentialHardcodedTokenDetectsLongHexBlob() { let content = "private let token = \"0123456789abcdef0123456789abcdef\"" XCTAssertTrue(TokenSecurity.containsPotentialHardcodedToken(in: content)) } func testJWTExpirationAndRotationWindow() throws { let now = Date(timeIntervalSince1970: 1_700_000_000) let expiration = now.addingTimeInterval(30 * 60) let token = try makeJWT(exp: expiration) XCTAssertEqual(TokenSecurity.jwtExpiration(token), expiration) XCTAssertFalse(TokenSecurity.isExpired(token, now: now)) XCTAssertTrue(TokenSecurity.shouldRotate(token, now: now, rotationWindow: 60 * 60)) } func testExpiredJWTReturnsExpired() throws { let now = Date(timeIntervalSince1970: 1_700_000_000) let expiration = now.addingTimeInterval(-10) let token = try makeJWT(exp: expiration) XCTAssertTrue(TokenSecurity.isExpired(token, now: now)) } func testMalformedTokenDoesNotCrashAndDoesNotTriggerRotation() { let malformed = "not-a-jwt" XCTAssertNil(TokenSecurity.jwtExpiration(malformed)) XCTAssertFalse(TokenSecurity.isExpired(malformed)) XCTAssertFalse(TokenSecurity.shouldRotate(malformed)) } private func makeJWT(exp: Date) throws -> String { let header = ["alg": "HS256", "typ": "JWT"] let payload = ["exp": Int(exp.timeIntervalSince1970)] let headerData = try JSONSerialization.data(withJSONObject: header) let payloadData = try JSONSerialization.data(withJSONObject: payload) return "\(base64URL(headerData)).\(base64URL(payloadData)).signature" } private func base64URL(_ data: Data) -> String { data.base64EncodedString() .replacingOccurrences(of: "+", with: "-") .replacingOccurrences(of: "/", with: "_") .replacingOccurrences(of: "=", with: "") } }