Files
summercms/cmd/summer/main_test.go
Jakub Zych 6dacddc040 feat(11.1-01): add summer docs:build with the docsite generator core
- internal/docsite loads docs/ with strict site.yaml and frontmatter decoding
- goldmark GFM pipeline renders pages into an embedded html/template shell
- emits .html pages, .md siblings, llms.txt, llms-full.txt, search-index.json
- output guard refuses unmarked non-empty dirs and --out inside --src or root
- docs/index.md and docs/setup/installation.md; /site/ is gitignored
2026-09-30 21:18:32 +02:00

216 lines
6.1 KiB
Go

package main
import (
"bytes"
"go/parser"
"go/token"
"os"
"path/filepath"
"slices"
"strconv"
"strings"
"testing"
"git.golem15.com/golem15/summercms/modules/bonfire"
)
func TestToolCommandNames(t *testing.T) {
var names []string
for _, c := range toolCommands() {
names = append(names, c.Name)
}
for _, want := range []string{"build", "make:plugin", "make:model", "make:migration", "make:command", "make:job", "make:admin-controller", "plugin:add", "dev", "migrate", "migrate:rollback", "migrate:status", "serve", "queue:work", "queue:clear", "schedule:run", "parity:broadcasts", "docs:build"} {
if !slices.Contains(names, want) {
t.Fatalf("missing %s in %v", want, names)
}
}
helpWants := map[string][]string{
"make:plugin": {"vendor.plugin"},
"make:model": {"[plugin] [name]", "no-migration"},
"make:migration": {"[plugin] [name]"},
"make:command": {"[plugin] [name]"},
"make:job": {"[plugin] [name]"},
"make:admin-controller": {"[plugin] [name]"},
"schedule:run": {"--once"},
"parity:broadcasts": {"--flow", "--step", "--ids", "127.0.0.1:8424"},
"docs:build": {"--out", "--src", "--root", "--base-url", "--check"},
}
for cmd, wants := range helpWants {
var buf bytes.Buffer
root, err := bonfire.NewRoot("summer", toolCommands(), &buf)
if err != nil {
t.Fatal(err)
}
root.SetArgs([]string{cmd, "--help"})
if err := root.Execute(); err != nil {
t.Fatalf("%s --help: %v", cmd, err)
}
out := buf.String()
for _, want := range wants {
if !strings.Contains(out, want) {
t.Fatalf("%s help missing %q:\n%s", cmd, want, out)
}
}
}
var buf bytes.Buffer
root, err := bonfire.NewRoot("summer", toolCommands(), &buf)
if err != nil {
t.Fatal(err)
}
root.SetArgs([]string{"make:model"})
err = root.Execute()
if err == nil || !strings.Contains(err.Error(), "vendor.plugin") || !strings.Contains(err.Error(), "Name") {
t.Fatalf("make:model missing args error = %v", err)
}
}
func TestToolDoesNotImportExamplePlugins(t *testing.T) {
roots := []string{".", filepath.Join("..", "..", "internal", "build")}
fset := token.NewFileSet()
for _, root := range roots {
entries, err := os.ReadDir(root)
if err != nil {
t.Fatal(err)
}
for _, entry := range entries {
name := entry.Name()
if entry.IsDir() || !strings.HasSuffix(name, ".go") {
continue
}
file, err := parser.ParseFile(fset, filepath.Join(root, name), nil, parser.ImportsOnly)
if err != nil {
t.Fatal(err)
}
for _, spec := range file.Imports {
path, err := strconv.Unquote(spec.Path.Value)
if err != nil {
t.Fatal(err)
}
if strings.Contains(path, "examples/hello") || strings.Contains(path, "golem15/fonoteka") {
t.Fatalf("%s imports %s", name, path)
}
}
}
}
}
func TestMakeCommandsViaCLI(t *testing.T) {
dir := copyHelloApp(t)
t.Chdir(dir)
runSummer := func(args ...string) string {
t.Helper()
var buf bytes.Buffer
root, err := bonfire.NewRoot("summer", toolCommands(), &buf)
if err != nil {
t.Fatal(err)
}
root.SetArgs(args)
if err := root.Execute(); err != nil {
t.Fatalf("%v: %v\n%s", args, err, buf.String())
}
return buf.String()
}
out := runSummer("make:plugin", "golem15.demo")
if !strings.Contains(out, filepath.Join("plugins", "demo")) {
t.Fatalf("make:plugin output missing plugin dir:\n%s", out)
}
pluginDir := filepath.Join(dir, "plugins", "demo")
if _, err := os.Stat(filepath.Join(pluginDir, "plugin.go")); err != nil {
t.Fatal(err)
}
out = runSummer("make:model", "golem15.demo", "Album", "--no-migration")
if !strings.Contains(out, filepath.Join("models", "album.go")) {
t.Fatalf("make:model output missing album.go:\n%s", out)
}
if strings.Contains(out, "create_golem15_demo_albums") {
t.Fatalf("--no-migration still printed a migration:\n%s", out)
}
out = runSummer("make:job", "golem15.demo", "Reindex")
if !strings.Contains(out, filepath.Join("jobs", "reindex.go")) {
t.Fatalf("make:job output missing reindex.go:\n%s", out)
}
t.Chdir(pluginDir)
out = runSummer("make:command", "Purge")
if !strings.Contains(out, filepath.Join("console", "purge.go")) {
t.Fatalf("one-arg make:command output missing purge.go:\n%s", out)
}
pluginGo, err := os.ReadFile(filepath.Join(pluginDir, "plugin.go"))
if err != nil {
t.Fatal(err)
}
if !bytes.Contains(pluginGo, []byte("generatedJobs()")) {
t.Fatal("scaffold plugin.go should already call generated accessors")
}
}
func copyHelloApp(t *testing.T) string {
t.Helper()
src, err := filepath.Abs(filepath.Join("..", "..", "examples", "hello"))
if err != nil {
t.Fatal(err)
}
framework, err := filepath.Abs(filepath.Join("..", ".."))
if err != nil {
t.Fatal(err)
}
dst := t.TempDir()
if err := filepath.WalkDir(src, func(path string, d os.DirEntry, err error) error {
if err != nil {
return err
}
rel, err := filepath.Rel(src, path)
if err != nil {
return err
}
if rel == "bin" || strings.HasPrefix(rel, "bin"+string(os.PathSeparator)) {
if d.IsDir() {
return filepath.SkipDir
}
return nil
}
target := filepath.Join(dst, rel)
if d.IsDir() {
return os.MkdirAll(target, 0o755)
}
data, err := os.ReadFile(path)
if err != nil {
return err
}
if filepath.Base(path) == "go.mod" {
data = rewriteFrameworkReplace(data, framework)
}
if err := os.MkdirAll(filepath.Dir(target), 0o755); err != nil {
return err
}
return os.WriteFile(target, data, 0o644)
}); err != nil {
t.Fatal(err)
}
return dst
}
func rewriteFrameworkReplace(data []byte, framework string) []byte {
const module = "git.golem15.com/golem15/summercms"
lines := strings.Split(string(data), "\n")
for i, line := range lines {
trimmed := strings.TrimSpace(line)
switch {
case strings.HasPrefix(trimmed, "replace "+module+" =>"):
indent := line[:len(line)-len(strings.TrimLeft(line, " \t"))]
lines[i] = indent + "replace " + module + " => " + framework
case strings.HasPrefix(trimmed, module+" =>"):
indent := line[:len(line)-len(strings.TrimLeft(line, " \t"))]
lines[i] = indent + module + " => " + framework
}
}
return []byte(strings.Join(lines, "\n"))
}