eleven / messenger Public

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

Plain git — no account needed to clone.

Download

Download this file

1package main
2
3import (
4 "encoding/json"
5 "sync"
6 "testing"
7)
8
9// changelogClean mirrors web/static/format.js changelogParagraphs β€” these cases
10// are shared with test/format.test.mjs so the two sides can't drift.
11func TestChangelogClean(t *testing.T) {
12 cases := []struct{ name, in, want string }{
13 {"drops the πŸ€– authorship marker", "πŸ€– Unread dots now sync across your devices.", "Unread dots now sync across your devices."},
14 {"drops a person marker", "πŸ‘¨ Hand-written note.", "Hand-written note."},
15 {
16 "collapses hard wraps but keeps paragraph breaks",
17 "πŸ€– Your space can now keep itself up\nto date. Admins get a new\nUpdates section.\n\nUpdates take seconds.",
18 "Your space can now keep itself up to date. Admins get a new Updates section.\n\nUpdates take seconds.",
19 },
20 {"no marker passes through", "Plain note, no marker.", "Plain note, no marker."},
21 {"blank collapses to empty", " ", ""},
22 }
23 for _, c := range cases {
24 if got := changelogClean(c.in); got != c.want {
25 t.Errorf("%s: got %q, want %q", c.name, got, c.want)
26 }
27 }
28}
29
30func TestAnnounceChangelog(t *testing.T) {
31 _, s := newTestServer(t)
32 if len(s.changelog) < 2 {
33 t.Fatal("embedded changelog unexpectedly small")
34 }
35
36 // First run: no watermark β†’ baseline silently, announce nothing.
37 fresh, sent := s.announceChangelog()
38 if fresh != 0 || sent != 0 {
39 t.Fatalf("first run should baseline: fresh=%d sent=%d", fresh, sent)
40 }
41 raw, ok, _ := s.db.getConfig(changelogAnnouncedKey)
42 if !ok {
43 t.Fatal("baseline did not write the watermark")
44 }
45 var ids []string
46 if err := json.Unmarshal([]byte(raw), &ids); err != nil || len(ids) != len(s.changelog) {
47 t.Fatalf("watermark should hold every entry id: %d != %d (%v)", len(ids), len(s.changelog), err)
48 }
49
50 // Steady state: nothing new β†’ nothing announced.
51 if fresh, _ = s.announceChangelog(); fresh != 0 {
52 t.Fatalf("no new entries but fresh=%d", fresh)
53 }
54
55 // A "deploy": the stored watermark misses the two newest entries (as if this
56 // binary shipped them). They announce, and the watermark catches up.
57 trimmed, _ := json.Marshal(ids[:len(ids)-2])
58 if err := s.db.setConfig(changelogAnnouncedKey, string(trimmed)); err != nil {
59 t.Fatal(err)
60 }
61 fresh, sent = s.announceChangelog()
62 if fresh != 2 {
63 t.Fatalf("expected 2 fresh entries, got %d", fresh)
64 }
65 if sent != 0 { // no push subscriptions in the test DB
66 t.Fatalf("expected 0 sends without subscriptions, got %d", sent)
67 }
68 raw, _, _ = s.db.getConfig(changelogAnnouncedKey)
69 var after []string
70 _ = json.Unmarshal([]byte(raw), &after)
71 if len(after) != len(s.changelog) {
72 t.Fatalf("watermark should catch up: %d != %d", len(after), len(s.changelog))
73 }
74
75 // A corrupt watermark re-baselines rather than announcing the backlog.
76 _ = s.db.setConfig(changelogAnnouncedKey, "not-json")
77 if fresh, _ = s.announceChangelog(); fresh != 0 {
78 t.Fatalf("corrupt watermark should re-baseline, got fresh=%d", fresh)
79 }
80}
81
82func TestAdminUserIDs(t *testing.T) {
83 _, s := newTestServer(t)
84 a, _ := s.db.createUser("Ada")
85 b, _ := s.db.createUser("Bob")
86 if err := s.db.setUserRole(a, "admin"); err != nil {
87 t.Fatal(err)
88 }
89 admins, err := s.db.adminUserIDs()
90 if err != nil {
91 t.Fatal(err)
92 }
93 if len(admins) != 1 || admins[0] != a {
94 t.Fatalf("want [%s], got %v", a, admins)
95 }
96 _ = b // stays a member β€” must not appear
97}
98
99// stubAPNs records sendAlert calls so apnsAnnounce's admin filtering is
100// observable without any network.
101type stubAPNs struct {
102 mu sync.Mutex
103 alerts []string // "apnsToken|title|body"
104}
105
106func (s *stubAPNs) send(string, bool, string, string, int64, int64) error { return nil }
107func (s *stubAPNs) sendAlert(deviceToken string, _ bool, title, body string) error {
108 s.mu.Lock()
109 defer s.mu.Unlock()
110 s.alerts = append(s.alerts, deviceToken+"|"+title+"|"+body)
111 return nil
112}
113
114func TestAPNsAnnounceReachesOnlyAdmins(t *testing.T) {
115 _, s := newTestServer(t)
116 stub := &stubAPNs{}
117 s.apns = stub
118
119 adminTok := mintDeviceToken(t, s.db, "Ada")
120 memberTok := mintDeviceToken(t, s.db, "Bob")
121 adminUID, _ := s.db.deviceTokenUser(adminTok)
122 if err := s.db.setUserRole(adminUID, "admin"); err != nil {
123 t.Fatal(err)
124 }
125 if err := s.db.addAPNsToken(APNsToken{APNsToken: "apns-ada", Token: adminTok}); err != nil {
126 t.Fatal(err)
127 }
128 if err := s.db.addAPNsToken(APNsToken{APNsToken: "apns-bob", Token: memberTok}); err != nil {
129 t.Fatal(err)
130 }
131
132 sent := s.apnsAnnounce("What's new", "Notes.", map[string]bool{adminUID: true})
133 if sent != 1 {
134 t.Fatalf("sent=%d, want 1 (admin only)", sent)
135 }
136 if len(stub.alerts) != 1 || stub.alerts[0] != "apns-ada|What's new|Notes." {
137 t.Fatalf("unexpected alerts: %v", stub.alerts)
138 }
139}
140