Repository navigation
Expand file tree
/
Copy pathcreateadmin_test.go
More file actions
217 lines (179 loc) · 7.48 KB
/
Copy pathcreateadmin_test.go
File metadata and controls
217 lines (179 loc) · 7.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
// SPDX-License-Identifier: Apache-2.0
package auth_test
import (
"context"
"errors"
"fmt"
"slices"
"sync/atomic"
"testing"
"github.com/gopherium/framework/gonsole"
"github.com/gopherium/framework/gonsole/auth"
"github.com/gopherium/framework/gonsole/testkit"
)
// createPage is the help page of account:create-admin.
const createPage = `create an account under a role
Usage:
myapp account:create-admin [flags]
Flags:
-email address
address of the new account
-name name
display name of the new account
-role role
role the new account starts under
`
// migratedLine is the line the account schema step writes before a command that asks for it.
const migratedLine = "migrated accounts\n"
// creating returns the arguments that create maria.perez@example.com under role.
func creating(role string) []string {
return []string{"account:create-admin", "-email", "maria.perez@example.com", "-name", "Maria Perez", "-role", role}
}
func TestCreateAdminCreatesAnAccountAfterTheSchema(t *testing.T) {
t.Parallel()
address := fresh(t)
var reads rolesRead
var before atomic.Int32
before.Store(-1)
cfg := config(&reads, "")
cfg.Stores = func(ctx context.Context, call gonsole.Call) (auth.Stores, func(context.Context) error, error) {
before.Store(worldAt(t, address).migrations.Load())
return memoryStores(ctx, call)
}
got := testkit.Run(t, program(address, auth.CreateAdmin(cfg)), demoPassword+"\n", creating("admin")...)
want := testkit.Result{Code: gonsole.ExitDone, Stdout: "Password: created user maria.perez@example.com\n",
Stderr: migratedLine}
if got != want {
t.Fatalf("Run() = %+v, want %+v", got, want)
}
if ran := before.Load(); ran != 1 {
t.Errorf("the accounts step ran %d times before the account, want once", ran)
}
if held := account(t, storeAt(t, address), "maria.perez@example.com"); held.Role != "admin" ||
held.Name != "Maria Perez" {
t.Errorf("account = %+v, want Maria Perez under admin", held)
}
}
func TestCreateAdminRefusesALineItCannotRun(t *testing.T) {
t.Parallel()
cases := []struct {
name string
args []string
stdin string
code int
stdout string
stderr string
reads int
}{
{"an unknown role", creating("owner"), "", gonsole.ExitMisused, "",
migratedLine + "myapp: unknown role \"owner\", want admin, editor or author\n\n" + createPage, 1},
{"a padded role", creating(" admin"), "", gonsole.ExitMisused, "",
migratedLine + "myapp: unknown role \" admin\", want admin, editor or author\n\n" + createPage, 1},
{"a taken address", []string{"account:create-admin", "-email", "admin@example.com", "-name", "Maria Perez",
"-role", "admin"}, demoPassword + "\n", gonsole.ExitFailed, "Password: ",
migratedLine + "myapp: gouncer: email already taken\n", 1},
{"a weak password", creating("admin"), "short\n", gonsole.ExitFailed, "Password: ",
migratedLine + "myapp: gouncer: password shorter than 12 characters\n", 1},
{"a request for help", []string{"account:create-admin", "-h"}, "", gonsole.ExitDone, createPage, "", 0},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
var reads rolesRead
got := testkit.Run(t, program(seeded(t), auth.CreateAdmin(config(&reads, ""))), tc.stdin, tc.args...)
want := testkit.Result{Code: tc.code, Stdout: tc.stdout, Stderr: tc.stderr}
if got != want {
t.Errorf("Run() = %+v, want %+v", got, want)
}
if reads.count != tc.reads {
t.Errorf("roles read %d times, want %d", reads.count, tc.reads)
}
})
}
}
func TestCreateAdminRefusesAMissingFlagBeforeAnySchemaStep(t *testing.T) {
t.Parallel()
cases := []struct {
name string
args []string
stderr string
}{
{"no address", []string{"account:create-admin", "-name", "Maria Perez", "-role", "admin"},
"myapp: account:create-admin wants -email <address>\n\n" + createPage},
{"a blank address", []string{"account:create-admin", "-email", " ", "-name", "Maria Perez", "-role", "admin"},
"myapp: account:create-admin wants -email <address>\n\n" + createPage},
{"no name", []string{"account:create-admin", "-email", "maria.perez@example.com", "-role", "admin"},
"myapp: account:create-admin wants -name <name>\n\n" + createPage},
{"a blank name", append(creating("admin")[:3], "-name", " ", "-role", "admin"),
"myapp: account:create-admin wants -name <name>\n\n" + createPage},
{"no role", creating("admin")[:5], "myapp: account:create-admin wants -role <role>\n\n" + createPage},
{"a blank role", creating(" "), "myapp: account:create-admin wants -role <role>\n\n" + createPage},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
address := fresh(t)
var reads rolesRead
got := testkit.Run(t, program(address, auth.CreateAdmin(config(&reads, ""))), "", tc.args...)
if want := (testkit.Result{Code: gonsole.ExitMisused, Stderr: tc.stderr}); got != want {
t.Errorf("Run() = %+v, want %+v", got, want)
}
if reads.count != 0 {
t.Errorf("roles read %d times, want none", reads.count)
}
if ran := worldAt(t, address).migrations.Load(); ran != 0 {
t.Errorf("the accounts step ran %d times, want a bare database", ran)
}
})
}
}
func TestCreateAdminTakesARoleThePluginsDeclare(t *testing.T) {
t.Parallel()
address := migrated(t)
cfg := auth.Config{Roles: func(context.Context, gonsole.Call) (auth.Roles, error) {
return auth.Roles{Known: append(slices.Clone(vocabulary.Known), "superadmin"), Privileged: vocabulary.Privileged}, nil
}, Stores: memoryStores}
got := testkit.Run(t, program(address, auth.CreateAdmin(cfg)), demoPassword+"\n", creating("superadmin")...)
if got.Code != gonsole.ExitDone {
t.Fatalf("Run() = %+v, want a created account", got)
}
if held := account(t, storeAt(t, address), "maria.perez@example.com"); held.Role != "superadmin" {
t.Errorf("role = %q, want superadmin", held.Role)
}
}
func TestCreateAdminFailsWhenTheRolesCannotBeRead(t *testing.T) {
t.Parallel()
cfg := auth.Config{Roles: func(context.Context, gonsole.Call) (auth.Roles, error) {
return auth.Roles{}, errors.New("the plugin table is locked")
}, Stores: memoryStores}
got := testkit.Run(t, program(migrated(t), auth.CreateAdmin(cfg)), demoPassword+"\n", creating("admin")...)
want := testkit.Result{Code: gonsole.ExitFailed, Stderr: migratedLine + "myapp: the plugin table is locked\n"}
if got != want {
t.Errorf("Run() = %+v, want %+v", got, want)
}
}
// unknownRole is one role account:create-admin refuses and the alternatives its refusal names.
type unknownRole struct {
role, alternatives string
}
// refusesUnknownRole runs account:create-admin under refused.role over a program declaring known and wants it refused.
func refusesUnknownRole(t *testing.T, known []string, refused unknownRole) {
t.Helper()
cfg := auth.Config{Roles: func(context.Context, gonsole.Call) (auth.Roles, error) {
return auth.Roles{Known: known}, nil
}, Stores: memoryStores}
got := testkit.Run(t, program(migrated(t), auth.CreateAdmin(cfg)), "", creating(refused.role)...)
want := migratedLine + fmt.Sprintf("myapp: unknown role %q, want %s\n\n", refused.role, refused.alternatives) +
createPage
if got.Code != gonsole.ExitMisused || got.Stderr != want {
t.Errorf("Run() = %+v, want exit 2 and %q", got, want)
}
}
func TestCreateAdminNamesNoRoleWhenTheProgramDeclaresNone(t *testing.T) {
t.Parallel()
refusesUnknownRole(t, nil, unknownRole{"admin", "a role the program declares"})
}
func TestCreateAdminNamesTheOneRoleTheProgramDeclares(t *testing.T) {
t.Parallel()
refusesUnknownRole(t, []string{"admin"}, unknownRole{"owner", "admin"})
}