package packs
import (
"io/fs"
"os"
"path/filepath"
"strings"
"testing"
"git.coffeylabs.org/coffey-labs/hotdog-cms/internal/build"
"git.coffeylabs.org/coffey-labs/hotdog-cms/internal/markup"
"git.coffeylabs.org/coffey-labs/hotdog-cms/internal/site"
"git.coffeylabs.org/coffey-labs/hotdog-cms/starter"
)
func write(t *testing.T, dir string, files map[string]string) string {
t.Helper()
for rel, body := range files {
p := filepath.Join(dir, filepath.FromSlash(rel))
os.MkdirAll(filepath.Dir(p), 0o755)
os.WriteFile(p, []byte(body), 0o644)
}
return dir
}
func starterSite(t *testing.T) string {
t.Helper()
dir := t.TempDir()
fs.WalkDir(starter.Files, "site", func(p string, d fs.DirEntry, err error) error {
target := filepath.Join(dir, strings.TrimPrefix(p, "site"))
if d.IsDir() {
return os.MkdirAll(target, 0o755)
}
data, _ := starter.Files.ReadFile(p)
return os.WriteFile(target, []byte(strings.ReplaceAll(string(data), "{{SITE_NAME}}", "Test")), 0o644)
})
return dir
}
const faqSection = `{{/* section
label: FAQ
fields:
- name: items
type: list
fields:
- { name: q, type: text, label: Question }
- { name: a, type: markdown, label: Answer }
*/}}
{{ .q }}
{{ markdownify .a }}
Yes, AGPL.
") || !strings.Contains(string(home), "/packs/faq/faq.") { t.Errorf("built page:\n%s", home) } // An upgrade replaces its files and drops the ones it no longer has. os.Remove(filepath.Join(pack, "assets", "old.css")) os.WriteFile(filepath.Join(pack, "pack.yaml"), []byte("name: faq\nversion: 1.1.0\n"), 0o644) if _, err := Add(site1, pack, false); err != nil { t.Fatal(err) } if _, err := os.Stat(filepath.Join(site1, "assets", "packs", "faq", "old.css")); err == nil { t.Error("a file the new version dropped is still there") } cfg, _ = site.LoadConfig(site1) if cfg.Packs[0].Version != "1.1.0" || len(cfg.Packs[0].Files) != 2 { t.Errorf("after upgrade: %+v", cfg.Packs) } // Another pack can't claim the same file; a site's own file isn't overwritten. other := write(t, t.TempDir(), map[string]string{"pack.yaml": "name: other\nversion: 1.0.0\n", "sections/faq.html": "{{ .Data.x }}
"}) if _, err := Add(site1, other, false); err == nil || !strings.Contains(err.Error(), "already installed by the faq pack") { t.Errorf("clash between packs: %v", err) } own := write(t, t.TempDir(), map[string]string{"pack.yaml": "name: hero2\nversion: 1.0.0\n", "sections/hero.html": "mine
"}) if _, err := Add(site1, own, false); err == nil || !strings.Contains(err.Error(), "already in the site") { t.Errorf("overwrote the site's own file: %v", err) } // Removing it takes its files away and forgets it. if err := Remove(site1, "faq"); err != nil { t.Fatal(err) } cfg, _ = site.LoadConfig(site1) if _, err := os.Stat(filepath.Join(site1, "sections", "faq.html")); err == nil || len(cfg.Packs) != 0 { t.Errorf("after remove: %v %+v", err, cfg.Packs) } if raw, _ := os.ReadFile(filepath.Join(site1, "site.yaml")); strings.Contains(string(raw), "\npacks:") { t.Error("site.yaml still lists packs") } } func TestPacksCarryNoCode(t *testing.T) { bad := write(t, t.TempDir(), map[string]string{ "pack.yaml": "name: bad\nversion: 1.0.0\n", "sections/a.html": `nested
", }) _, _, err := Check(bad) if err == nil { t.Fatal("a pack with code was accepted") } for _, want := range []string{"sections/a.html", "sections/b.html", "partials/c.html", "assets/tracker.js: JavaScript", "assets/logo.svg: SVG", "content/extra.md", "sections/sub/d.html"} { if !strings.Contains(err.Error(), want) { t.Errorf("missing %q in:\n%v", want, err) } } } func TestBundledCalendar(t *testing.T) { if !Bundled("calendar") || Bundled("../calendar") || Bundled("nope") { t.Fatal("bundled pack names") } dir := starterSite(t) ref, err := Add(dir, "calendar", false) if err != nil { t.Fatal(err) } if ref.Source != "hotdog-cms:calendar" || len(ref.AddedCollections) != 1 || ref.Notes == "" { t.Errorf("ref: %+v", ref) } cfg, _ := site.LoadConfig(dir) if cc := cfg.Collections["events"]; !cc.Calendar || cc.Layout != "event" || cc.ListLayout != "events" { t.Errorf("events collection: %+v", cc) } if raw, _ := os.ReadFile(filepath.Join(dir, "site.yaml")); !strings.Contains(string(raw), " # paginate: 20\n events:\n") { t.Errorf("site.yaml's comments moved:\n%s", raw) } os.MkdirAll(filepath.Join(dir, "content", "events"), 0o755) os.WriteFile(filepath.Join(dir, "content", "events", "fair.md"), []byte("---\ntitle: Fair\nstart: 2099-05-01\n---\n"), 0o644) res, err := build.Run(build.Options{SiteDir: dir}) if err != nil { t.Fatal(err) } list, _ := os.ReadFile(filepath.Join(res.Out, "events", "index.html")) for _, want := range []string{"Coming up", `href="/events/fair/"`, "webcal://example.org/events/calendar.ics", "Friday, May 1, 2099"} { if !strings.Contains(string(list), want) { t.Errorf("events page lacks %q", want) } } // Installing again keeps the site's own settings for the collection. if ref, err := Add(dir, "calendar", false); err != nil || len(ref.AddedCollections) != 0 { t.Errorf("reinstall: %v %+v", err, ref) } } func TestMarkupTricks(t *testing.T) { for _, bad := range []string{ `