- Add surf.Router.AdminPrefix (additive): the admin mount path and true when BuildRouter mounted the cabana admin, false otherwise - serve binds with net.Listen before printing, so a busy port fails without a misleading URL, then prints the listen address with a browser URL and an admin status line: the admin URL, a warning naming summer admin:create when backend_users is empty or missing, or admin: off with the steps to add an admin controller - The administrator count is bounded by a 2 s timeout and never fails boot - route:list ends with the same admin status line - surf and cabana READMEs and the console and routing docs describe the output
125 lines
4.2 KiB
Go
125 lines
4.2 KiB
Go
package surf
|
||
|
||
import (
|
||
"bytes"
|
||
"os"
|
||
"path/filepath"
|
||
"slices"
|
||
"strings"
|
||
"testing"
|
||
|
||
"git.golem15.com/golem15/summercms/modules/backpack"
|
||
"git.golem15.com/golem15/summercms/modules/bonfire"
|
||
"git.golem15.com/golem15/summercms/modules/compass"
|
||
"gocloud.dev/blob"
|
||
)
|
||
|
||
func TestPublishUploadsRequiresURL(t *testing.T) {
|
||
app := serveTestApp(t, "uploads:\n bucket_url: \"\"\n")
|
||
_, err := publishUploads(t.Context(), app)
|
||
if err == nil || !strings.Contains(err.Error(), "bucket_url") {
|
||
t.Fatalf("got %v, want bucket_url error", err)
|
||
}
|
||
}
|
||
|
||
func TestPublishUploadsStoresBucket(t *testing.T) {
|
||
app := serveTestApp(t, "uploads:\n bucket_url: \"mem://\"\n public_path_prefix: \"/storage/uploads\"\n")
|
||
bucket, err := publishUploads(t.Context(), app)
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
t.Cleanup(func() { _ = bucket.Close() })
|
||
got, ok := app.Lookup[*blob.Bucket]()
|
||
if !ok || got != bucket {
|
||
t.Fatal("publishUploads must store the opened *blob.Bucket")
|
||
}
|
||
}
|
||
|
||
func serveTestApp(t *testing.T, storageYAML string) *backpack.App {
|
||
t.Helper()
|
||
dir := t.TempDir()
|
||
if err := os.WriteFile(filepath.Join(dir, "app.yaml"), []byte("name: serve-uploads\n"), 0o644); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if err := os.WriteFile(filepath.Join(dir, "storage.yaml"), []byte(storageYAML), 0o644); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
cfg, err := compass.Open(compass.Options{
|
||
Dir: dir,
|
||
Env: "development",
|
||
Environ: []string{"SUMMER_ENV=development"},
|
||
})
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
return backpack.New(cfg)
|
||
}
|
||
|
||
func TestServeURL(t *testing.T) {
|
||
for _, tc := range []struct{ addr, want string }{
|
||
{":8123", "http://127.0.0.1:8123/"},
|
||
{"127.0.0.1:9000", "http://127.0.0.1:9000/"},
|
||
{"[::1]:8080", "http://[::1]:8080/"},
|
||
{"0.0.0.0:80", "http://127.0.0.1/"},
|
||
{"[::]:8080", "http://127.0.0.1:8080/"},
|
||
{"localhost:3000", "http://localhost:3000/"},
|
||
{"8080", ""},
|
||
{"", ""},
|
||
{"127.0.0.1:", ""},
|
||
} {
|
||
if got := serveURL(tc.addr); got != tc.want {
|
||
t.Errorf("serveURL(%q) = %q, want %q", tc.addr, got, tc.want)
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestServeBanner(t *testing.T) {
|
||
listen := bannerLine{text: "listening on :8123, open http://127.0.0.1:8123/"}
|
||
mounted := bannerLine{text: "admin: http://127.0.0.1:8123/backend"}
|
||
create := "summer admin:create --email you@example.com --superuser"
|
||
for _, tc := range []struct {
|
||
name string
|
||
addr string
|
||
prefix string
|
||
mounted bool
|
||
admins adminPresence
|
||
want []bannerLine
|
||
}{
|
||
{"present", ":8123", "/backend", true, adminsPresent, []bannerLine{listen, mounted}},
|
||
{"none", ":8123", "/backend", true, adminsNone, []bannerLine{listen, mounted,
|
||
{warn: true, text: "no administrators yet; create one with " + create}}},
|
||
{"missing table", ":8123", "/backend", true, adminsMissingTable, []bannerLine{listen, mounted,
|
||
{warn: true, text: "the admin tables do not exist yet; run summer migrate, then " + create}}},
|
||
{"unknown", ":8123", "/backend", true, adminsUnknown, []bannerLine{listen, mounted,
|
||
{text: "sign in with an administrator; " + create + " creates one"}}},
|
||
{"off", ":8123", "", false, adminsUnknown, []bannerLine{listen,
|
||
{text: "admin: off — no plugin registers an admin controller"},
|
||
{text: "to add one, run summer make:admin-controller <vendor.plugin> <Name>, then summer build, summer migrate and " + create + "; the admin needs SUMMER_ADMIN__JWT__SECRET, which summer new writes to .env"}}},
|
||
{"unparsable address", "8080", "/backend", true, adminsPresent, []bannerLine{
|
||
{text: "listening on 8080"}, {text: "admin: mounted at /backend"}}},
|
||
} {
|
||
t.Run(tc.name, func(t *testing.T) {
|
||
got := serveBanner(tc.addr, tc.prefix, tc.mounted, tc.admins)
|
||
if !slices.Equal(got, tc.want) {
|
||
t.Fatalf("serveBanner:\n got %#v\nwant %#v", got, tc.want)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
func TestPrintBanner(t *testing.T) {
|
||
var buf bytes.Buffer
|
||
out := bonfire.NewOutput(strings.NewReader(""), &buf, &buf)
|
||
printBanner(out, []bannerLine{{text: "plain"}, {warn: true, text: "careful"}})
|
||
got := buf.String()
|
||
if !strings.Contains(got, "ℹ plain") || !strings.Contains(got, "⚠ careful") {
|
||
t.Fatalf("printBanner output = %q", got)
|
||
}
|
||
}
|
||
|
||
func TestCountAdminsNilDB(t *testing.T) {
|
||
if got := countAdmins(t.Context(), nil); got != adminsUnknown {
|
||
t.Fatalf("countAdmins(nil) = %v, want adminsUnknown", got)
|
||
}
|
||
}
|