eleven / ocho Public

Clone
git clone https://amadan.net/eleven/ocho

Plain git — no account needed to clone.

Download

Download this file

1import XCTest
2@testable import LChatCore
3
4// 🤖 The DEK-sync slice's pure halves (issue #1089): the release matching rule
5// (a fresh SiwA assertion's account must carry the very anchor the item was
6// written under) and the synced item's wire shape. The keychain and network
7// edges are deliberately thin and exercised by the device matrix, not here.
8final class DEKSyncTests: XCTestCase {
9 private let item = DEKSync.Item(v: 1, dekB64: "AAAA", siwaAnchor: "hash-1",
10 credentialID: "cred-1")
11 private func anchor(_ kind: String, _ id: String) -> HomeClient.AnchorEntry {
12 HomeClient.AnchorEntry(kind: kind, id: id)
13 }
14
15 // ---- the release matching rule ------------------------------------------
16
17 func testReleasableWhenAccountCarriesTheAnchor() {
18 XCTAssertTrue(DEKSync.releasable(item, anchors: [
19 anchor("passkey", "pk-1"), anchor("siwa", "hash-1"),
20 ]))
21 }
22
23 func testReleasableAcceptsAnyOfSeveralSiwaAnchors() {
24 // A combined account may hold two Apple anchors; the item matches the
25 // one it was written under regardless of which Apple ID asserted.
26 XCTAssertTrue(DEKSync.releasable(item, anchors: [
27 anchor("siwa", "hash-other"), anchor("siwa", "hash-1"),
28 ]))
29 }
30
31 func testNotReleasableForADifferentAccount() {
32 // The family-iPad shape: the assertion resolved someone else's (or a
33 // brand-new) account — its anchors don't include the item's.
34 XCTAssertFalse(DEKSync.releasable(item, anchors: [
35 anchor("siwa", "hash-other"),
36 ]))
37 }
38
39 func testNotReleasableWhenTheAnchorWasRemoved() {
40 XCTAssertFalse(DEKSync.releasable(item, anchors: [
41 anchor("passkey", "pk-1"), anchor("email", "hash-1"),
42 ]))
43 }
44
45 func testNotReleasableAgainstAnEmptyAccount() {
46 XCTAssertFalse(DEKSync.releasable(item, anchors: []))
47 }
48
49 func testMatchRequiresTheSiwaKind() {
50 // Same id under a different kind must not satisfy the gate — the rule
51 // is "the SiwA anchor", not "any anchor with that hash".
52 XCTAssertFalse(DEKSync.releasable(item, anchors: [
53 anchor("email", "hash-1"), anchor("google", "hash-1"),
54 ]))
55 }
56
57 // ---- the item's wire shape ----------------------------------------------
58
59 func testItemRoundTrips() throws {
60 let data = try JSONEncoder().encode(item)
61 XCTAssertEqual(try JSONDecoder().decode(DEKSync.Item.self, from: data), item)
62 }
63
64 func testItemToleratesUnknownFieldsFromNewerBuilds() throws {
65 // A newer build may extend the synced item; older builds must still
66 // read the fields they know (the vault blob's same forward rule).
67 let json = #"{"v":2,"dekB64":"BBBB","siwaAnchor":"h","credentialID":"c","future":true}"#
68 let decoded = try JSONDecoder().decode(DEKSync.Item.self, from: Data(json.utf8))
69 XCTAssertEqual(decoded.dekB64, "BBBB")
70 XCTAssertEqual(decoded.siwaAnchor, "h")
71 }
72
73 // ---- the anchors listing decode -----------------------------------------
74
75 func testAnchorEntryDecodesTheServerShape() throws {
76 // home/anchors.go rows: {kind, id, created_at} — created_at ignored.
77 let json = #"{"anchors":[{"kind":"siwa","id":"hash-1","created_at":1752882000}]}"#
78 struct R: Decodable { let anchors: [HomeClient.AnchorEntry] }
79 let r = try JSONDecoder().decode(R.self, from: Data(json.utf8))
80 XCTAssertEqual(r.anchors.first?.kind, "siwa")
81 XCTAssertEqual(r.anchors.first?.id, "hash-1")
82 }
83}
84