The ninja developer CLI moves out of block/core into its own module git.dev.alexdunmow.com/block/cli. Pins block/core@v0.18.2 for abi/v1, the plugin.mod parser, and DESCRIBE guest builds; vendors its own orchestrator registry client (internal/api, generated from a vendored plugin_registry.proto) since it cannot import block/core/internal. Produces byte-identical v1 artifacts (verified: art-deco codeless SHA256-identical to the pre-move build). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
80 lines
2.9 KiB
Go
80 lines
2.9 KiB
Go
package bnp
|
|
|
|
import (
|
|
"testing"
|
|
|
|
abiv1 "git.dev.alexdunmow.com/block/core/abi/v1"
|
|
)
|
|
|
|
// TestApplyManifestYAMLSupplementsWithoutWiping proves the WO-WZ-021 mixed-form
|
|
// builder semantics: folding manifest.yaml onto a manifest that already carries
|
|
// guest DESCRIBE output SUPPLEMENTS the declarative surfaces and OVERRIDES only
|
|
// the keys it actually declares — an undeclared scalar key never wipes a
|
|
// guest-populated field.
|
|
func TestApplyManifestYAMLSupplementsWithoutWiping(t *testing.T) {
|
|
dir := t.TempDir()
|
|
writeFixture(t, dir, map[string]string{
|
|
"manifest.yaml": "" +
|
|
"bundled_fonts: fonts.json\n" +
|
|
"page_templates:\n" +
|
|
" - {system: blog, key: listing, title: Listing}\n" +
|
|
"email_wrappers: [newsletter]\n",
|
|
"fonts.json": `{"fonts":["Inter"]}`,
|
|
"templates/blog/listing.ninjatpl": `<main>{{ title }}</main>`,
|
|
"templates/email/newsletter.ninjatpl": `<table>{{ body|safe }}</table>`,
|
|
})
|
|
|
|
// Simulate a guest DESCRIBE result: theme_presets set by the guest, plus a
|
|
// guest-rendered page template the manifest.yaml does NOT list.
|
|
m := &abiv1.PluginManifest{
|
|
AbiVersion: 1,
|
|
Name: "fixture-mixed",
|
|
Version: "0.1.0",
|
|
ThemePresets: []byte(`{"guest":true}`),
|
|
PageTemplates: []*abiv1.PageTemplateMeta{
|
|
{SystemKey: "blog", Key: "guestonly", Title: "Guest Only"},
|
|
},
|
|
}
|
|
|
|
if err := applyManifestYAML(dir, m); err != nil {
|
|
t.Fatalf("applyManifestYAML: %v", err)
|
|
}
|
|
|
|
// Undeclared theme_presets must be PRESERVED, not wiped to nil.
|
|
if string(m.ThemePresets) != `{"guest":true}` {
|
|
t.Errorf("theme_presets wiped/altered: %q", string(m.ThemePresets))
|
|
}
|
|
// Declared bundled_fonts folded in.
|
|
if string(m.BundledFonts) != `{"fonts":["Inter"]}` {
|
|
t.Errorf("bundled_fonts = %q", string(m.BundledFonts))
|
|
}
|
|
// Declared page template appended alongside the guest one (2 total).
|
|
if len(m.PageTemplates) != 2 {
|
|
t.Fatalf("page templates = %d, want 2 (guest + declarative)", len(m.PageTemplates))
|
|
}
|
|
// Declared email wrapper folded in.
|
|
if len(m.EmailWrapperSystemKeys) != 1 || m.EmailWrapperSystemKeys[0] != "newsletter" {
|
|
t.Errorf("email wrappers = %v", m.EmailWrapperSystemKeys)
|
|
}
|
|
}
|
|
|
|
// TestWasmBuildFoldsManifestYAML end-to-end: a repo with Go source AND a
|
|
// manifest.yaml builds a (non-codeless) wasm artifact whose manifest.pb carries
|
|
// the folded declarative surfaces. The absence of a manifest.yaml is a no-op —
|
|
// covered by every existing wasm build test.
|
|
func TestApplyManifestYAMLNoFileIsNoop(t *testing.T) {
|
|
dir := t.TempDir() // no manifest.yaml
|
|
m := &abiv1.PluginManifest{
|
|
AbiVersion: 1,
|
|
Name: "fixture-pure",
|
|
Version: "0.1.0",
|
|
ThemePresets: []byte(`{"guest":true}`),
|
|
}
|
|
if err := applyManifestYAML(dir, m); err != nil {
|
|
t.Fatalf("applyManifestYAML (no file): %v", err)
|
|
}
|
|
if string(m.ThemePresets) != `{"guest":true}` {
|
|
t.Errorf("no-manifest.yaml build mutated the manifest: %q", string(m.ThemePresets))
|
|
}
|
|
}
|