package rootparticipation import ( "context" "encoding/json" "os" "path/filepath" "sync" "testing" ) type testDisk struct { dir string mu sync.Mutex } func (d *testDisk) Read(n string) ([]byte, error) { return os.ReadFile(filepath.Join(d.dir, n)) } func (d *testDisk) Write(n string, b []byte) error { return os.WriteFile(filepath.Join(d.dir, n), b, 0600) } func (d *testDisk) Remove(n string) error { return os.Remove(filepath.Join(d.dir, n)) } func (d *testDisk) Names() ([]string, error) { es, e := os.ReadDir(d.dir) var out []string for _, v := range es { out = append(out, v.Name()) } return out, e } func (d *testDisk) Close() error { return nil } func (d *testDisk) Lock(context.Context, string) (func(), error) { d.mu.Lock() return d.mu.Unlock, nil } func censusFixture(t *testing.T) Ledger { t.Helper() s := &testDisk{dir: t.TempDir()} b, _ := json.Marshal(Census{Schema: 1, Box: "box", Enrollment: "en", Migration: 1, Revision: 1, Complete: true, LaunchRevision: 1}) if err := s.Write("census.json", b); err != nil { t.Fatal(err) } return Ledger{State: s, Box: "box", Enrollment: "en", VerifyLaunches: func(context.Context, uint64) error { return nil }} } func TestPlan14ExecutionUpdateExclusion(t *testing.T) { for _, first := range []string{"acquisition", "update"} { t.Run(first, func(t *testing.T) { l := censusFixture(t) entered, release := make(chan struct{}), make(chan struct{}) acquire := func() error { return l.Acquire(t.Context(), func() error { close(entered) <-release return l.State.Write("shop.example.json", []byte(`{"Schema":1,"Status":"pending"}`)) }) } update := func() error { return l.BeginUpdate(t.Context(), "update-1") } var winner, loser func() error if first == "acquisition" { winner, loser = acquire, update } else { l.VerifyLaunches = func(context.Context, uint64) error { close(entered); <-release; return nil } winner, loser = update, acquire } done := make(chan error, 1) go func() { done <- winner() }() <-entered other := make(chan error, 1) go func() { other <- loser() }() // Removing every service mirror cannot change the root transaction. data := t.TempDir() if err := os.Mkdir(filepath.Join(data, ".hostexec"), 0700); err != nil { t.Fatal(err) } if err := os.RemoveAll(filepath.Join(data, ".hostexec")); err != nil { t.Fatal(err) } close(release) if err := <-done; err != nil { t.Fatal(err) } if err := <-other; err == nil { t.Fatal("update and acquisition both succeeded") } }) } } func TestPlan14ParticipationRequiresCompleteRootCensus(t *testing.T) { l := censusFixture(t) if err := l.Idle(t.Context()); err != nil { t.Fatal(err) } if err := l.State.Remove("census.json"); err != nil { t.Fatal(err) } if err := l.Idle(t.Context()); err == nil { t.Fatal("missing root census became empty") } if err := l.Acquire(t.Context(), func() error { t.Fatal("unknown history acquired"); return nil }); err == nil { t.Fatal("unknown history admitted") } }