From af8e58a0380226f56498302c14b76727db187ebb Mon Sep 17 00:00:00 2001 From: Jakub Zych Date: Thu, 24 Sep 2026 18:24:06 +0200 Subject: [PATCH] test(09-03): add failing tests for Winter admin controller scaffolding - make:admin-controller must emit config_form and config_list beside model fields and columns - A pre-existing model asset must fail before any controller file is written --- cabana/schema.go | 5 ++ internal/build/build_test.go | 156 +++++++++++++++++++++++++++++++++++ 2 files changed, 161 insertions(+) diff --git a/cabana/schema.go b/cabana/schema.go index a8d702e..dbc6126 100644 --- a/cabana/schema.go +++ b/cabana/schema.go @@ -41,6 +41,11 @@ type columnDocument struct { Select string `yaml:"select"` } +// CompileList compiles one controller's config_list.yaml and columns.yaml. +func CompileList(pluginID string, ctl pact.AdminController, fsys fs.FS) (*ListSchema, error) { + return compileList(pluginID, ctl, fsys) +} + func compileList(pluginID string, ctl pact.AdminController, fsys fs.FS) (*ListSchema, error) { dir := strings.Trim(path.Clean(ctl.ConfigDir()), "/") if dir == "." || strings.HasPrefix(dir, "..") { diff --git a/internal/build/build_test.go b/internal/build/build_test.go index 6bd4868..91c00c9 100644 --- a/internal/build/build_test.go +++ b/internal/build/build_test.go @@ -8,6 +8,9 @@ import ( "strconv" "strings" "testing" + + "git.golem15.com/golem15/summercms/cabana" + "git.golem15.com/golem15/summercms/pact" ) func TestLoadHelloManifest(t *testing.T) { @@ -878,6 +881,159 @@ func TestMakeArtifactRejectsMalformedNames(t *testing.T) { } } +func TestMakeAdminControllerLayout(t *testing.T) { + dir := copyHelloApp(t) + ctx := t.Context() + pluginDir, err := MakePlugin(ctx, dir, "golem15.demo") + if err != nil { + t.Fatal(err) + } + pluginBefore, err := os.ReadFile(filepath.Join(pluginDir, "plugin.go")) + if err != nil { + t.Fatal(err) + } + + t.Run("duplicate asset writes nothing", func(t *testing.T) { + kept := filepath.Join(pluginDir, "models", "genres", "fields.yaml") + if err := os.MkdirAll(filepath.Dir(kept), 0o755); err != nil { + t.Fatal(err) + } + const original = "fields: {}\n" + if err := os.WriteFile(kept, []byte(original), 0o644); err != nil { + t.Fatal(err) + } + if _, err := MakeAdminController(ctx, dir, "golem15.demo", "Genres"); err == nil { + t.Fatal("duplicate model asset want error") + } + body, err := os.ReadFile(kept) + if err != nil || string(body) != original { + t.Fatalf("existing fields.yaml changed: %v %q", err, body) + } + for _, rel := range []string{ + filepath.Join("controllers", "genres.go"), + filepath.Join("controllers", "genres", "config_form.yaml"), + filepath.Join("controllers", "genres", "config_list.yaml"), + filepath.Join("models", "genres", "columns.yaml"), + } { + if _, err := os.Stat(filepath.Join(pluginDir, rel)); !os.IsNotExist(err) { + t.Fatalf("partial scaffold file %s err=%v", rel, err) + } + } + }) + + t.Run("winter files", func(t *testing.T) { + admin, err := MakeAdminController(ctx, dir, "golem15.demo", "Albums") + if err != nil { + t.Fatal(err) + } + want := []string{ + filepath.Join(pluginDir, "controllers", "albums.go"), + filepath.Join(pluginDir, "controllers", "albums", "config_form.yaml"), + filepath.Join(pluginDir, "controllers", "albums", "config_list.yaml"), + filepath.Join(pluginDir, "models", "albums", "fields.yaml"), + filepath.Join(pluginDir, "models", "albums", "columns.yaml"), + } + if strings.Join(admin.Files, "\n") != strings.Join(want, "\n") { + t.Fatalf("files =\n%s\nwant\n%s", strings.Join(admin.Files, "\n"), strings.Join(want, "\n")) + } + for _, rel := range []string{ + filepath.Join("controllers", "albums", "fields.yaml"), + filepath.Join("controllers", "albums", "columns.yaml"), + } { + if _, err := os.Stat(filepath.Join(pluginDir, rel)); !os.IsNotExist(err) { + t.Fatalf("old controller-local asset %s still exists", rel) + } + } + goSrc, err := os.ReadFile(want[0]) + if err != nil { + t.Fatal(err) + } + for _, text := range []string{ + `ID() string { return "golem15.demo.albums" }`, + `ModelName() string { return "Albums" }`, + `ConfigDir() string { return "controllers/albums" }`, + } { + if !bytes.Contains(goSrc, []byte(text)) { + t.Fatalf("albums.go missing %s:\n%s", text, goSrc) + } + } + formSrc, err := os.ReadFile(want[1]) + if err != nil { + t.Fatal(err) + } + for _, text := range []string{ + "modelClass: Albums", + "form: ~/plugins/golem15/demo/models/albums/fields.yaml", + } { + if !bytes.Contains(formSrc, []byte(text)) { + t.Fatalf("config_form.yaml missing %s:\n%s", text, formSrc) + } + } + listSrc, err := os.ReadFile(want[2]) + if err != nil { + t.Fatal(err) + } + for _, text := range []string{ + "modelClass: Albums", + "list: ~/plugins/golem15/demo/models/albums/columns.yaml", + } { + if !bytes.Contains(listSrc, []byte(text)) { + t.Fatalf("config_list.yaml missing %s:\n%s", text, listSrc) + } + } + fsys := os.DirFS(pluginDir) + ctl := scaffoldAlbumsController{} + if _, err := cabana.CompileForm("golem15.demo", ctl, fsys); err != nil { + t.Fatalf("strict form compile: %v", err) + } + if _, err := cabana.CompileList("golem15.demo", ctl, fsys); err != nil { + t.Fatalf("strict list compile: %v", err) + } + reg1, err := os.ReadFile(filepath.Join(pluginDir, "registry.gen.go")) + if err != nil { + t.Fatal(err) + } + if !bytes.Contains(reg1, []byte("controllers.AlbumsController()")) { + t.Fatalf("registry missing albums controller:\n%s", reg1) + } + if err := refreshRegistry(pluginDir); err != nil { + t.Fatal(err) + } + reg2, err := os.ReadFile(filepath.Join(pluginDir, "registry.gen.go")) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(reg1, reg2) { + t.Fatal("repeated registry generation changed bytes") + } + if _, err := MakeAdminController(ctx, dir, "golem15.demo", "Albums"); err == nil { + t.Fatal("duplicate controller want error") + } + reg3, err := os.ReadFile(filepath.Join(pluginDir, "registry.gen.go")) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(reg1, reg3) { + t.Fatal("failed duplicate scaffold changed registry.gen.go") + } + pluginAfter, err := os.ReadFile(filepath.Join(pluginDir, "plugin.go")) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(pluginBefore, pluginAfter) { + t.Fatal("handwritten plugin.go was rewritten") + } + }) +} + +type scaffoldAlbumsController struct{} + +func (scaffoldAlbumsController) ID() string { return "golem15.demo.albums" } +func (scaffoldAlbumsController) ModelName() string { return "Albums" } +func (scaffoldAlbumsController) ConfigDir() string { return "controllers/albums" } + +var _ pact.AdminController = scaffoldAlbumsController{} + func assertScaffoldFiles(t *testing.T, pluginDir string) { t.Helper() assertWinterPluginShape(t, pluginDir)