oficina / sheets Public

Clone
git clone https://amadan.net/oficina/sheets

Plain git — no account needed to clone.

Download

Download this file

1package sheets
2
3import "testing"
4
5func TestMemberForIsIdempotent(t *testing.T) {
6 app := newTestApp(t)
7 a, err := app.memberFor("Ada@example.com")
8 if err != nil {
9 t.Fatalf("memberFor: %v", err)
10 }
11 if a.ID == 0 {
12 t.Fatal("memberFor returned id 0 — the uid-0 trap AGENTS.md names")
13 }
14 b, err := app.memberFor("ada@example.com")
15 if err != nil {
16 t.Fatalf("memberFor again: %v", err)
17 }
18 if b.ID != a.ID {
19 t.Fatalf("the same person got two ids (%d then %d): the address "+
20 "comparison is case-sensitive, but the allowlist's is not", a.ID, b.ID)
21 }
22}
23
24func TestMemberForSurroundingSpaceIsTheSamePerson(t *testing.T) {
25 app := newTestApp(t)
26 a, err := app.memberFor("ada@example.com")
27 if err != nil {
28 t.Fatalf("memberFor: %v", err)
29 }
30 b, err := app.memberFor(" ada@example.com ")
31 if err != nil {
32 t.Fatalf("memberFor padded: %v", err)
33 }
34 if a.ID != b.ID {
35 t.Fatalf("a padded address minted a second member: %d then %d", a.ID, b.ID)
36 }
37}
38
39func TestMemberForRejectsAnEmptyAddress(t *testing.T) {
40 app := newTestApp(t)
41 if _, err := app.memberFor(""); err == nil {
42 t.Fatal(`memberFor("") = nil error; an empty address must never mint a member`)
43 }
44 if _, err := app.memberFor(" "); err == nil {
45 t.Fatal("memberFor with only whitespace = nil error, want a refusal")
46 }
47}
48
49func TestTwoPeopleGetTwoIDs(t *testing.T) {
50 app := newTestApp(t)
51 ada, err := app.memberFor("ada@example.com")
52 if err != nil {
53 t.Fatalf("memberFor ada: %v", err)
54 }
55 bob, err := app.memberFor("bob@example.com")
56 if err != nil {
57 t.Fatalf("memberFor bob: %v", err)
58 }
59 if ada.ID == bob.ID {
60 t.Fatalf("two addresses share one actor id (%d); every edit would be misattributed", ada.ID)
61 }
62}
63
64// A concurrent first sight is the realistic case — two tabs, one
65// person, both arriving on the same magic link — and read-then-create
66// loses it.
67func TestMemberForIsSafeUnderConcurrentFirstSight(t *testing.T) {
68 app := newTestApp(t)
69 const n = 8
70 ids := make(chan uint64, n)
71 errs := make(chan error, n)
72 start := make(chan struct{})
73 for i := 0; i < n; i++ {
74 go func() {
75 <-start
76 m, err := app.memberFor("ada@example.com")
77 if err != nil {
78 errs <- err
79 return
80 }
81 ids <- m.ID
82 }()
83 }
84 close(start)
85 var first uint64
86 for i := 0; i < n; i++ {
87 select {
88 case err := <-errs:
89 t.Fatalf("concurrent memberFor: %v", err)
90 case id := <-ids:
91 if first == 0 {
92 first = id
93 } else if id != first {
94 t.Fatalf("concurrent first sight minted two members: %d and %d", first, id)
95 }
96 }
97 }
98}
99