Темизация: adaptive цвета чата, context menu, attachment picker, auth + instant отклик DarkMode кнопки

This commit is contained in:
2026-04-05 12:16:24 +05:00
parent a72a1bd6c9
commit d65624ad35
21 changed files with 803 additions and 159 deletions

View File

@@ -2,12 +2,23 @@ import XCTest
@testable import Rosetta
final class CallPacketParityTests: XCTestCase {
func testSignalPeerRoundTripForCallKeyExchangeAndCreateRoom() throws {
func testSignalPeerRoundTripForLegacyCallAcceptEndCallAndKeyExchange() throws {
let call = PacketSignalPeer(
src: "02caller",
dst: "02callee",
sharedPublic: "",
signalType: .call,
callId: "call-123",
joinToken: "join-123",
roomId: ""
)
let accept = PacketSignalPeer(
src: "02callee",
dst: "02caller",
sharedPublic: "",
signalType: .accept,
callId: "call-123",
joinToken: "join-123",
roomId: ""
)
let keyExchange = PacketSignalPeer(
@@ -15,14 +26,18 @@ final class CallPacketParityTests: XCTestCase {
dst: "02caller",
sharedPublic: "abcdef012345",
signalType: .keyExchange,
callId: "",
joinToken: "",
roomId: ""
)
let createRoom = PacketSignalPeer(
let endCall = PacketSignalPeer(
src: "02caller",
dst: "02callee",
sharedPublic: "",
signalType: .createRoom,
roomId: "room-42"
signalType: .endCall,
callId: "call-123",
joinToken: "join-123",
roomId: ""
)
let decodedCall = try decodeSignal(call)
@@ -30,29 +45,67 @@ final class CallPacketParityTests: XCTestCase {
XCTAssertEqual(decodedCall.src, "02caller")
XCTAssertEqual(decodedCall.dst, "02callee")
XCTAssertEqual(decodedCall.sharedPublic, "")
XCTAssertEqual(decodedCall.callId, "call-123")
XCTAssertEqual(decodedCall.joinToken, "join-123")
XCTAssertEqual(decodedCall.roomId, "")
let decodedAccept = try decodeSignal(accept)
XCTAssertEqual(decodedAccept.signalType, .accept)
XCTAssertEqual(decodedAccept.callId, "call-123")
XCTAssertEqual(decodedAccept.joinToken, "join-123")
let decodedKeyExchange = try decodeSignal(keyExchange)
XCTAssertEqual(decodedKeyExchange.signalType, .keyExchange)
XCTAssertEqual(decodedKeyExchange.src, "02callee")
XCTAssertEqual(decodedKeyExchange.dst, "02caller")
XCTAssertEqual(decodedKeyExchange.sharedPublic, "abcdef012345")
XCTAssertEqual(decodedKeyExchange.callId, "")
XCTAssertEqual(decodedKeyExchange.joinToken, "")
XCTAssertEqual(decodedKeyExchange.roomId, "")
let decodedEndCall = try decodeSignal(endCall)
XCTAssertEqual(decodedEndCall.signalType, .endCall)
XCTAssertEqual(decodedEndCall.callId, "call-123")
XCTAssertEqual(decodedEndCall.joinToken, "join-123")
}
func testSignalCodeFourRoundTripForLegacyActiveAndCreateRoomFallback() throws {
let legacyActive = PacketSignalPeer(
src: "02caller",
dst: "02callee",
sharedPublic: "",
signalType: .createRoom,
callId: "",
joinToken: "",
roomId: ""
)
let createRoom = PacketSignalPeer(
src: "02caller",
dst: "02callee",
sharedPublic: "",
signalType: .createRoom,
callId: "",
joinToken: "",
roomId: "room-42"
)
let decodedLegacyActive = try decodeSignal(legacyActive)
XCTAssertEqual(decodedLegacyActive.signalType, .createRoom)
XCTAssertEqual(decodedLegacyActive.roomId, "")
let decodedCreateRoom = try decodeSignal(createRoom)
XCTAssertEqual(decodedCreateRoom.signalType, .createRoom)
XCTAssertEqual(decodedCreateRoom.src, "02caller")
XCTAssertEqual(decodedCreateRoom.dst, "02callee")
XCTAssertEqual(decodedCreateRoom.sharedPublic, "")
XCTAssertEqual(decodedCreateRoom.roomId, "room-42")
}
func testSignalPeerRoundTripForBusyAndPeerDisconnectedShortFormat() throws {
func testSignalPeerRoundTripForBusyPeerDisconnectedAndRingingTimeoutShortFormat() throws {
let busy = PacketSignalPeer(
src: "02should-not-be-sent",
dst: "02should-not-be-sent",
sharedPublic: "ignored",
signalType: .endCallBecauseBusy,
callId: "ignored",
joinToken: "ignored",
roomId: "ignored-room"
)
let disconnected = PacketSignalPeer(
@@ -60,6 +113,17 @@ final class CallPacketParityTests: XCTestCase {
dst: "02should-not-be-sent",
sharedPublic: "ignored",
signalType: .endCallBecausePeerDisconnected,
callId: "ignored",
joinToken: "ignored",
roomId: "ignored-room"
)
let ringingTimeout = PacketSignalPeer(
src: "02should-not-be-sent",
dst: "02should-not-be-sent",
sharedPublic: "ignored",
signalType: .ringingTimeout,
callId: "ignored",
joinToken: "ignored",
roomId: "ignored-room"
)
@@ -68,6 +132,8 @@ final class CallPacketParityTests: XCTestCase {
XCTAssertEqual(decodedBusy.src, "")
XCTAssertEqual(decodedBusy.dst, "")
XCTAssertEqual(decodedBusy.sharedPublic, "")
XCTAssertEqual(decodedBusy.callId, "")
XCTAssertEqual(decodedBusy.joinToken, "")
XCTAssertEqual(decodedBusy.roomId, "")
let decodedDisconnected = try decodeSignal(disconnected)
@@ -75,7 +141,18 @@ final class CallPacketParityTests: XCTestCase {
XCTAssertEqual(decodedDisconnected.src, "")
XCTAssertEqual(decodedDisconnected.dst, "")
XCTAssertEqual(decodedDisconnected.sharedPublic, "")
XCTAssertEqual(decodedDisconnected.callId, "")
XCTAssertEqual(decodedDisconnected.joinToken, "")
XCTAssertEqual(decodedDisconnected.roomId, "")
let decodedTimeout = try decodeSignal(ringingTimeout)
XCTAssertEqual(decodedTimeout.signalType, .ringingTimeout)
XCTAssertEqual(decodedTimeout.src, "")
XCTAssertEqual(decodedTimeout.dst, "")
XCTAssertEqual(decodedTimeout.sharedPublic, "")
XCTAssertEqual(decodedTimeout.callId, "")
XCTAssertEqual(decodedTimeout.joinToken, "")
XCTAssertEqual(decodedTimeout.roomId, "")
}
func testWebRtcRoundTripForOfferAnswerAndIceCandidate() throws {

View File

@@ -97,12 +97,14 @@ struct SignalPeerCallFlowTests {
let caller = "02a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"
let callee = "03f0e1d2c3b4a5968778695a4b3c2d1e0f9e8d7c6b5a49382716051a2b3c4d5e6f"
let packet = PacketSignalPeer(src: caller, dst: callee, sharedPublic: "",
signalType: .call, roomId: "")
signalType: .call, callId: "call-1", joinToken: "join-1", roomId: "")
let decoded = try decode(packet)
#expect(decoded.signalType == .call)
#expect(decoded.src == caller)
#expect(decoded.dst == callee)
#expect(decoded.sharedPublic == "")
#expect(decoded.callId == "call-1")
#expect(decoded.joinToken == "join-1")
#expect(decoded.roomId == "")
}
@@ -153,6 +155,19 @@ struct SignalPeerCallFlowTests {
#expect(decoded.signalType == .endCallBecausePeerDisconnected)
}
@Test("RINGING_TIMEOUT short format — 3 bytes wire size")
func ringingTimeoutShortFormat() throws {
let packet = PacketSignalPeer(src: "ignored", dst: "ignored", sharedPublic: "ignored",
signalType: .ringingTimeout, roomId: "ignored")
let data = PacketRegistry.encode(packet)
#expect(data.count == 3)
let decoded = try decode(packet)
#expect(decoded.signalType == .ringingTimeout)
#expect(decoded.src == "")
#expect(decoded.dst == "")
}
private func decode(_ packet: PacketSignalPeer) throws -> PacketSignalPeer {
let data = PacketRegistry.encode(packet)
guard let result = PacketRegistry.decode(from: data),
@@ -171,7 +186,8 @@ struct CallPushEnumParityTests {
arguments: [
(SignalType.call, 0), (SignalType.keyExchange, 1), (SignalType.activeCall, 2),
(SignalType.endCall, 3), (SignalType.createRoom, 4),
(SignalType.endCallBecausePeerDisconnected, 5), (SignalType.endCallBecauseBusy, 6)
(SignalType.endCallBecausePeerDisconnected, 5), (SignalType.endCallBecauseBusy, 6),
(SignalType.accept, 7), (SignalType.ringingTimeout, 8)
])
func signalTypeEnumValues(pair: (SignalType, Int)) {
#expect(pair.0.rawValue == pair.1)
@@ -222,12 +238,12 @@ struct CallPushWireFormatTests {
#expect(data[14] == 0x00); #expect(data[15] == 0x42)
}
@Test("SignalPeer call byte layout: signalType→src→dst")
@Test("SignalPeer call byte layout: signalType→src→dst→callId→joinToken")
func signalPeerCallByteLayout() {
let packet = PacketSignalPeer(src: "S", dst: "D", sharedPublic: "",
signalType: .call, roomId: "")
let data = PacketRegistry.encode(packet)
#expect(data.count == 15)
#expect(data.count == 23)
// packetId = 0x001A
#expect(data[0] == 0x00); #expect(data[1] == 0x1A)
@@ -241,6 +257,12 @@ struct CallPushWireFormatTests {
#expect(data[9] == 0x00); #expect(data[10] == 0x00)
#expect(data[11] == 0x00); #expect(data[12] == 0x01)
#expect(data[13] == 0x00); #expect(data[14] == 0x44)
// callId "": length=0
#expect(data[15] == 0x00); #expect(data[16] == 0x00)
#expect(data[17] == 0x00); #expect(data[18] == 0x00)
// joinToken "": length=0
#expect(data[19] == 0x00); #expect(data[20] == 0x00)
#expect(data[21] == 0x00); #expect(data[22] == 0x00)
}
}

View File

@@ -1,3 +1,4 @@
import CryptoKit
import XCTest
@testable import Rosetta
@@ -101,4 +102,127 @@ final class CallRoutingTests: XCTestCase {
XCTAssertEqual(CallManager.shared.uiState.peerPublicKey, peerA)
XCTAssertEqual(CallManager.shared.uiState.statusText, "Calling...")
}
func testLegacyOutgoingFlowCallAcceptKeyExchangeActive() {
let start = CallManager.shared.startOutgoingCall(
toPublicKey: peerA,
title: "Peer A",
username: "peer_a"
)
XCTAssertEqual(start, .started)
let accept = PacketSignalPeer(
src: peerA,
dst: ownKey,
sharedPublic: "",
signalType: .accept,
callId: "call-legacy-1",
joinToken: "join-legacy-1",
roomId: ""
)
CallManager.shared.testHandleSignalPacket(accept)
XCTAssertEqual(CallManager.shared.signalingMode, .legacy)
XCTAssertEqual(CallManager.shared.callId, "call-legacy-1")
XCTAssertEqual(CallManager.shared.joinToken, "join-legacy-1")
XCTAssertEqual(CallManager.shared.uiState.phase, .keyExchange)
let keyExchange = PacketSignalPeer(
src: peerA,
dst: ownKey,
sharedPublic: makePeerPublicHex(),
signalType: .keyExchange,
roomId: ""
)
CallManager.shared.testHandleSignalPacket(keyExchange)
XCTAssertEqual(CallManager.shared.uiState.phase, .keyExchange)
// Legacy ACTIVE arrives as signal code 4 with empty roomId.
let active = PacketSignalPeer(
src: peerA,
dst: ownKey,
sharedPublic: "",
signalType: .createRoom,
roomId: ""
)
CallManager.shared.testHandleSignalPacket(active)
XCTAssertEqual(CallManager.shared.uiState.phase, .webRtcExchange)
}
func testCreateRoomFallbackOutgoingFlowKeyExchangeBeforeAccept() {
let start = CallManager.shared.startOutgoingCall(
toPublicKey: peerA,
title: "Peer A",
username: "peer_a"
)
XCTAssertEqual(start, .started)
let keyExchange = PacketSignalPeer(
src: peerA,
dst: ownKey,
sharedPublic: makePeerPublicHex(),
signalType: .keyExchange,
roomId: ""
)
CallManager.shared.testHandleSignalPacket(keyExchange)
XCTAssertEqual(CallManager.shared.signalingMode, .createRoom)
XCTAssertEqual(CallManager.shared.uiState.phase, .webRtcExchange)
}
func testDeferredAcceptCompletesWhenMetadataArrivesLater() {
CallManager.shared.setupIncomingCallFromPush(
callerKey: peerA,
callerName: "Peer A"
)
XCTAssertEqual(CallManager.shared.uiState.phase, .incoming)
let acceptResult = CallManager.shared.acceptIncomingCall()
XCTAssertEqual(acceptResult, .started)
XCTAssertTrue(CallManager.shared.pendingIncomingAccept)
XCTAssertEqual(CallManager.shared.uiState.phase, .keyExchange)
XCTAssertEqual(CallManager.shared.signalingMode, .undecided)
let delayedCall = PacketSignalPeer(
src: peerA,
dst: ownKey,
sharedPublic: "",
signalType: .call,
callId: "call-delayed-1",
joinToken: "join-delayed-1",
roomId: ""
)
CallManager.shared.testHandleSignalPacket(delayedCall)
XCTAssertEqual(CallManager.shared.signalingMode, .legacy)
XCTAssertFalse(CallManager.shared.pendingIncomingAccept)
XCTAssertEqual(CallManager.shared.callId, "call-delayed-1")
XCTAssertEqual(CallManager.shared.joinToken, "join-delayed-1")
XCTAssertEqual(CallManager.shared.uiState.phase, .keyExchange)
}
func testRingingTimeoutSignalTearsDownCall() {
let start = CallManager.shared.startOutgoingCall(
toPublicKey: peerA,
title: "Peer A",
username: "peer_a"
)
XCTAssertEqual(start, .started)
let timeoutPacket = PacketSignalPeer(
src: "",
dst: "",
sharedPublic: "",
signalType: .ringingTimeout,
roomId: ""
)
CallManager.shared.testHandleSignalPacket(timeoutPacket)
XCTAssertEqual(CallManager.shared.uiState.phase, .idle)
XCTAssertEqual(CallManager.shared.uiState.statusText, "No answer")
}
private func makePeerPublicHex() -> String {
Curve25519.KeyAgreement.PrivateKey().publicKey.rawRepresentation.hexString
}
}