Files
summercms/internal/build/manifest_append_test.go
Jakub Zych 7de774b0b3 test(build): plugin dirs, install resolution and summer.yaml edits
- TestPluginDirs and TestEffectiveFramework over go list JSON fixtures and
  git checkouts; TestResolveInstall against an in-memory registry;
  TestAppendManifestPlugins for the in-place summer.yaml edit
- fix: a package of the application's own module was reported as a
  submodule with the application's git describe; it is in-app (local)
- fix: summer.yaml items written as '-   id:' keep their spacing when
  plugin:add or plugin:install appends to them
2026-10-29 21:22:47 +01:00

130 lines
5.7 KiB
Go

package build
import (
"os"
"path/filepath"
"strings"
"testing"
)
// TestAppendManifestPlugins pins the in-place summer.yaml edit of
// plugin:add and plugin:install: comments, blank lines and key order stay,
// new items take the existing indentation, a missing plugins block is
// appended, and an edit that does not parse back leaves the file as it was.
func TestAppendManifestPlugins(t *testing.T) {
add := []Plugin{{ID: "acme.shop", Module: "example.test/sm-shop-plugin"}, {ID: "acme.pgsubs", Module: "example.test/pg/subs"}}
cases := []struct {
name, src, want string
}{
{
name: "comments, blank lines and later keys stay",
src: "# The application.\nmodule: example.test/app # app module\nbinary: app\n\nplugins:\n" +
" # Core plugins.\n - id: acme.user\n module: example.test/sm-user-plugin # pinned\n\n" +
" - id: acme.blog\n module: example.test/sm-blog-plugin\n\n# Build tags.\ntags:\n - summercms_mcp\n",
want: "# The application.\nmodule: example.test/app # app module\nbinary: app\n\nplugins:\n" +
" # Core plugins.\n - id: acme.user\n module: example.test/sm-user-plugin # pinned\n\n" +
" - id: acme.blog\n module: example.test/sm-blog-plugin\n" +
" - id: acme.shop\n module: example.test/sm-shop-plugin\n - id: acme.pgsubs\n module: example.test/pg/subs\n" +
"\n# Build tags.\ntags:\n - summercms_mcp\n",
},
{
name: "indentation is matched",
src: "module: example.test/app\nbinary: app\nplugins:\n- id: acme.user\n module: example.test/sm-user-plugin\n",
want: "module: example.test/app\nbinary: app\nplugins:\n- id: acme.user\n module: example.test/sm-user-plugin\n" +
"- id: acme.shop\n module: example.test/sm-shop-plugin\n- id: acme.pgsubs\n module: example.test/pg/subs\n",
},
{
name: "four-space indentation",
src: "module: example.test/app\nbinary: app\nplugins:\n - id: acme.user\n module: example.test/sm-user-plugin\n",
want: "module: example.test/app\nbinary: app\nplugins:\n - id: acme.user\n module: example.test/sm-user-plugin\n" +
" - id: acme.shop\n module: example.test/sm-shop-plugin\n - id: acme.pgsubs\n module: example.test/pg/subs\n",
},
{
name: "dash alone on its line",
src: "module: example.test/app\nbinary: app\nplugins:\n -\n id: acme.user\n module: example.test/sm-user-plugin\n",
want: "module: example.test/app\nbinary: app\nplugins:\n -\n id: acme.user\n module: example.test/sm-user-plugin\n" +
" - id: acme.shop\n module: example.test/sm-shop-plugin\n - id: acme.pgsubs\n module: example.test/pg/subs\n",
},
{
name: "missing plugins block is appended",
src: "# App.\nmodule: example.test/app\nbinary: app",
want: "# App.\nmodule: example.test/app\nbinary: app\nplugins:\n" +
" - id: acme.shop\n module: example.test/sm-shop-plugin\n - id: acme.pgsubs\n module: example.test/pg/subs\n",
},
{
name: "empty inline list becomes a block",
src: "module: example.test/app\nbinary: app\nplugins: [] # none yet\ntags: []\n",
want: "module: example.test/app\nbinary: app\nplugins:\n" +
" - id: acme.shop\n module: example.test/sm-shop-plugin\n - id: acme.pgsubs\n module: example.test/pg/subs\n" +
"tags: []\n",
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
path := filepath.Join(t.TempDir(), manifestName)
write(t, path, c.src)
if err := appendManifestPlugins(path, add); err != nil {
t.Fatalf("appendManifestPlugins: %v", err)
}
got, _ := os.ReadFile(path)
if string(got) != c.want {
t.Errorf("summer.yaml =\n%s\nwant\n%s", got, c.want)
}
m, err := LoadManifest(path)
if err != nil {
t.Fatal(err)
}
n := len(m.Plugins)
if n < 2 || m.Plugins[n-2] != add[0] || m.Plugins[n-1] != add[1] {
t.Errorf("plugins = %+v", m.Plugins)
}
})
}
t.Run("refusals leave the file unchanged", func(t *testing.T) {
refusals := []struct {
name, src string
plugins []Plugin
want string
}{
{"inline list", "module: example.test/app\nbinary: app\nplugins: [{id: acme.user, module: example.test/u}]\n", add, "inline"},
{"bad id", "module: example.test/app\nbinary: app\n", []Plugin{{ID: "Acme", Module: "example.test/x"}}, "plugins[0]"},
{"bad module", "module: example.test/app\nbinary: app\n", []Plugin{{ID: "acme.x", Module: "-x"}}, "module"},
{"broken file", "module: [\n", add, ""},
// A second YAML document after the plugins block: the items
// land in it, the first document does not gain them, and the
// original bytes are restored.
{"edit does not parse back", "module: example.test/app\nbinary: app\nplugins:\n- id: acme.user\n module: example.test/sm-user-plugin\n---\nother: 1\n", add, "left unchanged"},
}
for _, r := range refusals {
path := filepath.Join(t.TempDir(), manifestName)
write(t, path, r.src)
err := appendManifestPlugins(path, r.plugins)
if err == nil || !strings.Contains(err.Error(), r.want) {
t.Errorf("%s: err = %v, want %q", r.name, err, r.want)
}
if got, _ := os.ReadFile(path); string(got) != r.src {
t.Errorf("%s: file changed:\n%s", r.name, got)
}
}
})
t.Run("nothing to add", func(t *testing.T) {
path := filepath.Join(t.TempDir(), manifestName)
src := "module: example.test/app\nbinary: app\n"
write(t, path, src)
if err := appendManifestPlugins(path, nil); err != nil {
t.Fatal(err)
}
if got, _ := os.ReadFile(path); string(got) != src {
t.Errorf("file changed:\n%s", got)
}
})
t.Run("missing file", func(t *testing.T) {
if err := appendManifestPlugins(filepath.Join(t.TempDir(), manifestName), add); err == nil {
t.Error("appendManifestPlugins on a missing file succeeded")
}
})
}