Name the decisions these tests defend, and check the bundle at all
From auditing the decision records: of 28, only 12 were named by any test, so "which decisions are defended" could not be answered without reading everything. ADR 0017 says a test names the decision it defends — that rule was itself unenforced. Most of the gap was citation, not coverage. Drift detection was tested in several places without naming ADR 0011; the archive refusal without naming 0012; forged declarations without naming 0002. Named now, so the question is answerable by grep. The bundle was the real gap: nothing tested substrate-first-node.lock at all. It is what a machine becomes when there is no mesh to ask — the one declaration applied with nothing to verify it against — and it was edited by hand and read by nothing but a running host. Two tests now assert what it carries: exactly postgres, lavinmq and the control plane. That defends ADR 0028, which removed the object store from the substrate after it had been a member for months on the strength of "it cannot grant itself a bucket" — true, and the answer to only half the test. Nothing counted what the bundle held. Fault-injected, and the first attempt did not bite: the injection landed on a comment line, which stripComments discards. Injecting into the image field fails as it should.
This commit is contained in:
@@ -0,0 +1,69 @@
|
|||||||
|
// Package examples checks the bundles shipped in this directory.
|
||||||
|
//
|
||||||
|
// **Nothing checked them before.** `substrate-first-node.lock` is what a machine becomes when
|
||||||
|
// there is no mesh to ask — the one declaration applied with nothing to verify it against — and
|
||||||
|
// it was edited by hand and read by nobody but a running host.
|
||||||
|
package examples
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/novox/mesh-host/internal/declaration"
|
||||||
|
)
|
||||||
|
|
||||||
|
func bundle(t *testing.T) *declaration.Declaration {
|
||||||
|
t.Helper()
|
||||||
|
raw, err := os.ReadFile("substrate-first-node.lock")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
d, err := declaration.ParseFileTrusted(raw)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("the bundle a first node applies does not parse: %v", err)
|
||||||
|
}
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defends novox/hq ADR 0028: the substrate supplies the control plane and nothing else.
|
||||||
|
//
|
||||||
|
// The object store was substrate for months on the strength of "it cannot grant itself a bucket",
|
||||||
|
// which answers half the test. The control plane never needed one, and nothing noticed because
|
||||||
|
// nothing counted what the bundle holds.
|
||||||
|
func TestTheBundleCarriesTheSubstrateAndTheControlPlaneAndNothingElse(t *testing.T) {
|
||||||
|
var images []string
|
||||||
|
for _, r := range bundle(t).Resources {
|
||||||
|
c, ok := r.(*declaration.Container)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// The registry the images come from is the lab's, and is not what this asserts.
|
||||||
|
name := c.Image
|
||||||
|
if i := strings.LastIndex(name, "/"); i >= 0 {
|
||||||
|
name = name[i+1:]
|
||||||
|
}
|
||||||
|
images = append(images, strings.SplitN(name, "@", 2)[0])
|
||||||
|
}
|
||||||
|
sort.Strings(images)
|
||||||
|
|
||||||
|
want := []string{"lavinmq", "mesh-control", "postgres"}
|
||||||
|
if strings.Join(images, ",") != strings.Join(want, ",") {
|
||||||
|
t.Fatalf("the bundle carries %v; expected exactly %v.\n\n"+
|
||||||
|
"Adding one is a change to what every first node becomes, and to ADR 0006's "+
|
||||||
|
"membership — take it deliberately, not by editing a lock file.", images, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The control plane is in the bundle, and the design overlooked it once by reasoning about
|
||||||
|
// substrate services rather than counting containers (novox/hq 03-DESIGN/01-to-be/07).
|
||||||
|
func TestTheControlPlaneIsCarriedToo(t *testing.T) {
|
||||||
|
for _, r := range bundle(t).Resources {
|
||||||
|
if c, ok := r.(*declaration.Container); ok && strings.Contains(c.Image, "mesh-control") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.Fatal("nothing in the bundle starts the control plane, so the machine would raise a " +
|
||||||
|
"substrate and stop")
|
||||||
|
}
|
||||||
@@ -57,6 +57,10 @@ func TestApplyingTwiceChangesNothingTheSecondTime(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Defends novox/hq ADR 0011: a managed file is generated onto a node and never edited there.
|
||||||
|
//
|
||||||
|
// Not by overwriting silently — by noticing. An edit that vanishes without a word is how somebody
|
||||||
|
// spends an afternoon re-fixing a bug they already fixed.
|
||||||
func TestADriftedMachineIsReturned(t *testing.T) {
|
func TestADriftedMachineIsReturned(t *testing.T) {
|
||||||
// The other half of idempotence, and the half that matters: converging is not "do nothing
|
// The other half of idempotence, and the half that matters: converging is not "do nothing
|
||||||
// if the state file says it was done". The machine is read, not the record.
|
// if the state file says it was done". The machine is read, not the record.
|
||||||
|
|||||||
@@ -204,6 +204,11 @@ func TestAnUnpackedArchiveIsNotFetchedAgainForNothing(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Defends novox/hq ADR 0012: the mesh creates no symlinks — a derived file is a copy.
|
||||||
|
//
|
||||||
|
// The archive is the one path where a symlink could arrive without anybody declaring it, which is
|
||||||
|
// why the refusal lives here. ADR 0012 was earned by production data loss through a symlink
|
||||||
|
// resolved inside a container volume path.
|
||||||
func TestAnArchiveWithSomethingThatIsNotAFileIsRefused(t *testing.T) {
|
func TestAnArchiveWithSomethingThatIsNotAFileIsRefused(t *testing.T) {
|
||||||
// A theme needing a symlink would otherwise arrive silently incomplete, and a device node in
|
// A theme needing a symlink would otherwise arrive silently incomplete, and a device node in
|
||||||
// an archive is not something to unpack quietly onto a machine.
|
// an archive is not something to unpack quietly onto a machine.
|
||||||
|
|||||||
@@ -44,6 +44,11 @@ func TestTheMeshsOwnDeclarationIsApplied(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Defends novox/hq ADR 0002: everything reaching a node arrives over the broker — and therefore
|
||||||
|
// ADR 0004's consequence, that the broker is not trusted to say who is speaking.
|
||||||
|
//
|
||||||
|
// A transport nobody authenticates per-message would let whatever holds the connection attribute
|
||||||
|
// a declaration to any node it liked.
|
||||||
func TestAForgedDeclarationIsNeverApplied(t *testing.T) {
|
func TestAForgedDeclarationIsNeverApplied(t *testing.T) {
|
||||||
// The check that stands between "the mesh changes this machine" and "anybody does". The host
|
// The check that stands between "the mesh changes this machine" and "anybody does". The host
|
||||||
// applies whatever the link delivers, so a forged declaration is the whole machine.
|
// applies whatever the link delivers, so a forged declaration is the whole machine.
|
||||||
|
|||||||
Reference in New Issue
Block a user