carlos / platform Public

e2e: the bridge's smoke test (internal/e2e/selfserve, tag e2e)

TestBridgeBoxesReportThemselves builds host.E2E beside a console over one
store, runs one pass and one status tick and finds each box's status block
naming its own box id, instance id and lane, the source's naming the
instance's facts, the serial consumed and the pass and status fault points
fired once per box.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev 70b30cc34558bd73c19296285ecfb67e0dc1bd54 parent 8c93f48
1 file changed, +107 −0
  • internal/e2e/selfserve/bridge_test.go +107 −0
diff --git a/internal/e2e/selfserve/bridge_test.go b/internal/e2e/selfserve/bridge_test.go
new file mode 100644
index 000000000..30ac54e26
--- /dev/null
+++ b/internal/e2e/selfserve/bridge_test.go
@@ -0,0 +1,107 @@
+//go:build e2e
+
+// Package selfserve holds the self-serve region moves' end-to-end
+// witnesses: the console's doors and self-serve pass over host.E2E, the two
+// real boxes of internal/host's offline move tests, in one process over one
+// deployment store. Every file here carries the e2e build tag; the gate runs
+// them with `go test -tags e2e ./internal/e2e/...`.
+package selfserve
+
+import (
+ "context"
+ "path/filepath"
+ "slices"
+ "testing"
+
+ "github.com/carlosframework/platform/internal/console"
+ "github.com/carlosframework/platform/internal/host"
+ "github.com/carlosframework/platform/internal/release"
+ "github.com/carlosframework/platform/internal/status"
+)
+
+// TestBridgeBoxesReportThemselves is the bridge's smoke test: built over the
+// store a console uses, with that console beside it, one pass and one
+// status tick on both boxes leave each box's status block naming its own
+// box id, instance id and lane, and the source's naming the instance's host
+// facts; the serial is consumed; and the pass and status fault points fire
+// once per box.
+func TestBridgeBoxesReportThemselves(t *testing.T) {
+ ctx := context.Background()
+ store := release.NewDirStore(t.TempDir())
+ e := host.NewE2E(t, store)
+
+ dir := t.TempDir()
+ cst, err := console.Open(filepath.Join(dir, "console.db"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ t.Cleanup(func() { cst.Close() })
+ if _, err := console.NewServer(console.Config{Origin: "http://127.0.0.1", Keymail: "http://keymail.invalid",
+ Registry: filepath.Join(dir, "registry.db"), Releases: store, ReleasePub: e.ReleasePub(), Key: e.ReleaseKey(), AppScheme: "http",
+ HomeRegion: host.E2EHomeRegion, SecretsPub: e.SecretsPub()}, cst); err != nil {
+ t.Fatal(err)
+ }
+
+ fired := map[string]int{}
+ for _, label := range []string{host.E2ESourceBox, host.E2ETargetBox} {
+ for _, point := range []string{host.FaultPass, host.FaultStatus} {
+ p := point + label
+ e.Fault(p, func() { fired[p]++ })
+ }
+ }
+ if err := e.Pass(ctx); err != nil {
+ t.Fatal(err)
+ }
+ if err := e.Status(ctx); err != nil {
+ t.Fatal(err)
+ }
+ for p, n := range fired {
+ if n != 1 {
+ t.Errorf("fault point %s fired %d times, want once", p, n)
+ }
+ }
+ if len(fired) != 4 {
+ t.Errorf("fault points fired: %v, want the pass and status points of both boxes", fired)
+ }
+ if err := release.BumpSerial(ctx, store, e.Clock().Now()); err != nil {
+ t.Fatal(err)
+ }
+ if err := e.Serial(ctx); err != nil {
+ t.Fatal(err)
+ }
+
+ boxes, complete, err := status.LoadBoxesComplete(ctx, store)
+ if err != nil || !complete {
+ t.Fatalf("status boxes: complete %v, %v", complete, err)
+ }
+ byLabel := map[string]*status.BoxMetrics{}
+ for _, b := range boxes {
+ byLabel[b.Label] = b.Metrics
+ }
+ for label, lane := range map[string]string{host.E2ESourceBox: "data-uk", host.E2ETargetBox: "data-ie"} {
+ h := e.Box(label)
+ m := byLabel[label]
+ if m == nil {
+ t.Fatalf("no status block for %s (have %v)", label, boxes)
+ }
+ if h.BoxID() == "" || h.InstanceID() == "" {
+ t.Fatalf("%s: box id %q, instance id %q", label, h.BoxID(), h.InstanceID())
+ }
+ if m.BoxID != h.BoxID() || m.InstanceID != h.InstanceID() {
+ t.Errorf("%s's block names box %q instance %q, want %q and %q", label, m.BoxID, m.InstanceID, h.BoxID(), h.InstanceID())
+ }
+ if !slices.Equal(m.Lanes, []string{lane}) {
+ t.Errorf("%s's lanes %v, want [%s]", label, m.Lanes, lane)
+ }
+ }
+ src := byLabel[host.E2ESourceBox]
+ if f, ok := src.Hosts[host.E2EHost]; !ok || f.Incarnation == 0 && f.FilesEntries == 0 {
+ t.Errorf("the source's block has no facts for %s: %+v", host.E2EHost, src.Hosts)
+ }
+ if _, ok := byLabel[host.E2ETargetBox].Hosts[host.E2EHost]; ok {
+ t.Errorf("the target reports facts for %s before any move", host.E2EHost)
+ }
+ if e.Box(host.E2ESourceBox).DataBucket() != "data-ie" || e.Box(host.E2ETargetBox).DataBucket() != "data-uk" {
+ t.Errorf("data buckets %q and %q", e.Box(host.E2ESourceBox).DataBucket(), e.Box(host.E2ETargetBox).DataBucket())
+ }
+}