- TestPluginCommands: plugin:install needs --yes off a terminal, --offline and a disabled registry refuse ID installs without registry calls, plugin:manifest --check exit status and output on missing and stale files - check-phase34.sh: the 34-06 unit tests are required names of --go
162 lines
5.9 KiB
Go
162 lines
5.9 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"sync/atomic"
|
|
"testing"
|
|
|
|
"git.golem15.com/golem15/summercms/modules/bonfire"
|
|
"git.golem15.com/golem15/summercms/modules/kiosk"
|
|
)
|
|
|
|
// runTool runs the summer CLI with args through bonfire.Execute and
|
|
// returns the exit status and what went to the root's writer.
|
|
func runTool(t *testing.T, args ...string) (int, string) {
|
|
t.Helper()
|
|
var buf bytes.Buffer
|
|
root, err := bonfire.NewRoot("summer", toolCommands(), &buf)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
root.SetErr(&buf)
|
|
code := bonfire.Execute(context.Background(), root, args)
|
|
return code, buf.String()
|
|
}
|
|
|
|
// writeFiles writes files (slash paths relative to root).
|
|
func writeFiles(t *testing.T, root string, files map[string]string) {
|
|
t.Helper()
|
|
for rel, body := range files {
|
|
path := filepath.Join(root, filepath.FromSlash(rel))
|
|
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(path, []byte(body), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestPluginCommands covers the CLI rules of plugin:install and
|
|
// plugin:manifest that do not need a build: the --yes rule off a terminal,
|
|
// --offline ID installs, and --check's exit status and output.
|
|
func TestPluginCommands(t *testing.T) {
|
|
var calls atomic.Int32
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
calls.Add(1)
|
|
if r.URL.Path == kiosk.RegistryAPIPath+"/plugins/acme.shop/versions" {
|
|
_ = json.NewEncoder(w).Encode(kiosk.VersionsResponse{Data: []kiosk.RegistryVersion{{
|
|
Version: "v0.1.0", Framework: "v0.6.0", Module: "example.test/sm-shop-plugin", Package: "example.test/sm-shop-plugin",
|
|
}}})
|
|
return
|
|
}
|
|
http.NotFound(w, r)
|
|
}))
|
|
t.Cleanup(srv.Close)
|
|
|
|
app := t.TempDir()
|
|
writeFiles(t, app, map[string]string{
|
|
"summer.yaml": "module: example.test/app\nbinary: app\n",
|
|
"go.mod": "module example.test/app\n\ngo 1.27.0\n\nrequire git.golem15.com/golem15/summercms v0.6.0\n",
|
|
"config/.gitkeep": "",
|
|
})
|
|
t.Chdir(app)
|
|
t.Setenv("SUMMER_PLUGINS__REGISTRY_URL", srv.URL)
|
|
|
|
t.Run("install needs --yes off a terminal", func(t *testing.T) {
|
|
code, out := runTool(t, "plugin:install", "acme.shop")
|
|
if code == bonfire.ExitOK || !strings.Contains(out, "--yes") {
|
|
t.Fatalf("exit %d, output:\n%s", code, out)
|
|
}
|
|
if !strings.Contains(out, "acme.shop@v0.1.0 (module)") {
|
|
t.Errorf("the plan was not printed before the refusal:\n%s", out)
|
|
}
|
|
before, _ := os.ReadFile(filepath.Join(app, "summer.yaml"))
|
|
if string(before) != "module: example.test/app\nbinary: app\n" {
|
|
t.Errorf("summer.yaml changed:\n%s", before)
|
|
}
|
|
})
|
|
|
|
t.Run("offline ID install", func(t *testing.T) {
|
|
calls.Store(0)
|
|
code, out := runTool(t, "plugin:install", "acme.shop", "--offline", "--yes")
|
|
if code == bonfire.ExitOK || !strings.Contains(out, "--offline") {
|
|
t.Fatalf("exit %d, output:\n%s", code, out)
|
|
}
|
|
if n := calls.Load(); n != 0 {
|
|
t.Errorf("--offline made %d registry calls", n)
|
|
}
|
|
})
|
|
|
|
t.Run("disabled registry", func(t *testing.T) {
|
|
t.Setenv("SUMMER_PLUGINS__REGISTRY_URL", "")
|
|
calls.Store(0)
|
|
code, out := runTool(t, "plugin:install", "acme.shop", "--yes")
|
|
if code == bonfire.ExitOK || !strings.Contains(out, "plugins.registry_url") || calls.Load() != 0 {
|
|
t.Fatalf("exit %d, calls %d, output:\n%s", code, calls.Load(), out)
|
|
}
|
|
})
|
|
|
|
t.Run("install outside an application", func(t *testing.T) {
|
|
t.Chdir(t.TempDir())
|
|
if code, _ := runTool(t, "plugin:install", "acme.shop", "--yes"); code == bonfire.ExitOK {
|
|
t.Error("plugin:install outside an application succeeded")
|
|
}
|
|
})
|
|
|
|
t.Run("manifest --check", func(t *testing.T) {
|
|
plugin := t.TempDir()
|
|
writeFiles(t, plugin, map[string]string{
|
|
"go.mod": "module example.test/sm-shop-plugin\n\ngo 1.27.0\n\nrequire " + kiosk.FrameworkModule + " v0.6.0\n",
|
|
"plugin.go": "package shop\n\ntype Plugin struct{}\n\nfunc (*Plugin) ID() string { return \"acme.shop\" }\n",
|
|
"README.md": "# Shop\n\nProducts and orders.\n",
|
|
})
|
|
path := filepath.Join(plugin, kiosk.FileName)
|
|
|
|
code, out := runTool(t, "plugin:manifest", "--check", plugin)
|
|
if code != bonfire.ExitFailure || !strings.Contains(out, "Expected "+path+":") || !strings.Contains(out, "id: acme.shop") ||
|
|
!strings.Contains(out, "is missing") {
|
|
t.Fatalf("missing: exit %d, output:\n%s", code, out)
|
|
}
|
|
if _, err := os.Stat(path); !os.IsNotExist(err) {
|
|
t.Fatalf("--check wrote %s", path)
|
|
}
|
|
|
|
if code, out := runTool(t, "plugin:manifest", plugin); code != bonfire.ExitOK || !strings.Contains(out, "Wrote "+path) {
|
|
t.Fatalf("write: exit %d, output:\n%s", code, out)
|
|
}
|
|
if code, out := runTool(t, "plugin:manifest", "--check", plugin); code != bonfire.ExitOK || !strings.Contains(out, "is up to date") {
|
|
t.Fatalf("fresh: exit %d, output:\n%s", code, out)
|
|
}
|
|
|
|
writeFiles(t, plugin, map[string]string{"README.md": "# Shop\n\nProducts, carts and orders.\n"})
|
|
written, _ := os.ReadFile(path)
|
|
code, out = runTool(t, "plugin:manifest", "--check", plugin)
|
|
if code != bonfire.ExitFailure || !strings.Contains(out, "is stale") || !strings.Contains(out, "description: Products, carts and orders.") {
|
|
t.Fatalf("stale: exit %d, output:\n%s", code, out)
|
|
}
|
|
if after, _ := os.ReadFile(path); !bytes.Equal(after, written) {
|
|
t.Error("--check rewrote a stale file")
|
|
}
|
|
|
|
code, out = runTool(t, "plugin:manifest", "--check", "--name", "Shop", plugin)
|
|
if code == bonfire.ExitOK || !strings.Contains(out, "takes no --name or --description") {
|
|
t.Fatalf("--check --name: exit %d, output:\n%s", code, out)
|
|
}
|
|
code, out = runTool(t, "plugin:manifest", "--pretend", plugin)
|
|
if code != bonfire.ExitOK || !strings.Contains(out, "description: Products, carts and orders.") {
|
|
t.Fatalf("--pretend: exit %d, output:\n%s", code, out)
|
|
}
|
|
if after, _ := os.ReadFile(path); !bytes.Equal(after, written) {
|
|
t.Error("--pretend wrote the file")
|
|
}
|
|
})
|
|
}
|