package host import ( "bytes" "context" "crypto/ed25519" "encoding/json" "errors" "os" "path/filepath" "testing" "github.com/carlosframework/platform/internal/moves" "github.com/carlosframework/platform/internal/release" ) func TestPlan14FloorRiseWithoutPlatformUpdate(t *testing.T) { for _, prev := range []string{"none", "v2.0.0", "v1.0.0"} { t.Run(prev, func(t *testing.T) { id := rootFloorIdentity{Deployment: "deploy", Box: "box", Enrollment: "en"} f := rootMovesFloors{private: &rollbackMemory{files: map[string][]byte{}}, public: &rollbackMemory{files: map[string][]byte{}}, identity: id} a := moves.Floor{Min: 1, MinRelease: "v1.0.0"} b := moves.Floor{Min: 2, MinRelease: "v2.0.0"} if err := f.reconstruct(t.Context(), rootFloorReconstruction{Identity: id, Floor: &a, Source: "history", Digest: floorTestDigest, Authorization: floorTestAuthorization(t, f)}); err != nil { t.Fatal(err) } store := release.NewDirStore(t.TempDir()) raw, _ := json.Marshal(b) if _, err := store.Put(t.Context(), moves.FloorKey, raw, ""); err != nil { t.Fatal(err) } mirror := t.TempDir() if _, err := syncLocalFloor(t.Context(), store, mirror); err != nil { t.Fatal(err) } if p, err := f.projection(); err != nil || p.Floor.MinRelease != a.MinRelease { t.Fatal("mirror acknowledged B", p, err) } reads := 0 read := func(context.Context) (*moves.Floor, error) { reads++; return &b, nil } binary := func(path string) (rootFloorBinary, error) { if path == "prev" && prev == "none" { return rootFloorBinary{}, os.ErrNotExist } v := "v2.0.0" if path == "prev" { v = prev } return rootFloorBinary{Version: v, Protocol: 2, Digest: floorTestDigest}, nil } judge := rootFloorJudge{floors: f, remote: read, legacy: func() ([]*moves.Floor, error) { return rootFloorLegacy(mirror) }, binary: binary, current: "current", prev: "prev", idle: func(context.Context) error { return nil }} if err := judge.refresh(t.Context()); err != nil { t.Fatal(err) } if reads != 1 { t.Fatal("no-prev path skipped authoritative GET") } p, err := f.projection() if err != nil || p.Floor == nil || *p.Floor != b { t.Fatal("root refresh", p, err) } ad, err := f.advertisement() if err != nil { t.Fatal(err) } if ad.PrevRelease != prev { t.Fatal("prev identity", ad) } if (ad.PrevAllowedFor == b.MinRelease) != (prev == "v2.0.0") { t.Fatal("verdict", ad) } if err := os.RemoveAll(mirror); err != nil { t.Fatal(err) } if again, err := f.advertisement(); err != nil || again != ad { t.Fatal("mirror changed advertisement", again, err) } // A failed persistence cannot publish the new generation's acknowledgement. n := 0 broken := f broken.private = &floorFaultStore{Store: f.private, n: &n, fail: 1} judge.floors = broken if judge.refresh(t.Context()) == nil { t.Fatal("failed floor sync accepted") } if _, err := f.advertisement(); err == nil { t.Fatal("failed sync left acceptance") } }) } } func TestPlan14RootFloorRecoveryOffline(t *testing.T) { for _, entry := range []string{"recover", "rollback"} { t.Run(entry, func(t *testing.T) { f, _ := applyHappy(t) j := f.brokenBox("v2.0.0", "v1.0.0", "v1.0.0") journal := filepath.Join(systemRootDir(f.a.optDir), "journal.json") before, _ := os.ReadFile(systemBinaryPath(f.a.optDir)) original, _ := os.ReadFile(journal) unavailable := errors.New("provider unavailable") reads := 0 f.a.rootMoves = func() (rootFloorIdentity, bool, error) { return rootFloorIdentity{Deployment: "deploy", Box: "box", Enrollment: "en"}, true, nil } f.a.openStore = func(context.Context) (release.Store, ed25519.PublicKey, error) { reads++; return nil, nil, unavailable } var err error if entry == "recover" { _, err = f.a.recoverIfNeeded(t.Context()) } else { _, err = f.a.restoreFiles(t.Context(), &j) } if err == nil || reads != 0 { t.Fatalf("recovery used network or accepted missing signed identity: reads=%d err=%v", reads, err) } after, _ := os.ReadFile(systemBinaryPath(f.a.optDir)) retained, _ := os.ReadFile(journal) if !bytes.Equal(before, after) || !bytes.Equal(original, retained) { t.Fatal("failed refresh changed executable or journal") } }) } }