179 lines
5.2 KiB
Go
179 lines
5.2 KiB
Go
package bm_test
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"slices"
|
|
"testing"
|
|
|
|
_ "git.golem15.com/golem15/sm-user-plugin"
|
|
"git.golem15.com/golem15/summercms/modules/backpack"
|
|
"git.golem15.com/golem15/summercms/modules/bonfire"
|
|
"git.golem15.com/golem15/summercms/modules/bouncer"
|
|
"git.golem15.com/golem15/summercms/modules/cabana"
|
|
"git.golem15.com/golem15/summercms/modules/compass"
|
|
"git.golem15.com/golem15/summercms/modules/pact"
|
|
"git.golem15.com/golem15/summercms/modules/party"
|
|
"git.golem15.com/golem15/summercms/modules/surf"
|
|
bm "git.golem15.com/jakub/sm-bm-plugin"
|
|
)
|
|
|
|
const (
|
|
testUserSecret = "jz-bm-test-only-user-jwt-secret-32bytes"
|
|
testAdminSecret = "jz-bm-test-only-admin-jwt-secret-32bytes"
|
|
)
|
|
|
|
// testConfig is the application config the tests boot with: the HTTP
|
|
// limits, the golem15.user JWT secret and the admin secret.
|
|
func testConfig(t *testing.T) *compass.Config {
|
|
t.Helper()
|
|
dir := t.TempDir()
|
|
httpYAML := "body_limits:\n default_bytes: 1048576\n upload_bytes: 10485760\n"
|
|
if err := os.WriteFile(filepath.Join(dir, "http.yaml"), []byte(httpYAML), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
cfg, err := compass.Open(compass.Options{
|
|
Dir: dir,
|
|
Environ: []string{
|
|
"SUMMER_ENV=testing",
|
|
"SUMMER_GOLEM15__USER__JWT__SECRET=" + testUserSecret,
|
|
"SUMMER_ADMIN__JWT__SECRET=" + testAdminSecret,
|
|
},
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return cfg
|
|
}
|
|
|
|
// activate boots golem15.user and jz.bm the way the generated main does.
|
|
func activate(t *testing.T, app *backpack.App) []party.Plugin {
|
|
t.Helper()
|
|
plugins, err := party.Activate(app, []string{"golem15.user", "jz.bm"})
|
|
if err != nil {
|
|
t.Fatalf("Activate: %v", err)
|
|
}
|
|
return plugins
|
|
}
|
|
|
|
func bmPlugin(t *testing.T, plugins []party.Plugin) *bm.Plugin {
|
|
t.Helper()
|
|
for _, p := range plugins {
|
|
if b, ok := p.(*bm.Plugin); ok {
|
|
return b
|
|
}
|
|
}
|
|
t.Fatal("jz.bm was not activated")
|
|
return nil
|
|
}
|
|
|
|
func TestPluginActivates(t *testing.T) {
|
|
app := backpack.New(testConfig(t))
|
|
p := bmPlugin(t, activate(t, app))
|
|
if p.ID() != "jz.bm" || !slices.Equal(p.Requires(), []string{"golem15.user"}) {
|
|
t.Errorf("ID %q Requires %v", p.ID(), p.Requires())
|
|
}
|
|
if n := len(p.Models()); n != 9 {
|
|
t.Errorf("Models = %d, want 9", n)
|
|
}
|
|
if n := len(p.Migrations()); n != 10 {
|
|
t.Errorf("Migrations = %d, want 10", n)
|
|
}
|
|
if !slices.ContainsFunc(p.Commands(), func(c bonfire.Command) bool { return c.Name == "bm:seed" }) {
|
|
t.Error("bm:seed is not registered")
|
|
}
|
|
if got := app.Config.Int("jz.bm.materials_per_page"); got != 20 {
|
|
t.Errorf("jz.bm.materials_per_page = %d, want the default 20", got)
|
|
}
|
|
if got := app.Config.Int("jz.bm.dashboard_limit"); got != 5 {
|
|
t.Errorf("jz.bm.dashboard_limit = %d, want the default 5", got)
|
|
}
|
|
}
|
|
|
|
func TestRoutes(t *testing.T) {
|
|
app := backpack.New(testConfig(t))
|
|
plugins := activate(t, app)
|
|
router, err := surf.BuildRouter(app, plugins)
|
|
if err != nil {
|
|
t.Fatalf("BuildRouter: %v", err)
|
|
}
|
|
want := []string{
|
|
"GET /_bm/api/v1/dashboard",
|
|
"GET /_bm/api/v1/courses",
|
|
"GET /_bm/api/v1/courses/{id}",
|
|
"GET /_bm/api/v1/schedule",
|
|
"GET /_bm/api/v1/meetings",
|
|
"GET /_bm/api/v1/deadlines",
|
|
"GET /_bm/api/v1/readings",
|
|
"PUT /_bm/api/v1/readings/{id}/progress",
|
|
"DELETE /_bm/api/v1/readings/{id}/progress",
|
|
"GET /_bm/api/v1/materials",
|
|
"GET /_bm/api/v1/materials/{id}",
|
|
}
|
|
var got []string
|
|
for _, rt := range router.Routes() {
|
|
if rt.PluginID != "jz.bm" {
|
|
continue
|
|
}
|
|
if rt.Method+" "+rt.Pattern == "GET /{path...}" {
|
|
// The student app, public by design; see static_test.go.
|
|
if len(rt.Middleware) != 0 {
|
|
t.Errorf("the student app has middleware %v", rt.Middleware)
|
|
}
|
|
continue
|
|
}
|
|
got = append(got, rt.Method+" "+rt.Pattern)
|
|
if !slices.Contains(rt.Middleware, "jwt.auth") {
|
|
t.Errorf("%s %s is not behind jwt.auth: %v", rt.Method, rt.Pattern, rt.Middleware)
|
|
}
|
|
}
|
|
if !slices.Equal(got, want) {
|
|
t.Errorf("routes =\n%v\nwant\n%v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestAdminCompiles(t *testing.T) {
|
|
app := backpack.New(testConfig(t))
|
|
plugins := activate(t, app)
|
|
p := bmPlugin(t, plugins)
|
|
routes, err := cabana.Activate(app, plugins)
|
|
if err != nil {
|
|
t.Fatalf("cabana.Activate: %v", err)
|
|
}
|
|
if routes == nil {
|
|
t.Fatal("no admin routes")
|
|
}
|
|
declared := map[string]bool{}
|
|
for _, perm := range p.Permissions() {
|
|
declared[perm.Code] = true
|
|
}
|
|
for _, ctl := range p.AdminControllers() {
|
|
perms := ctl.(pact.AdminPermissioned).RequiredPermissions()
|
|
for _, code := range perms {
|
|
if !declared[code] {
|
|
t.Errorf("%s requires undeclared %s", ctl.ID(), code)
|
|
}
|
|
}
|
|
if rec := ctl.(pact.AdminRecordSource).NewRecord(); rec == nil {
|
|
t.Errorf("%s has no record model", ctl.ID())
|
|
}
|
|
form, err := cabana.CompileForm("jz.bm", ctl, p.AdminFS())
|
|
if err != nil {
|
|
t.Errorf("%s form: %v", ctl.ID(), err)
|
|
} else if len(form.Fields) == 0 {
|
|
t.Errorf("%s form has no fields", ctl.ID())
|
|
}
|
|
if _, err := cabana.CompileList("jz.bm", ctl, p.AdminFS()); err != nil {
|
|
t.Errorf("%s list: %v", ctl.ID(), err)
|
|
}
|
|
editor := &bouncer.Principal{ID: 1, Backend: true, PermissionGrants: map[string]bool{"jz.bm.*": true}}
|
|
if !cabana.Allows(editor, perms) {
|
|
t.Errorf("jz.bm.* does not open %s", ctl.ID())
|
|
}
|
|
}
|
|
nav := p.Navigation()
|
|
if len(nav) != 1 || len(nav[0].SideMenu) != 7 {
|
|
t.Errorf("navigation = %+v, want one group with 7 side items", nav)
|
|
}
|
|
}
|