package host import ( "context" "crypto/sha256" "encoding/hex" "encoding/json" "testing" "github.com/carlosframework/platform/internal/hostbox" "github.com/carlosframework/platform/internal/hostclaim" "github.com/carlosframework/platform/internal/hostguard" "github.com/carlosframework/platform/internal/provision" "github.com/carlosframework/platform/internal/registry" ) func TestPlan14InventoryRequirementsRegistration(t *testing.T) { t.Parallel() for _, kind := range []string{"stateful", "service"} { t.Run(kind, func(t *testing.T) { f, root, q := plan14InstallRegistration(t, hostclaim.KindServing, "") // Use the claim's canonical account/app, independently of fixture defaults. h := f.read().Claim.Holder rec, _, err := provision.GetRecordETag(t.Context(), f.claims, h.Account, h.App, q.Target.Host) if err != nil { t.Fatal(err) } if kind == "service" { rec.Kind, rec.Addr = "service", "127.0.0.1:8080" plan14PutRecord(t, f.claims, *rec) _, q.RecordETag, err = f.claims.Get(t.Context(), q.RecordKey) if err != nil { t.Fatal(err) } } _, initial := hostbox.RemoteJournals(func(ctx context.Context, prepared hostbox.JournalRequest) (hostbox.JournalResponse, error) { q = prepared return root.commitRootInventory(ctx, prepared) }) if _, err := initial.WriteExpected(t.Context(), q.Inventory, hostbox.InventoryExpectation{RecordKey: q.RecordKey, RecordETag: q.RecordETag, Target: q.Target}); err != nil { t.Fatal(err) } views, err := root.Stop.Coordinator.Guard.Root.GenerationInstallations(t.Context()) if err != nil || len(views) != 1 { t.Fatal(views, err) } want := hostguard.InstallationArtifacts{Schema: 1, StateDirectory: kind == "stateful"} if views[0].Installation.Artifacts != want { t.Fatalf("root requirements: %+v want %+v", views[0].Installation.Artifacts, want) } if _, err := root.commitRootInventory(t.Context(), q); err != nil { t.Fatal("replay", err) } rec.Kind, rec.Addr = "service", "127.0.0.1:8080" plan14PutRecord(t, f.claims, *rec) _, etag, err := f.claims.Get(t.Context(), q.RecordKey) if err != nil { t.Fatal(err) } _, client := hostbox.RemoteJournals(root.commitRootInventory) if _, err := client.WriteExpected(t.Context(), q.Inventory, hostbox.InventoryExpectation{Revision: 1, RecordKey: q.RecordKey, RecordETag: etag, Target: q.Target}); err != nil { t.Fatal(err) } views, err = root.Stop.Coordinator.Guard.Root.GenerationInstallations(t.Context()) if err != nil || views[0].Installation.Artifacts != want { t.Fatalf("shape change erased history: %+v %v", views, err) } }) } } func TestPlan14InventoryRequirementsUnknownAndRatchet(t *testing.T) { prior := hostguard.RootInstallation{} row := registry.Route{DBPath: "/data/instances/tenant.example/state.db"} in := hostbox.StopInventory{Host: "tenant.example", Entries: []hostbox.StopInventoryEntry{{Kind: "timer", RelativePath: "tenant.example/backup"}}} if got, err := rootInventoryArtifacts(prior, row, in, 2); err != nil || got != (hostguard.InstallationArtifacts{}) { t.Fatalf("unknown history initialized: %+v %v", got, err) } prior.Artifacts = hostguard.InstallationArtifacts{Schema: 1, StateDirectory: true, Timers: "backup"} got, err := rootInventoryArtifacts(prior, registry.Route{}, hostbox.StopInventory{Host: in.Host}, 2) if err != nil || got != prior.Artifacts { t.Fatal("lost old requirements", got, err) } for _, bad := range []hostguard.InstallationArtifacts{{}, {Schema: 2}, {Schema: 1, Timers: "backup\nbackup"}, {Schema: 1, Timers: "z\na"}, {Schema: 1, Timers: "../other"}, {Schema: 1, Timers: "backup\n"}} { if _, err := inventoryTimerNames(bad); err == nil { t.Fatalf("accepted malformed requirements: %+v", bad) } } } func TestPlan14InventoryRequirementsHistoryIdentity(t *testing.T) { in := hostguard.RootInstallation{Host: "tenant.example", Incarnation: "inc", Stamp: 3, Account: "bab", App: "shop", Record: "record", Box: "box", Enrollment: "en", Migration: 1} // This is the pre-E37 wire shape, including its field order. raw := []byte(`{"Registration":{"Op":"","Kind":"","Phase":"","RowID":"","Session":0},"Empty":false,"Host":"tenant.example","Incarnation":"inc","Account":"bab","App":"shop","Record":"record","Box":"box","Enrollment":"en","Stamp":3,"Migration":1,"InventoryRevision":0}`) var check hostguard.RootInstallation if err := json.Unmarshal(raw, &check); err != nil || check != in { t.Fatal(check, err) } sum := sha256.Sum256(raw) want := hex.EncodeToString(sum[:]) for _, a := range []hostguard.InstallationArtifacts{{}, {Schema: 1, StateDirectory: true, Timers: "backup"}} { in.Artifacts = a if got := rootHistoryInstallation(in); got != want { t.Fatalf("installation identity changed: %s want %s", got, want) } } }