eleven / ocho Public

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

Plain git — no account needed to clone.

Download

Download this file

1package vault
2
3import (
4 "encoding/json"
5 "os"
6 "reflect"
7 "testing"
8)
9
10// TestVectors folds every event_sequence in test/event-vectors/vault.json and
11// compares the canonical result with expected_state. The same file drives the
12// web side (test/event-vectors.test.mjs, against the real vault.js reducer) —
13// the vectors are the spec; a failure here means Go and web have diverged, and
14// the fix goes in an implementation, never the vector.
15func TestVectors(t *testing.T) {
16 raw, err := os.ReadFile("../../test/event-vectors/vault.json")
17 if err != nil {
18 t.Fatalf("read vectors: %v", err)
19 }
20 var file struct {
21 Vectors []struct {
22 Name string `json:"name"`
23 EventSequence []Event `json:"event_sequence"`
24 ExpectedState any `json:"expected_state"`
25 } `json:"vectors"`
26 }
27 if err := json.Unmarshal(raw, &file); err != nil {
28 t.Fatalf("parse vectors: %v", err)
29 }
30 if len(file.Vectors) == 0 {
31 t.Fatal("no vectors found")
32 }
33 for _, v := range file.Vectors {
34 t.Run(v.Name, func(t *testing.T) {
35 got := canonical(FoldAll(v.EventSequence))
36 want := roundTrip(t, v.ExpectedState)
37 if !reflect.DeepEqual(got, want) {
38 gotJSON, _ := json.MarshalIndent(got, "", " ")
39 wantJSON, _ := json.MarshalIndent(want, "", " ")
40 t.Errorf("fold mismatch\n got: %s\nwant: %s", gotJSON, wantJSON)
41 }
42 })
43 }
44}
45
46// canonical renders a State in the vector file's canonical form (every field
47// present, "" / 0 defaults, [] not null) as generic JSON values, so it compares
48// exactly against the parsed expected_state.
49func canonical(s State) any {
50 items := make([]any, 0, len(s.Items))
51 for _, it := range s.Items {
52 items = append(items, map[string]any{
53 "id": it.ID, "name": it.Name, "value": it.Value, "sealed": it.Sealed, "kind": it.Kind,
54 "by": it.By, "byName": it.ByName, "at": float64(it.At),
55 "updatedBy": it.UpdatedBy, "updatedByName": it.UpdatedByName, "updatedAt": float64(it.UpdatedAt),
56 })
57 }
58 log := make([]any, 0, len(s.Log))
59 for _, l := range s.Log {
60 log = append(log, map[string]any{
61 "id": l.ID, "ref": l.Ref, "action": l.Action, "name": l.Name,
62 "by": l.By, "byName": l.ByName, "at": float64(l.At),
63 })
64 }
65 var keyring any
66 if s.Keyring != nil {
67 wraps := make([]any, 0, len(s.Keyring.Wraps))
68 for _, w := range s.Keyring.Wraps {
69 wraps = append(wraps, map[string]any{"id": w.ID, "kind": w.Kind, "label": w.Label, "uid": w.UID, "credId": w.CredID, "wrapped": w.Wrapped})
70 }
71 keyring = map[string]any{"v": float64(s.Keyring.V), "pub": s.Keyring.Pub, "wraps": wraps}
72 }
73 return map[string]any{"items": items, "log": log, "keyring": keyring}
74}
75
76// roundTrip normalises the vector's expected_state through the same canonical
77// shape (filling absent defaults) so sparse vectors compare cleanly.
78func roundTrip(t *testing.T, expected any) any {
79 t.Helper()
80 raw, err := json.Marshal(expected)
81 if err != nil {
82 t.Fatalf("marshal expected: %v", err)
83 }
84 var s State
85 if err := json.Unmarshal(raw, &s); err != nil {
86 t.Fatalf("unmarshal expected into State: %v", err)
87 }
88 return canonical(s)
89}
90
91// TestFoldNeverMutates pins the copy-on-write contract: callers may hold the
92// old state across a fold (the web keeps prior states for snapshots).
93func TestFoldNeverMutates(t *testing.T) {
94 s0 := FoldAll([]Event{
95 {"type": "vault.add", "id": "s1", "aid": "a1", "label": "WiFi", "value": "hunter2"},
96 })
97 s1 := Fold(s0, Event{"type": "vault.lock", "aid": "a2", "pub": "p",
98 "wrap": map[string]any{"id": "w1", "kind": "passkey", "wrapped": "x"},
99 "sealed": map[string]any{"s1": "sealed-blob"}})
100 if s0.Items[0].Value != "hunter2" || s0.Items[0].Sealed != "" {
101 t.Fatal("Fold mutated the prior state's item")
102 }
103 if s1.Items[0].Sealed != "sealed-blob" || s1.Items[0].Value != "" {
104 t.Fatal("lock did not seal the covered item")
105 }
106 s2 := Fold(s1, Event{"type": "vault.enroll", "aid": "a3",
107 "wrap": map[string]any{"id": "w2", "kind": "keychain", "wrapped": "y"}})
108 if len(s1.Keyring.Wraps) != 1 {
109 t.Fatal("enroll mutated the prior keyring")
110 }
111 if len(s2.Keyring.Wraps) != 2 {
112 t.Fatal("enroll did not add the wrap")
113 }
114}
115
116// TestFindItem pins the case-insensitive near-duplicate lookup.
117func TestFindItem(t *testing.T) {
118 s := FoldAll([]Event{{"type": "vault.add", "id": "s1", "aid": "a1", "label": "WiFi", "value": "v"}})
119 if it := s.FindItem(" wifi "); it == nil || it.ID != "s1" {
120 t.Fatalf("FindItem case-insensitive lookup failed: %+v", it)
121 }
122 if s.FindItem("nope") != nil {
123 t.Fatal("FindItem invented an item")
124 }
125}
126