test(cabana,flare,dev,summer): json, reported errors and dev serve
- Docker: admin:token:create and list --json, -q keeps the token - flare generate and test-push failures print once and are Reported - summer dev starts serve with the forwarded globals, reports rebuilds, failures and restarts, and stops serve with SIGTERM returning nil - delegates carry two examples and say the global options pass through
This commit is contained in:
134
modules/cabana/token_json_test.go
Normal file
134
modules/cabana/token_json_test.go
Normal file
@@ -0,0 +1,134 @@
|
||||
package cabana_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.golem15.com/golem15/summercms/modules/backpack"
|
||||
"git.golem15.com/golem15/summercms/modules/bonfire"
|
||||
"git.golem15.com/golem15/summercms/modules/cabana"
|
||||
"git.golem15.com/golem15/summercms/modules/party"
|
||||
)
|
||||
|
||||
// tokenJSONApp is a command app on the package database with the demo
|
||||
// plugin's scopes and one administrator.
|
||||
func tokenJSONApp(t *testing.T, login string) *backpack.App {
|
||||
t.Helper()
|
||||
gdb := adminGorm(t)
|
||||
if err := gdb.AutoMigrate(&tokenDemoWidget{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
app := commandApp(t, gdb)
|
||||
if err := app.Publish(party.Activated{tokenDemoPlugin{fsys: tokenDemoFS()}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
insertAdmin(t, gdb, login, login+"@example.test", "secret-pass", true, false)
|
||||
return app
|
||||
}
|
||||
|
||||
// callToken runs one admin:token command through bonfire.Call, so the
|
||||
// global options and flags are parsed as on the command line.
|
||||
func callToken(t *testing.T, app *backpack.App, name string, args ...string) string {
|
||||
t.Helper()
|
||||
var buf bytes.Buffer
|
||||
if err := bonfire.Call(context.Background(), cabana.RuntimeCommands(app), name, args, &buf); err != nil {
|
||||
t.Fatalf("%s %v: %v\n%s", name, args, err, buf.String())
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// TestTokenCreateJSON: admin:token:create --json prints one JSON object
|
||||
// with id, name, token, scopes and expires_at, under -q too.
|
||||
func TestTokenCreateJSON(t *testing.T) {
|
||||
app := tokenJSONApp(t, "json-create")
|
||||
for _, extra := range [][]string{nil, {"-q"}} {
|
||||
args := append([]string{"json-create", "--name", "agent", "--scope", "demo", "--expires", "2027-01-01", "--json"}, extra...)
|
||||
out := callToken(t, app, "admin:token:create", args...)
|
||||
var got struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Token string `json:"token"`
|
||||
Scopes []string `json:"scopes"`
|
||||
ExpiresAt *string `json:"expires_at"`
|
||||
}
|
||||
dec := json.NewDecoder(strings.NewReader(out))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&got); err != nil {
|
||||
t.Fatalf("%v: not the JSON object: %v\n%s", extra, err, out)
|
||||
}
|
||||
if got.ID == 0 || got.Name != "agent" || !wellFormedPlain(got.Token) || strings.Join(got.Scopes, ",") != "demo" ||
|
||||
got.ExpiresAt == nil || !strings.HasPrefix(*got.ExpiresAt, "2027-01-01") {
|
||||
t.Errorf("%v: created = %+v", extra, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestTokenListJSON: admin:token:list --json prints a JSON array of every
|
||||
// active token with its hint and never the token itself; --revoked adds
|
||||
// revoked ones; an empty list is [].
|
||||
func TestTokenListJSON(t *testing.T) {
|
||||
app := tokenJSONApp(t, "json-list")
|
||||
empty := callToken(t, app, "admin:token:list", "json-list", "--json")
|
||||
if strings.TrimSpace(empty) != "[]" {
|
||||
t.Fatalf("empty list = %q, want []", empty)
|
||||
}
|
||||
plain := strings.TrimSpace(callToken(t, app, "admin:token:create", "json-list", "--name", "one", "--scope", "demo"))
|
||||
second := strings.TrimSpace(callToken(t, app, "admin:token:create", "json-list", "--name", "two", "--scope", "demo"))
|
||||
|
||||
out := callToken(t, app, "admin:token:list", "json-list", "--json")
|
||||
var list []map[string]any
|
||||
if err := json.Unmarshal([]byte(out), &list); err != nil {
|
||||
t.Fatalf("not a JSON array: %v\n%s", err, out)
|
||||
}
|
||||
if len(list) != 2 {
|
||||
t.Fatalf("list = %v", list)
|
||||
}
|
||||
for _, key := range []string{"id", "login", "name", "scopes", "hint", "created_at", "last_used_at", "expires_at", "revoked_at"} {
|
||||
if _, ok := list[0][key]; !ok {
|
||||
t.Errorf("token entry lacks %s: %v", key, list[0])
|
||||
}
|
||||
}
|
||||
if list[0]["login"] != "json-list" || list[0]["name"] != "two" || list[0]["hint"] != "g15_…"+second[len(second)-4:] {
|
||||
t.Errorf("newest entry = %v", list[0])
|
||||
}
|
||||
if strings.Contains(out, plain) || strings.Contains(out, second) {
|
||||
t.Fatalf("the list leaks a plaintext token:\n%s", out)
|
||||
}
|
||||
|
||||
id := uint64(list[1]["id"].(float64))
|
||||
callToken(t, app, "admin:token:revoke", jsonNumber(id))
|
||||
if active := callToken(t, app, "admin:token:list", "json-list", "--json"); strings.Count(active, `"id":`) != 1 {
|
||||
t.Errorf("revoked token still listed:\n%s", active)
|
||||
}
|
||||
if all := callToken(t, app, "admin:token:list", "json-list", "--json", "--revoked", "-q"); strings.Count(all, `"id":`) != 2 || !strings.Contains(all, `"revoked_at": "`) {
|
||||
t.Errorf("--revoked -q list:\n%s", all)
|
||||
}
|
||||
}
|
||||
|
||||
// TestTokenCreateQuietKeepsToken: under -q admin:token:create still prints
|
||||
// the token, and only the token (data, not decoration).
|
||||
func TestTokenCreateQuietKeepsToken(t *testing.T) {
|
||||
app := tokenJSONApp(t, "json-quiet")
|
||||
out := callToken(t, app, "admin:token:create", "json-quiet", "--name", "agent", "--scope", "demo", "-q")
|
||||
if strings.Count(out, "\n") != 1 || !wellFormedPlain(strings.TrimSpace(out)) {
|
||||
t.Fatalf("-q output = %q, want one token line", out)
|
||||
}
|
||||
var list []struct {
|
||||
ID uint64 `json:"id"`
|
||||
}
|
||||
if err := json.Unmarshal([]byte(callToken(t, app, "admin:token:list", "json-quiet", "--json")), &list); err != nil || len(list) != 1 {
|
||||
t.Fatalf("list: %v %v", list, err)
|
||||
}
|
||||
revoked := callToken(t, app, "admin:token:revoke", jsonNumber(list[0].ID), "-q")
|
||||
if revoked != "" {
|
||||
t.Errorf("-q revoke printed %q, want nothing", revoked)
|
||||
}
|
||||
}
|
||||
|
||||
func jsonNumber(n uint64) string {
|
||||
b, _ := json.Marshal(n)
|
||||
return string(b)
|
||||
}
|
||||
Reference in New Issue
Block a user