The .bnp packers never carried preview.png (the .so-era source archives did implicitly), so any republish silently wiped a theme's registry preview. Both builders now pack an optional root preview.png and a screenshots/ dir (png/jpg/jpeg/webp, <=8MB each, <=12 files, validated), reported in the build/verify summaries. ninja theme screenshot gains --pages/--screenshots-dir to capture NN-<slug>.png per gallery page for the registry gallery. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
153 lines
4.3 KiB
Go
153 lines
4.3 KiB
Go
package bnp
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// presentationFixture extends the codeless fixture with a root preview.png
|
|
// and a screenshots/ dir.
|
|
func presentationFixture(t *testing.T) string {
|
|
t.Helper()
|
|
dir := codelessFixture(t)
|
|
writeFixture(t, dir, map[string]string{
|
|
"preview.png": "not-really-png-but-bytes",
|
|
"screenshots/01-home.png": "home-bytes",
|
|
"screenshots/02-blog.jpg": "blog-bytes",
|
|
"screenshots/03-page.webp": "page-bytes",
|
|
})
|
|
return dir
|
|
}
|
|
|
|
func TestCodelessBuildPacksPresentationMembers(t *testing.T) {
|
|
dir := presentationFixture(t)
|
|
|
|
out := filepath.Join(t.TempDir(), "fixture-theme-0.1.0.bnp")
|
|
res, err := BuildCodeless(context.Background(), BuildOptions{Dir: dir, Output: out})
|
|
if err != nil {
|
|
t.Fatalf("BuildCodeless: %v", err)
|
|
}
|
|
if !res.PreviewIncluded || res.ScreenshotCount != 3 {
|
|
t.Errorf("result preview=%t screenshots=%d; want true/3", res.PreviewIncluded, res.ScreenshotCount)
|
|
}
|
|
found := false
|
|
for _, d := range res.IncludedDirs {
|
|
if d == dirScreenshots {
|
|
found = true
|
|
}
|
|
}
|
|
if !found {
|
|
t.Errorf("IncludedDirs %v missing %q", res.IncludedDirs, dirScreenshots)
|
|
}
|
|
|
|
v, err := Verify(out)
|
|
if err != nil {
|
|
t.Fatalf("Verify: %v", err)
|
|
}
|
|
if !v.HasPreview || v.ScreenshotCount != 3 {
|
|
t.Errorf("verify preview=%t screenshots=%d; want true/3", v.HasPreview, v.ScreenshotCount)
|
|
}
|
|
}
|
|
|
|
func TestCodelessBuildWithoutPresentationMembers(t *testing.T) {
|
|
dir := codelessFixture(t)
|
|
out := filepath.Join(t.TempDir(), "fixture-theme-0.1.0.bnp")
|
|
res, err := BuildCodeless(context.Background(), BuildOptions{Dir: dir, Output: out})
|
|
if err != nil {
|
|
t.Fatalf("BuildCodeless: %v", err)
|
|
}
|
|
if res.PreviewIncluded || res.ScreenshotCount != 0 {
|
|
t.Errorf("result preview=%t screenshots=%d; want false/0", res.PreviewIncluded, res.ScreenshotCount)
|
|
}
|
|
v, err := Verify(out)
|
|
if err != nil {
|
|
t.Fatalf("Verify: %v", err)
|
|
}
|
|
if v.HasPreview || v.ScreenshotCount != 0 {
|
|
t.Errorf("verify preview=%t screenshots=%d; want false/0", v.HasPreview, v.ScreenshotCount)
|
|
}
|
|
}
|
|
|
|
func TestValidateScreenshotsDirRejections(t *testing.T) {
|
|
build := func(t *testing.T, mutate func(dir string)) error {
|
|
t.Helper()
|
|
dir := codelessFixture(t)
|
|
mutate(dir)
|
|
out := filepath.Join(t.TempDir(), "out.bnp")
|
|
_, err := BuildCodeless(context.Background(), BuildOptions{Dir: dir, Output: out})
|
|
return err
|
|
}
|
|
|
|
t.Run("bad extension", func(t *testing.T) {
|
|
err := build(t, func(dir string) {
|
|
writeFixture(t, dir, map[string]string{"screenshots/readme.txt": "nope"})
|
|
})
|
|
if err == nil || !strings.Contains(err.Error(), "unsupported image type") {
|
|
t.Fatalf("err = %v; want unsupported image type", err)
|
|
}
|
|
})
|
|
|
|
t.Run("subdirectory", func(t *testing.T) {
|
|
err := build(t, func(dir string) {
|
|
writeFixture(t, dir, map[string]string{"screenshots/sub/x.png": "nope"})
|
|
})
|
|
if err == nil || !strings.Contains(err.Error(), "subdirectories are not allowed") {
|
|
t.Fatalf("err = %v; want subdirectory rejection", err)
|
|
}
|
|
})
|
|
|
|
t.Run("too many", func(t *testing.T) {
|
|
err := build(t, func(dir string) {
|
|
files := map[string]string{}
|
|
for i := range maxScreenshotFiles + 1 {
|
|
files[fmt.Sprintf("screenshots/%02d.png", i)] = "x"
|
|
}
|
|
writeFixture(t, dir, files)
|
|
})
|
|
if err == nil || !strings.Contains(err.Error(), "gallery cap") {
|
|
t.Fatalf("err = %v; want gallery cap rejection", err)
|
|
}
|
|
})
|
|
|
|
t.Run("oversized image", func(t *testing.T) {
|
|
err := build(t, func(dir string) {
|
|
big := filepath.Join(dir, dirScreenshots, "big.png")
|
|
if mkErr := os.MkdirAll(filepath.Dir(big), 0o755); mkErr != nil {
|
|
t.Fatal(mkErr)
|
|
}
|
|
f, cErr := os.Create(big)
|
|
if cErr != nil {
|
|
t.Fatal(cErr)
|
|
}
|
|
if tErr := f.Truncate(maxScreenshotBytes + 1); tErr != nil {
|
|
t.Fatal(tErr)
|
|
}
|
|
_ = f.Close()
|
|
})
|
|
if err == nil || !strings.Contains(err.Error(), "per-image cap") {
|
|
t.Fatalf("err = %v; want per-image cap rejection", err)
|
|
}
|
|
})
|
|
|
|
t.Run("oversized preview", func(t *testing.T) {
|
|
err := build(t, func(dir string) {
|
|
big := filepath.Join(dir, filePreview)
|
|
f, cErr := os.Create(big)
|
|
if cErr != nil {
|
|
t.Fatal(cErr)
|
|
}
|
|
if tErr := f.Truncate(maxScreenshotBytes + 1); tErr != nil {
|
|
t.Fatal(tErr)
|
|
}
|
|
_ = f.Close()
|
|
})
|
|
if err == nil || !strings.Contains(err.Error(), "per-image cap") {
|
|
t.Fatalf("err = %v; want per-image cap rejection", err)
|
|
}
|
|
})
|
|
}
|