One name per thing, per the HQ glossary: the module/container/image/binary/repo becomes mesh-controller, the seat the-controller, and the store+broker pair the foundation (embedded base bundles, default template and example lock renamed with their go:embed directives). No behaviour change — a pure vocabulary rename. Claude-Session: https://claude.ai/code/session_01D6qtiYU3P9jk3pnAXyAFyx
89 lines
3.6 KiB
Go
89 lines
3.6 KiB
Go
package bundle
|
|
|
|
import (
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/novox/mesh-host/internal/declaration"
|
|
)
|
|
|
|
func TestADefaultBuildCarriesNothingAndSaysSo(t *testing.T) {
|
|
// The important one. A host built without a bundle that applied nothing and reported
|
|
// success would look exactly like a host that raised a first node — and the difference
|
|
// would surface as a mesh that never came up, with nothing to point at.
|
|
if !IsEmpty("arch") {
|
|
t.Fatal("the default build claims to carry a foundation")
|
|
}
|
|
_, err := Load("arch")
|
|
if !errors.Is(err, ErrEmpty) {
|
|
t.Fatalf("an empty bundle did not refuse: %v", err)
|
|
}
|
|
if !strings.Contains(err.Error(), "would look exactly like applying something") {
|
|
t.Errorf("the refusal does not say why it matters: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestABundleWithContentIsParsedByTheSameParserTheLinkWillUse(t *testing.T) {
|
|
// A bundle that reaches a machine and is then refused by the host carrying it would be a
|
|
// build-time mistake found at the worst possible moment.
|
|
//
|
|
// Comment handling itself is asserted where it now lives, in `declaration`. It was here, and
|
|
// having it in two places is how it came to be *done* in two places.
|
|
real := []byte(`// pinned
|
|
{"declaration":1,"resources":[{"id":"d","type":"directory","path":"/etc/mesh"}]}`)
|
|
parsed, err := declaration.ParseFileTrusted(real)
|
|
if err != nil {
|
|
t.Fatalf("an annotated bundle was refused: %v", err)
|
|
}
|
|
if len(parsed.Resources) != 1 {
|
|
t.Fatalf("got %d resources", len(parsed.Resources))
|
|
}
|
|
}
|
|
|
|
func TestWhatValidatesIsWhatIsApplied(t *testing.T) {
|
|
// Found on a real machine. `mesh-host bundle` validated the carried bundle through Load,
|
|
// which strips comments; `reconcile` handed the RAW bytes to the parser, which does not.
|
|
// So the command whose whole job is to check the bundle said yes, and the command that
|
|
// uses it said no — two paths to one artefact, disagreeing.
|
|
//
|
|
// There is now one path. This asserts the property that made the bug possible cannot
|
|
// return: whatever Load accepts is what gets applied, byte for byte.
|
|
annotated := []byte("// a comment\n" + `{"declaration":1,"resources":[{"id":"d","type":"directory","path":"/etc/mesh"}]}`)
|
|
|
|
if _, err := declaration.ParseFileTrusted(annotated); err != nil {
|
|
t.Fatalf("an annotated bundle was refused: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestEverySystemHasABundleAndAndroidsRefuses(t *testing.T) {
|
|
// The bundle's contents are per system even though its mechanism is not (novox/hq ADR
|
|
// 0060), so a host must find one built for it — and a host built for a system with no
|
|
// bundle at all must say that rather than behave like an empty one.
|
|
for _, system := range []string{"arch", "alpine", "android"} {
|
|
if _, err := Load(system); err == nil {
|
|
t.Errorf("%s: a placeholder bundle loaded as if it had contents", system)
|
|
} else if !errors.Is(err, ErrEmpty) {
|
|
t.Errorf("%s: refused for the wrong reason: %v", system, err)
|
|
}
|
|
}
|
|
|
|
if _, err := Load("debian"); err == nil {
|
|
t.Error("a bundle was loaded for a system nobody has built")
|
|
} else if errors.Is(err, ErrEmpty) {
|
|
t.Error("an unbuilt system was reported as an empty bundle; those are different things")
|
|
}
|
|
}
|
|
|
|
func TestAndroidsBundleSaysWhyThereIsNone(t *testing.T) {
|
|
// Not a placeholder waiting to be filled in. An android host implements neither `package`
|
|
// nor `container` nor `service`, so every step of the bootstrap is a shape it does not
|
|
// have — a partial host can JOIN a mesh and cannot BE the first node.
|
|
text := string(Raw("android"))
|
|
for _, want := range []string{"cannot raise a mesh", "JOIN", "first node"} {
|
|
if !strings.Contains(text, want) {
|
|
t.Errorf("the android bundle does not explain itself; missing %q", want)
|
|
}
|
|
}
|
|
}
|