p2p-swift / Tests / IrdP2PTests / RoomHandshakeTests.swift
485 words
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
import Foundation
import Testing
@testable import IrdP2P
@testable import IrdP2PSignaling
import IrdP2PCrypto

@Suite struct RoomHandshakeTests {
    @Test func twoPeersVerifyEachOtherAndGoDirect() async throws {
        let h = Harness()
        let (a, ca) = try await h.join(peer: lowPeer)
        #expect(a.roomId == RoomCrypto.roomId(secret: h.secret, appSalt: h.appSalt))
        #expect(await ca.waitFor(.status(.joined)))
        #expect(await ca.waitFor(.roster([])))
        let (b, cb) = try await h.join(peer: highPeer)
        #expect(await cb.waitFor(.roster([])))              // b's roster settles before a is verified
        #expect(await ca.waitFor(.peerJoined(highPeer)))
        #expect(await ca.waitFor(.peerReady(highPeer, .relayed)))
        #expect(await cb.waitFor(.peerJoined(lowPeer)))
        #expect(await cb.waitFor(.peerReady(lowPeer, .relayed)))
        #expect(await ca.waitFor(.peerReady(highPeer, .direct)))
        #expect(await cb.waitFor(.peerReady(lowPeer, .direct)))
        #expect(await a.members == [highPeer])
        #expect(await b.transport(of: lowPeer) == .direct)
        // no identity announced: no identity events
        #expect(!ca.all.contains { if case .peerIdentity = $0 { return true } else { return false } })
        await b.leave()
        #expect(await ca.waitFor(.peerLeft(highPeer)))
        #expect(await cb.waitFor(.status(.closed(reason: "left"))))
        await a.leave()
    }

    @Test func identityIsVerifiedAndAnnounced() async throws {
        let h = Harness()
        var ida = Identity.generate()
        ida.name = "Ana"; ida.hue = 200; ida.glyph = "🐢"
        let (a, ca) = try await h.join(peer: lowPeer, identity: ida, preferDataChannel: false)
        let (b, cb) = try await h.join(peer: highPeer, identity: nil, preferDataChannel: false)
        let expected = PeerIdentity(publicKeyBase64URL: ida.publicKeyBase64URL, fingerprint: ida.fingerprint, name: "Ana", hue: 200, glyph: "🐢")
        #expect(await cb.waitFor(.peerIdentity(lowPeer, expected)))
        #expect(await b.identity(of: lowPeer) == expected)
        #expect(await ca.waitFor(.peerJoined(highPeer)))
        #expect(await a.identity(of: highPeer) == nil)
        await a.leave(); await b.leave()
    }

    @Test func strangersInTheRoomAreIgnored() async throws {
        let h = Harness()
        let (a, ca) = try await h.join(peer: lowPeer, preferDataChannel: false)
        #expect(await ca.waitFor(.roster([])))
        // someone who knows the room id but not the secret joins the relay room and talks garbage
        var cfg = RelaySession.Configuration(url: URL(string: "wss://x/ws")!, origin: nil, room: a.roomId, peer: thirdPeer, limits: .defaults)
        cfg.pacing = false
        let intruder = RelaySession(configuration: cfg, transport: h.relay, clock: h.clock)
        try await intruder.start()
        try await intruder.send(signal: .object(["v": .int(1), "iv": .string("AAAAAAAAAAAAAAAA"), "ct": .string("AAAAAAAAAAAAAAAAAAAAAAAAAAAA")]), to: lowPeer)
        try await intruder.send(signal: .string("junk"), to: lowPeer)
        // a genuine peer still works
        let (b, cb) = try await h.join(peer: highPeer, preferDataChannel: false)
        #expect(await cb.waitFor(.peerJoined(lowPeer)))
        #expect(await ca.waitFor(.peerJoined(highPeer)))
        #expect(await a.members == [highPeer])
        #expect(!ca.contains(.peerJoined(thirdPeer)))
        await intruder.leave()
        #expect(!ca.contains(.peerLeft(thirdPeer)))
        await a.leave(); await b.leave()
    }

    @Test func wrongSecretNeverMeets() async throws {
        let h = Harness()
        let (a, ca) = try await h.join(peer: lowPeer, preferDataChannel: false)
        let (b, cb) = try await h.join(peer: highPeer, preferDataChannel: false, secret: .random())
        #expect(await ca.waitFor(.roster([])))
        #expect(await cb.waitFor(.roster([])))
        #expect(a.roomId != b.roomId)
        await a.leave(); await b.leave()
    }

    @Test func silentPeerBecomesAStrangerAfterHelloTimeout() async throws {
        let h = Harness()
        let (a, ca) = try await h.join(peer: lowPeer, preferDataChannel: false)
        #expect(await ca.waitFor(.roster([])))
        var cfg = RelaySession.Configuration(url: URL(string: "wss://x/ws")!, origin: nil, room: a.roomId, peer: highPeer, limits: .defaults)
        cfg.pacing = false
        let silent = RelaySession(configuration: cfg, transport: h.relay, clock: h.clock)
        try await silent.start()
        #expect(await eventuallyTrue { h.relay.signalFrameCount >= 1 })   // a sent its hello
        h.clock.advance(by: .seconds(11))
        try? await Task.sleep(for: .milliseconds(50))
        #expect(await a.members.isEmpty)
        #expect(!ca.contains(.peerJoined(highPeer)))
        await silent.leave(); await a.leave()
    }
}

func eventuallyTrue(timeout: Duration = .seconds(3), _ condition: @Sendable () async -> Bool) async -> Bool {
    let deadline = ContinuousClock.now + timeout
    while ContinuousClock.now < deadline {
        if await condition() { return true }
        try? await Task.sleep(for: .milliseconds(5))
    }
    return await condition()
}

static mirror of HEAD · about · clone: git clone https://ird.cod-sursa.ro/p2p-swift.git