rastrillo / idear Public

Clone
git clone https://amadan.net/rastrillo/idear

Plain git — no account needed to clone.

Download

Download this file

1package idear
2
3import (
4 "errors"
5 "testing"
6 "time"
7)
8
9// member is a small constructor for test fixtures: an active member
10// with the given id and role, or a deactivated one when deactivated is
11// true. Kept separate from the struct literals below so each case in
12// the table reads as data, not setup logic.
13func member(id int64, role Role, deactivated bool) *Member {
14 m := &Member{ID: id, Role: role}
15 if deactivated {
16 t := time.Unix(0, 0)
17 m.DeactivatedAt = &t
18 }
19 return m
20}
21
22// TestMayActOn_Matrix states, cell by cell, whether actor may act on
23// target. Every want is written down by hand rather than recomputed
24// from rank() or AtLeast() — a test that re-derives the implementation's
25// own comparison proves only that the comparison equals itself, which
26// is exactly the bug round 1's auditor found in the allow-list tests.
27//
28// Each cell carries TWO hand-written answers, one per build: the
29// default, and -tags idear_peers. `make ci` runs the suite under both,
30// so neither column is ever unexercised.
31func TestMayActOn_Matrix(t *testing.T) {
32 // Distinct ids on both sides so "different person" cases are never
33 // accidentally also self-action cases.
34 activeOwner := member(1, RoleOwner, false)
35 activeAdmin := member(2, RoleAdmin, false)
36 activeMember := member(3, RoleMember, false)
37
38 otherOwner := member(10, RoleOwner, false)
39 otherAdmin := member(20, RoleAdmin, false)
40 otherMember := member(30, RoleMember, false)
41
42 deactivatedOwner := member(1, RoleOwner, true)
43 deactivatedAdmin := member(2, RoleAdmin, true)
44
45 cases := []struct {
46 name string
47 actor *Member
48 target *Member
49 // Whether MayActOn refuses: in the default build, and in a
50 // build with PeerGrants.
51 refused, refusedPeers bool
52 }{
53 // The full 3x3 actor/target matrix, actor and target always
54 // distinct people. Peer grants change exactly one cell: an
55 // Admin acting on another Admin. An Owner acting on another
56 // Owner stays refused — nobody acts on the Owner, whichever
57 // build, and there is only ever one to act on.
58 {"owner acts on other owner", activeOwner, otherOwner, true, true},
59 {"owner acts on admin", activeOwner, otherAdmin, false, false},
60 {"owner acts on member", activeOwner, otherMember, false, false},
61 {"admin acts on owner", activeAdmin, otherOwner, true, true},
62 {"admin acts on other admin", activeAdmin, otherAdmin, true, false},
63 {"admin acts on member", activeAdmin, otherMember, false, false},
64 {"member acts on owner", activeMember, otherOwner, true, true},
65 {"member acts on admin", activeMember, otherAdmin, true, true},
66 {"member acts on other member", activeMember, otherMember, true, true},
67
68 // A deactivated Admin is still an Admin-ranked target: within
69 // a peer's reach under PeerGrants, beyond it otherwise.
70 {"admin acts on deactivated admin", activeAdmin, member(21, RoleAdmin, true), true, false},
71
72 // Self-action: refused for every rank, including the Owner.
73 {"owner acts on self", activeOwner, member(1, RoleOwner, false), true, true},
74 {"admin acts on self", activeAdmin, member(2, RoleAdmin, false), true, true},
75 {"member acts on self", activeMember, member(3, RoleMember, false), true, true},
76
77 // Inactive actor: refused regardless of rank, before rank is
78 // even considered.
79 {"deactivated owner acts on member", deactivatedOwner, otherMember, true, true},
80 {"deactivated admin acts on member", deactivatedAdmin, otherMember, true, true},
81 {"deactivated admin acts on admin", deactivatedAdmin, otherAdmin, true, true},
82
83 // Defensive: a nil actor or nil target can never be permitted.
84 {"nil actor", nil, otherMember, true, true},
85 {"nil target", activeOwner, nil, true, true},
86 }
87
88 for _, c := range cases {
89 t.Run(c.name, func(t *testing.T) {
90 err := MayActOn(c.actor, c.target)
91 wantErr := c.refused
92 if PeerGrants {
93 wantErr = c.refusedPeers
94 }
95 if wantErr {
96 if err == nil {
97 t.Fatalf("MayActOn(%v, %v) = nil, want ErrForbidden", c.actor, c.target)
98 }
99 if !errors.Is(err, ErrForbidden) {
100 t.Fatalf("MayActOn(%v, %v) = %v, want wrapped ErrForbidden", c.actor, c.target, err)
101 }
102 return
103 }
104 if err != nil {
105 t.Fatalf("MayActOn(%v, %v) = %v, want nil", c.actor, c.target, err)
106 }
107 })
108 }
109}
110