crossmate

A collaborative crossword app for iOS
Log | Files | Refs | LICENSE

FriendZoneTests.swift (5411B)


      1 import Foundation
      2 import Testing
      3 
      4 @testable import Crossmate
      5 
      6 @Suite("FriendZone")
      7 struct FriendZoneTests {
      8 
      9     @Test("pairKey is symmetric in its arguments")
     10     func pairKeySymmetric() {
     11         let a = "_alice0000000000000000000000000000"
     12         let b = "_bob00000000000000000000000000000000"
     13         #expect(FriendZone.pairKey(a, b) == FriendZone.pairKey(b, a))
     14     }
     15 
     16     @Test("pairKey is deterministic across calls")
     17     func pairKeyDeterministic() {
     18         let a = "_alice"
     19         let b = "_bob"
     20         #expect(FriendZone.pairKey(a, b) == FriendZone.pairKey(a, b))
     21     }
     22 
     23     @Test("pairKey distinguishes different pairs")
     24     func pairKeyDistinct() {
     25         #expect(FriendZone.pairKey("_a", "_b") != FriendZone.pairKey("_a", "_c"))
     26     }
     27 
     28     @Test("pairKey is 64 lowercase hex characters")
     29     func pairKeyShape() {
     30         let key = FriendZone.pairKey("_a", "_b")
     31         #expect(key.count == 64)
     32         #expect(key.allSatisfy { "0123456789abcdef".contains($0) })
     33     }
     34 
     35     @Test("zoneName carries the prefix and is recognised as a friend zone")
     36     func zoneNameRoundTrip() {
     37         let key = FriendZone.pairKey("_a", "_b")
     38         let name = FriendZone.zoneName(pairKey: key)
     39         #expect(name == "friend-\(key)")
     40         #expect(FriendZone.isFriendZone(name))
     41         #expect(!FriendZone.isFriendZone("game-\(UUID().uuidString)"))
     42         #expect(!FriendZone.isFriendZone("account"))
     43     }
     44 
     45     @Test("owner election is deterministic, asymmetric, and excludes self")
     46     func ownerElection() {
     47         let small = "_aaa"
     48         let large = "_zzz"
     49         #expect(FriendZone.isOwner(localAuthorID: small, remoteAuthorID: large))
     50         #expect(!FriendZone.isOwner(localAuthorID: large, remoteAuthorID: small))
     51         // Exactly one side owns.
     52         #expect(
     53             FriendZone.isOwner(localAuthorID: small, remoteAuthorID: large)
     54                 != FriendZone.isOwner(localAuthorID: large, remoteAuthorID: small)
     55         )
     56         // Same user can never be a friend.
     57         #expect(!FriendZone.isOwner(localAuthorID: small, remoteAuthorID: small))
     58     }
     59 
     60     @Test("BootstrapPayload round-trips through its string encoding")
     61     func bootstrapPayloadRoundTrip() {
     62         let payload = FriendZone.BootstrapPayload(
     63             friendShareURL: "https://www.icloud.com/share/abc#xyz",
     64             pairKey: FriendZone.pairKey("_a", "_b"),
     65             ownerAuthorID: "_a"
     66         )
     67         let encoded = payload.encodedString()
     68         #expect(encoded != nil)
     69         #expect(FriendZone.BootstrapPayload.decode(encoded) == payload)
     70     }
     71 
     72     @Test("Bootstrap acceptor must be the other member of the pair")
     73     func bootstrapAcceptorMustBePairMember() {
     74         let payload = FriendZone.BootstrapPayload(
     75             friendShareURL: "https://www.icloud.com/share/abc#xyz",
     76             pairKey: FriendZone.pairKey("_alice", "_bob"),
     77             ownerAuthorID: "_alice"
     78         )
     79 
     80         #expect(FriendZone.canAcceptBootstrap(payload, localAuthorID: "_bob"))
     81         #expect(!FriendZone.canAcceptBootstrap(payload, localAuthorID: "_carol"))
     82         #expect(!FriendZone.canAcceptBootstrap(payload, localAuthorID: "_alice"))
     83         #expect(!FriendZone.canAcceptBootstrap(payload, localAuthorID: nil))
     84         #expect(!FriendZone.canAcceptBootstrap(payload, localAuthorID: ""))
     85     }
     86 
     87     @Test("BootstrapPayload.decode tolerates nil and malformed input")
     88     func bootstrapPayloadDecodeTolerant() {
     89         #expect(FriendZone.BootstrapPayload.decode(nil) == nil)
     90         #expect(FriendZone.BootstrapPayload.decode("") == nil)
     91         #expect(FriendZone.BootstrapPayload.decode("not json") == nil)
     92         #expect(FriendZone.BootstrapPayload.decode(#"{"friendShareURL":"x"}"#) == nil)
     93     }
     94 
     95     @Test("InvitePayload round-trips through its string encoding")
     96     func invitePayloadRoundTrip() {
     97         let payload = FriendZone.InvitePayload(
     98             gameShareURL: "https://www.icloud.com/share/xyz#abc"
     99         )
    100         let encoded = payload.encodedString()
    101         #expect(encoded != nil)
    102         #expect(FriendZone.InvitePayload.decode(encoded) == payload)
    103     }
    104 
    105     @Test("InvitePayload round-trips a grid silhouette segment")
    106     func invitePayloadRoundTripWithSilhouette() {
    107         let payload = FriendZone.InvitePayload(
    108             gameShareURL: "https://www.icloud.com/share/xyz#abc",
    109             gridSilhouette: "sf//AA"
    110         )
    111         let decoded = FriendZone.InvitePayload.decode(payload.encodedString())
    112         #expect(decoded == payload)
    113         #expect(decoded?.gridSilhouette == "sf//AA")
    114     }
    115 
    116     @Test("InvitePayload from an older sender decodes with a nil silhouette")
    117     func invitePayloadDecodesLegacyWithoutSilhouette() {
    118         // Payloads written before the silhouette field omit the key entirely.
    119         let legacy = #"{"gameShareURL":"https://www.icloud.com/share/xyz"}"#
    120         let decoded = FriendZone.InvitePayload.decode(legacy)
    121         #expect(decoded?.gameShareURL == "https://www.icloud.com/share/xyz")
    122         #expect(decoded?.gridSilhouette == nil)
    123     }
    124 
    125     @Test("InvitePayload.decode tolerates nil and malformed input")
    126     func invitePayloadDecodeTolerant() {
    127         #expect(FriendZone.InvitePayload.decode(nil) == nil)
    128         #expect(FriendZone.InvitePayload.decode("") == nil)
    129         #expect(FriendZone.InvitePayload.decode("garbage") == nil)
    130         #expect(FriendZone.InvitePayload.decode(#"{"x":1}"#) == nil)
    131     }
    132 }