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) } }