mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-25 12:25:19 +00:00
- Delete MessageRetentionService.swift - Remove retentionEnabledChannels from ChatViewModel - Remove /save command handling - Remove retention UI elements from ContentView - Remove channelRetention message type from protocol - Update documentation and tests
183 lines
6.9 KiB
Swift
183 lines
6.9 KiB
Swift
//
|
|
// KeychainIntegrationTests.swift
|
|
// bitchatTests
|
|
//
|
|
// Integration tests for keychain functionality
|
|
//
|
|
|
|
import XCTest
|
|
@testable import bitchat
|
|
|
|
class KeychainIntegrationTests: XCTestCase {
|
|
|
|
override func setUp() {
|
|
super.setUp()
|
|
// Start with clean state
|
|
_ = KeychainManager.shared.deleteAllKeychainData()
|
|
}
|
|
|
|
override func tearDown() {
|
|
// Clean up test data
|
|
_ = KeychainManager.shared.deleteAllKeychainData()
|
|
super.tearDown()
|
|
}
|
|
|
|
// MARK: - App Lifecycle Simulation Tests
|
|
|
|
func testCompleteAppLifecycle() {
|
|
print("\n🧪 Testing Complete App Lifecycle")
|
|
|
|
// 1. First app launch - create identity
|
|
print("1️⃣ First launch...")
|
|
let service1 = NoiseEncryptionService()
|
|
let fingerprint1 = service1.getIdentityFingerprint()
|
|
print(" Initial fingerprint: \(fingerprint1)")
|
|
|
|
// Verify stored in keychain
|
|
let keychainData1 = KeychainManager.shared.getIdentityKey(forKey: "noiseStaticKey")
|
|
XCTAssertNotNil(keychainData1, "Identity should be in keychain after first launch")
|
|
|
|
// 2. App goes to background and comes back
|
|
print("2️⃣ Background/foreground cycle...")
|
|
let service2 = NoiseEncryptionService()
|
|
let fingerprint2 = service2.getIdentityFingerprint()
|
|
XCTAssertEqual(fingerprint1, fingerprint2, "Identity should persist through background")
|
|
|
|
// 3. App terminates and relaunches
|
|
print("3️⃣ Terminate and relaunch...")
|
|
// In real app this would be a new process
|
|
let service3 = NoiseEncryptionService()
|
|
let fingerprint3 = service3.getIdentityFingerprint()
|
|
XCTAssertEqual(fingerprint1, fingerprint3, "Identity should persist through termination")
|
|
|
|
// 4. User triggers panic mode
|
|
print("4️⃣ Panic mode triggered...")
|
|
service3.clearPersistentIdentity()
|
|
|
|
// 5. App creates new identity
|
|
print("5️⃣ New identity after panic...")
|
|
let service4 = NoiseEncryptionService()
|
|
let fingerprint4 = service4.getIdentityFingerprint()
|
|
XCTAssertNotEqual(fingerprint1, fingerprint4, "New identity should be created after panic")
|
|
print(" New fingerprint: \(fingerprint4)")
|
|
|
|
print("✅ Lifecycle test complete\n")
|
|
}
|
|
|
|
// MARK: - Channel Password Tests
|
|
|
|
func testChannelPasswordPersistence() {
|
|
let channel1 = "#testchannel1"
|
|
let channel2 = "#testchannel2"
|
|
let password1 = "password123"
|
|
let password2 = "differentpass456"
|
|
|
|
// Save passwords
|
|
XCTAssertTrue(KeychainManager.shared.saveChannelPassword(password1, for: channel1))
|
|
XCTAssertTrue(KeychainManager.shared.saveChannelPassword(password2, for: channel2))
|
|
|
|
// Retrieve passwords
|
|
XCTAssertEqual(KeychainManager.shared.getChannelPassword(for: channel1), password1)
|
|
XCTAssertEqual(KeychainManager.shared.getChannelPassword(for: channel2), password2)
|
|
|
|
// Test getAllChannelPasswords
|
|
let allPasswords = KeychainManager.shared.getAllChannelPasswords()
|
|
XCTAssertEqual(allPasswords.count, 2)
|
|
XCTAssertEqual(allPasswords[channel1], password1)
|
|
XCTAssertEqual(allPasswords[channel2], password2)
|
|
|
|
// Delete one password
|
|
XCTAssertTrue(KeychainManager.shared.deleteChannelPassword(for: channel1))
|
|
XCTAssertNil(KeychainManager.shared.getChannelPassword(for: channel1))
|
|
XCTAssertEqual(KeychainManager.shared.getChannelPassword(for: channel2), password2)
|
|
}
|
|
|
|
// MARK: - Security Tests
|
|
|
|
func testNoPlaintextInUserDefaults() {
|
|
// Create services to generate keys
|
|
_ = NoiseEncryptionService()
|
|
|
|
// Check UserDefaults for any sensitive data
|
|
let keysToCheck = [
|
|
"bitchat.noiseIdentityKey",
|
|
"bitchat.channelPasswords",
|
|
"bitchat.identityKey",
|
|
"bitchat.staticKey"
|
|
]
|
|
|
|
for key in keysToCheck {
|
|
let data = UserDefaults.standard.object(forKey: key)
|
|
XCTAssertNil(data, "UserDefaults should not contain: \(key)")
|
|
}
|
|
}
|
|
|
|
// MARK: - Error Handling Tests
|
|
|
|
func testKeychainErrorRecovery() {
|
|
// Test that the app can recover from keychain errors
|
|
// This is difficult to test without mocking, but we can verify
|
|
// that multiple save attempts don't crash
|
|
|
|
let testData = "test".data(using: .utf8)!
|
|
|
|
// Rapid saves
|
|
for i in 0..<10 {
|
|
let saved = KeychainManager.shared.saveIdentityKey(testData, forKey: "rapidTest\(i)")
|
|
XCTAssertTrue(saved, "Save \(i) should succeed")
|
|
}
|
|
|
|
// Rapid deletes
|
|
for i in 0..<10 {
|
|
_ = KeychainManager.shared.deleteIdentityKey(forKey: "rapidTest\(i)")
|
|
}
|
|
}
|
|
|
|
// MARK: - Cleanup Tests
|
|
|
|
func testAggressiveCleanupOnlyDeletesBitchatItems() {
|
|
// This test verifies we don't delete other apps' keychain items
|
|
|
|
// Add a non-bitchat item (simulating another app)
|
|
let otherAppQuery: [String: Any] = [
|
|
kSecClass as String: kSecClassGenericPassword,
|
|
kSecAttrService as String: "com.otherapp.service",
|
|
kSecAttrAccount as String: "other_app_account",
|
|
kSecValueData as String: "other app data".data(using: .utf8)!
|
|
]
|
|
|
|
// Clean first
|
|
SecItemDelete(otherAppQuery as CFDictionary)
|
|
|
|
// Add the item
|
|
let addStatus = SecItemAdd(otherAppQuery as CFDictionary, nil)
|
|
XCTAssertEqual(addStatus, errSecSuccess, "Should add other app item")
|
|
|
|
// Add a bitchat legacy item
|
|
let bitchatQuery: [String: Any] = [
|
|
kSecClass as String: kSecClassGenericPassword,
|
|
kSecAttrService as String: "com.bitchat.legacy",
|
|
kSecAttrAccount as String: "test_account",
|
|
kSecValueData as String: "bitchat data".data(using: .utf8)!
|
|
]
|
|
SecItemDelete(bitchatQuery as CFDictionary)
|
|
let bitchatStatus = SecItemAdd(bitchatQuery as CFDictionary, nil)
|
|
XCTAssertEqual(bitchatStatus, errSecSuccess, "Should add bitchat item")
|
|
|
|
// Run aggressive cleanup
|
|
_ = KeychainManager.shared.aggressiveCleanupLegacyItems()
|
|
|
|
// Verify other app item still exists
|
|
var result: AnyObject?
|
|
let checkStatus = SecItemCopyMatching(otherAppQuery as CFDictionary, &result)
|
|
XCTAssertEqual(checkStatus, errSecSuccess, "Other app item should still exist")
|
|
|
|
// Verify bitchat item was deleted
|
|
var bitchatResult: AnyObject?
|
|
let bitchatCheck = SecItemCopyMatching(bitchatQuery as CFDictionary, &bitchatResult)
|
|
XCTAssertEqual(bitchatCheck, errSecItemNotFound, "Bitchat legacy item should be deleted")
|
|
|
|
// Clean up
|
|
SecItemDelete(otherAppQuery as CFDictionary)
|
|
}
|
|
} |