- make:admin-controller, make:command and make:migration output, finished: the Posts controller serves models.Post behind acme.blog.access_posts, WinterCMS-style form and list YAML embedded through pact.AdminAssets, blog:publish sets published_at by slug with a bound parameter - the posts route lists published posts only, newest first - Docker tests migrate an ICU pl-PL database, roll back published_at, serve the route and run blog:publish with a published and an opened database - the page gains the admin controller, console command and added-column sections
120 lines
3.8 KiB
Go
120 lines
3.8 KiB
Go
package blog_test
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"slices"
|
|
"testing"
|
|
|
|
"git.golem15.com/golem15/summercms/docs/examples/blog"
|
|
"git.golem15.com/golem15/summercms/docs/examples/blog/models"
|
|
"git.golem15.com/golem15/summercms/modules/backpack"
|
|
"git.golem15.com/golem15/summercms/modules/bonfire"
|
|
"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"
|
|
)
|
|
|
|
// activate boots acme.blog the way the generated main does: an application
|
|
// config directory with the required HTTP limits and admin secret, then
|
|
// party.Activate with the plugin ID.
|
|
func activate(t *testing.T) (*backpack.App, party.Plugin) {
|
|
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, Env: "testing", Environ: []string{}})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// The admin controller makes the admin API mount, which needs a secret.
|
|
if err := cfg.Set("admin.jwt.secret", "test-only-secret-with-at-least-32-bytes"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
app := backpack.New(cfg)
|
|
plugins, err := party.Activate(app, []string{"acme.blog"})
|
|
if err != nil {
|
|
t.Fatalf("Activate: %v", err)
|
|
}
|
|
if len(plugins) != 1 {
|
|
t.Fatalf("Activate returned %d plugins, want 1", len(plugins))
|
|
}
|
|
return app, plugins[0]
|
|
}
|
|
|
|
func TestPluginActivates(t *testing.T) {
|
|
app, p := activate(t)
|
|
if p.ID() != "acme.blog" {
|
|
t.Fatalf("ID = %q, want acme.blog", p.ID())
|
|
}
|
|
if _, ok := p.(*blog.Plugin); !ok {
|
|
t.Fatalf("activated plugin is %T, want *blog.Plugin", p)
|
|
}
|
|
if len(p.Requires()) != 0 {
|
|
t.Errorf("Requires = %v, want none", p.Requires())
|
|
}
|
|
if _, ok := p.(pact.HasRoutes); !ok {
|
|
t.Error("plugin does not implement pact.HasRoutes")
|
|
}
|
|
if _, ok := p.(pact.HasConfig); !ok {
|
|
t.Error("plugin does not implement pact.HasConfig")
|
|
}
|
|
if _, ok := p.(pact.HasLang); !ok {
|
|
t.Error("plugin does not implement pact.HasLang")
|
|
}
|
|
hasModels, ok := p.(pact.HasModels)
|
|
if !ok {
|
|
t.Fatal("plugin does not implement pact.HasModels")
|
|
}
|
|
if got := hasModels.Models(); len(got) != 1 {
|
|
t.Errorf("Models = %v, want one model", got)
|
|
} else if _, ok := got[0].(*models.Post); !ok {
|
|
t.Errorf("Models()[0] is %T, want *models.Post", got[0])
|
|
}
|
|
hasMigrations, ok := p.(pact.HasMigrations)
|
|
if !ok {
|
|
t.Fatal("plugin does not implement pact.HasMigrations")
|
|
}
|
|
if len(hasMigrations.Migrations()) == 0 {
|
|
t.Error("Migrations is empty")
|
|
}
|
|
hasCommands, ok := p.(pact.HasCommands)
|
|
if !ok {
|
|
t.Fatal("plugin does not implement pact.HasCommands")
|
|
}
|
|
if !slices.ContainsFunc(hasCommands.Commands(), func(c bonfire.Command) bool { return c.Name == "blog:publish" }) {
|
|
t.Error("Commands does not include blog:publish")
|
|
}
|
|
hasAdmin, ok := p.(pact.HasAdminControllers)
|
|
if !ok {
|
|
t.Fatal("plugin does not implement pact.HasAdminControllers")
|
|
}
|
|
if ctls := hasAdmin.AdminControllers(); len(ctls) != 1 || ctls[0].ID() != "acme.blog.posts" {
|
|
t.Errorf("AdminControllers = %v, want acme.blog.posts", ctls)
|
|
}
|
|
if _, ok := p.(pact.AdminAssets); !ok {
|
|
t.Error("plugin does not implement pact.AdminAssets")
|
|
}
|
|
if got := app.Config.Int("acme.blog.per_page"); got != 15 {
|
|
t.Errorf("acme.blog.per_page = %d, want the plugin default 15", got)
|
|
}
|
|
}
|
|
|
|
func TestRoutesRegistered(t *testing.T) {
|
|
app, p := activate(t)
|
|
router, err := surf.BuildRouter(app, []party.Plugin{p})
|
|
if err != nil {
|
|
t.Fatalf("BuildRouter: %v", err)
|
|
}
|
|
routes := router.Routes()
|
|
found := slices.ContainsFunc(routes, func(r surf.RouteInfo) bool {
|
|
return r.Method == "GET" && r.Pattern == "/api/blog/posts" && r.PluginID == "acme.blog"
|
|
})
|
|
if !found {
|
|
t.Fatalf("GET /api/blog/posts is not registered for acme.blog: %+v", routes)
|
|
}
|
|
}
|