import Foundation import XCTest @testable import bitchat final class SecureIdentityStateManagerTests: XCTestCase { func test_upsertCryptographicIdentity_tracksByPeerIDPrefixAndClaimedNickname() async { let manager = SecureIdentityStateManager(MockKeychain()) let noisePublicKey = Data(repeating: 0x11, count: 32) let signingPublicKey = Data(repeating: 0x22, count: 32) let fingerprint = noisePublicKey.sha256Fingerprint() manager.upsertCryptographicIdentity( fingerprint: fingerprint, noisePublicKey: noisePublicKey, signingPublicKey: signingPublicKey, claimedNickname: "Alice" ) let socialIdentityLoaded = await waitUntil { manager.getSocialIdentity(for: fingerprint)?.claimedNickname == "Alice" } XCTAssertTrue(socialIdentityLoaded) let matches = manager.getCryptoIdentitiesByPeerIDPrefix(PeerID(publicKey: noisePublicKey)) XCTAssertEqual(matches.count, 1) XCTAssertEqual(matches.first?.fingerprint, fingerprint) XCTAssertEqual(matches.first?.publicKey, noisePublicKey) XCTAssertEqual(matches.first?.signingPublicKey, signingPublicKey) } func test_setBlocked_clearsFavoriteState() async { let manager = SecureIdentityStateManager(MockKeychain()) let fingerprint = String(repeating: "ab", count: 32) manager.setFavorite(fingerprint, isFavorite: true) let favoriteSet = await waitUntil { manager.isFavorite(fingerprint: fingerprint) } XCTAssertTrue(favoriteSet) manager.setBlocked(fingerprint, isBlocked: true) let blockedSet = await waitUntil { manager.isBlocked(fingerprint: fingerprint) } XCTAssertTrue(blockedSet) XCTAssertFalse(manager.isFavorite(fingerprint: fingerprint)) XCTAssertEqual(manager.getSocialIdentity(for: fingerprint)?.claimedNickname, "Unknown") } func test_setVerified_updatesTrustLevelAndVerifiedSet() async { let manager = SecureIdentityStateManager(MockKeychain()) let fingerprint = String(repeating: "cd", count: 32) manager.setFavorite(fingerprint, isFavorite: false) _ = await waitUntil { manager.getSocialIdentity(for: fingerprint) != nil } manager.setVerified(fingerprint: fingerprint, verified: true) let verifiedSet = await waitUntil { manager.isVerified(fingerprint: fingerprint) } XCTAssertTrue(verifiedSet) XCTAssertTrue(manager.getVerifiedFingerprints().contains(fingerprint)) XCTAssertEqual(manager.getSocialIdentity(for: fingerprint)?.trustLevel, .verified) } func test_forceSave_persistsFavoriteStateAcrossReinit() async { let keychain = MockKeychain() let manager = SecureIdentityStateManager(keychain) let fingerprint = String(repeating: "ef", count: 32) manager.setFavorite(fingerprint, isFavorite: true) let favoriteSet = await waitUntil { manager.isFavorite(fingerprint: fingerprint) } XCTAssertTrue(favoriteSet) manager.forceSave() let reloaded = SecureIdentityStateManager(keychain) XCTAssertTrue(reloaded.isFavorite(fingerprint: fingerprint)) } func test_setNostrBlocked_normalizesToLowercaseAndPersists() async { let keychain = MockKeychain() let manager = SecureIdentityStateManager(keychain) let pubkey = "ABCDEF1234" manager.setNostrBlocked(pubkey, isBlocked: true) let nostrBlocked = await waitUntil { manager.isNostrBlocked(pubkeyHexLowercased: pubkey.lowercased()) } XCTAssertTrue(nostrBlocked) manager.forceSave() let reloaded = SecureIdentityStateManager(keychain) XCTAssertEqual(reloaded.getBlockedNostrPubkeys(), Set([pubkey.lowercased()])) XCTAssertTrue(reloaded.isNostrBlocked(pubkeyHexLowercased: pubkey)) } func test_corruptPersistedCache_fallsBackToEmptyState() { let keychain = MockKeychain() _ = keychain.saveIdentityKey(Data(repeating: 0x01, count: 32), forKey: "identityCacheEncryptionKey") _ = keychain.saveIdentityKey(Data([0xFF, 0x00, 0xAA]), forKey: "bitchat.identityCache.v2") let manager = SecureIdentityStateManager(keychain) XCTAssertTrue(manager.getFavorites().isEmpty) XCTAssertTrue(manager.getVerifiedFingerprints().isEmpty) XCTAssertTrue(manager.getBlockedNostrPubkeys().isEmpty) } func test_clearAllIdentityData_removesCachedState() async { let manager = SecureIdentityStateManager(MockKeychain()) let fingerprint = String(repeating: "12", count: 32) manager.setFavorite(fingerprint, isFavorite: true) manager.setVerified(fingerprint: fingerprint, verified: true) manager.setNostrBlocked("ABCD", isBlocked: true) let primed = await waitUntil { manager.isFavorite(fingerprint: fingerprint) && manager.isVerified(fingerprint: fingerprint) } XCTAssertTrue(primed) manager.clearAllIdentityData() let cleared = await waitUntil { !manager.isFavorite(fingerprint: fingerprint) && !manager.isVerified(fingerprint: fingerprint) && manager.getBlockedNostrPubkeys().isEmpty } XCTAssertTrue(cleared) } private func waitUntil( timeout: TimeInterval = 1.0, condition: @escaping () -> Bool ) async -> Bool { let deadline = Date().addingTimeInterval(timeout) while Date() < deadline { if condition() { return true } try? await Task.sleep(nanoseconds: 10_000_000) } return condition() } }