Found raising two machines: `apply <file>` refused the bundle example in this repository with `invalid character '/'`. The bundle strips whole-line comments; apply handed the raw bytes to the parser. So a file this repo ships could be built into a binary and not applied from disk. This is the third instance of one fault. There is already a test here named "what validates is what is applied", written when `mesh-host bundle` said yes and `reconcile` said no about the same artefact — two paths to one thing, disagreeing. Fixing that instance left the shape intact, so it came back somewhere else. So the fix is structural rather than local: `declaration.ParseFileTrusted` is the one way to read a declaration from disk, and the bundle and apply both use it. Comment handling and its test now live in one place, since having them in two is how it came to be done in two. The wire format is untouched — over the link it stays exactly JSON, because a format with a second thing to strip is a format with a second thing to disagree about. Asserted, and confirmed to fail if the link starts stripping.
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 substrate")
|
|
}
|
|
_, 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)
|
|
}
|
|
}
|
|
}
|