194 lines
7.1 KiB
Swift
194 lines
7.1 KiB
Swift
import XCTest
|
|
@testable import Rosetta
|
|
|
|
@MainActor
|
|
final class CallDisplayNameTests: XCTestCase {
|
|
|
|
private let ownKey = "02-own"
|
|
private let peerKey = "0263e9134d3abeb880bb5fa679954800a80d0c286c5c54c9452a996b0c7608db3"
|
|
|
|
override func setUp() {
|
|
super.setUp()
|
|
CallManager.shared.resetForSessionEnd()
|
|
CallManager.shared.bindAccount(publicKey: ownKey)
|
|
}
|
|
|
|
override func tearDown() {
|
|
CallManager.shared.resetForSessionEnd()
|
|
super.tearDown()
|
|
}
|
|
|
|
// MARK: - CallUiState.displayName fallback chain
|
|
|
|
func testDisplayNamePrefersTitle() {
|
|
let state = CallUiState(
|
|
peerPublicKey: peerKey,
|
|
peerTitle: "Alice",
|
|
peerUsername: "alice"
|
|
)
|
|
XCTAssertEqual(state.displayName, "Alice")
|
|
}
|
|
|
|
func testDisplayNameFallsBackToUsername() {
|
|
let state = CallUiState(
|
|
peerPublicKey: peerKey,
|
|
peerTitle: "",
|
|
peerUsername: "alice"
|
|
)
|
|
XCTAssertEqual(state.displayName, "@alice")
|
|
}
|
|
|
|
func testDisplayNameFallsBackToKeyPrefix() {
|
|
let state = CallUiState(
|
|
peerPublicKey: peerKey,
|
|
peerTitle: "",
|
|
peerUsername: ""
|
|
)
|
|
XCTAssertEqual(state.displayName, String(peerKey.prefix(12)))
|
|
}
|
|
|
|
func testDisplayNameReturnsUnknownWhenEmpty() {
|
|
let state = CallUiState()
|
|
XCTAssertEqual(state.displayName, "Unknown")
|
|
}
|
|
|
|
// MARK: - Outgoing call sets displayName before CallKit
|
|
|
|
func testOutgoingCallSetsDisplayNameFromTitle() {
|
|
let result = CallManager.shared.startOutgoingCall(
|
|
toPublicKey: peerKey,
|
|
title: "Alice",
|
|
username: "alice"
|
|
)
|
|
XCTAssertEqual(result, .started)
|
|
// After startOutgoingCall, uiState should have peerTitle set
|
|
XCTAssertEqual(CallManager.shared.uiState.peerTitle, "Alice")
|
|
XCTAssertEqual(CallManager.shared.uiState.displayName, "Alice")
|
|
}
|
|
|
|
func testOutgoingCallWithEmptyTitleUsesUsername() {
|
|
let result = CallManager.shared.startOutgoingCall(
|
|
toPublicKey: peerKey,
|
|
title: "",
|
|
username: "alice"
|
|
)
|
|
XCTAssertEqual(result, .started)
|
|
XCTAssertEqual(CallManager.shared.uiState.displayName, "@alice")
|
|
}
|
|
|
|
func testOutgoingCallWithNoIdentityUsesKeyPrefix() {
|
|
let result = CallManager.shared.startOutgoingCall(
|
|
toPublicKey: peerKey,
|
|
title: "",
|
|
username: ""
|
|
)
|
|
XCTAssertEqual(result, .started)
|
|
// Should NOT be the full key — must be truncated prefix
|
|
let displayName = CallManager.shared.uiState.displayName
|
|
XCTAssertEqual(displayName, String(peerKey.prefix(12)))
|
|
XCTAssertNotEqual(displayName, peerKey, "Full public key must NEVER be used as display name")
|
|
}
|
|
|
|
// MARK: - Incoming call name hydration
|
|
|
|
func testIncomingCallDisplayNameDefaultsToKeyPrefix() {
|
|
// Incoming calls start with empty title (signal packet has no name field)
|
|
let packet = PacketSignalPeer(
|
|
src: peerKey,
|
|
dst: ownKey,
|
|
sharedPublic: "",
|
|
signalType: .call,
|
|
roomId: ""
|
|
)
|
|
CallManager.shared.testHandleSignalPacket(packet)
|
|
|
|
XCTAssertEqual(CallManager.shared.uiState.phase, .incoming)
|
|
// Without DialogRepository entry, name falls back to key prefix
|
|
let displayName = CallManager.shared.uiState.displayName
|
|
XCTAssertTrue(displayName.count <= 12 || displayName == "Unknown",
|
|
"Incoming call display name should be short prefix, not full key: \(displayName)")
|
|
}
|
|
|
|
// MARK: - Server parity: signal packet has NO name field
|
|
|
|
func testIncomingCallFromServerHasNoNameInPacket() {
|
|
// Server PacketSignalPeer (0x1A) sends only src/dst public keys — no title.
|
|
// Verify that after signal processing, displayName is NOT the full public key.
|
|
let packet = PacketSignalPeer(
|
|
src: peerKey,
|
|
dst: ownKey,
|
|
sharedPublic: "",
|
|
signalType: .call,
|
|
roomId: ""
|
|
)
|
|
CallManager.shared.testHandleSignalPacket(packet)
|
|
|
|
let displayName = CallManager.shared.uiState.displayName
|
|
XCTAssertNotEqual(displayName, peerKey,
|
|
"Server sends no name — displayName must NOT be full 66-char public key")
|
|
XCTAssertTrue(displayName.count < 20,
|
|
"Display name should be short (prefix or 'Unknown'), got: \(displayName)")
|
|
}
|
|
|
|
// MARK: - Outgoing call: displayName available BEFORE CallKit report
|
|
|
|
func testDisplayNameSetBeforeCallKitReport() {
|
|
// beginCallSession is called BEFORE startOutgoingCall(peerKey:displayName:)
|
|
// so uiState.displayName must already be resolved when CallKit is invoked.
|
|
let result = CallManager.shared.startOutgoingCall(
|
|
toPublicKey: peerKey,
|
|
title: "Bob",
|
|
username: "bob"
|
|
)
|
|
XCTAssertEqual(result, .started)
|
|
// After startOutgoingCall, peerTitle should be "Bob" (set by beginCallSession)
|
|
XCTAssertEqual(CallManager.shared.uiState.peerTitle, "Bob")
|
|
// CallKit was called with displayName = "Bob" (verified by the flow:
|
|
// beginCallSession sets peerTitle → startOutgoingCall reads uiState.displayName)
|
|
}
|
|
|
|
// MARK: - Full key never leaks as display name
|
|
|
|
func testFullKeyNeverUsedInAnyScenario() {
|
|
// Scenario 1: outgoing with title
|
|
_ = CallManager.shared.startOutgoingCall(
|
|
toPublicKey: peerKey, title: "Alice", username: ""
|
|
)
|
|
XCTAssertNotEqual(CallManager.shared.uiState.displayName, peerKey)
|
|
CallManager.shared.resetForSessionEnd()
|
|
CallManager.shared.bindAccount(publicKey: ownKey)
|
|
|
|
// Scenario 2: outgoing with username only
|
|
_ = CallManager.shared.startOutgoingCall(
|
|
toPublicKey: peerKey, title: "", username: "alice"
|
|
)
|
|
XCTAssertNotEqual(CallManager.shared.uiState.displayName, peerKey)
|
|
CallManager.shared.resetForSessionEnd()
|
|
CallManager.shared.bindAccount(publicKey: ownKey)
|
|
|
|
// Scenario 3: outgoing with nothing
|
|
_ = CallManager.shared.startOutgoingCall(
|
|
toPublicKey: peerKey, title: "", username: ""
|
|
)
|
|
XCTAssertNotEqual(CallManager.shared.uiState.displayName, peerKey)
|
|
CallManager.shared.resetForSessionEnd()
|
|
CallManager.shared.bindAccount(publicKey: ownKey)
|
|
|
|
// Scenario 4: incoming via signal
|
|
let packet = PacketSignalPeer(
|
|
src: peerKey, dst: ownKey, sharedPublic: "",
|
|
signalType: .call, roomId: ""
|
|
)
|
|
CallManager.shared.testHandleSignalPacket(packet)
|
|
XCTAssertNotEqual(CallManager.shared.uiState.displayName, peerKey)
|
|
}
|
|
|
|
// MARK: - updateCallerName (CallKit update after hydration)
|
|
|
|
func testUpdateCallerNameIsAvailable() {
|
|
// Verify the method exists and doesn't crash with empty state
|
|
CallKitManager.shared.updateCallerName("") // should be a no-op
|
|
CallKitManager.shared.updateCallerName("Alice") // no UUID → no-op, but shouldn't crash
|
|
}
|
|
}
|