package hostops import ( "context" "crypto/ed25519" "encoding/json" "testing" "time" "github.com/carlosframework/platform/internal/hostclaim" "github.com/carlosframework/platform/internal/provision" "github.com/carlosframework/platform/internal/release" ) func TestProductionSidecarWakeRegistersAndSettlesBeforeDelete(t *testing.T) { ctx := context.Background() store := release.NewDirStore(t.TempDir()) at := time.Date(2026, 10, 7, 0, 0, 0, 0, time.UTC) host := provision.SidecarHost("bot", "app", "acct") record := provision.SidecarRecord{Schema: 1, Name: "bot", Host: host, Account: "acct", App: "app", CreatedAt: at} raw, _ := json.Marshal(record) key := provision.SidecarRecordKey("acct", "app", "bot") if _, err := store.Put(ctx, key, raw, ""); err != nil { t.Fatal(err) } nh := hostclaim.NewHolder{Family: "sidecar", Key: key, Account: "acct", App: "app", Name: "bot", Host: host, CreatedAt: at, Region: "ie"} if _, err := hostclaim.Update(ctx, store, hostclaim.Observed{Host: host, Absent: true}, hostclaim.Adopt(nh, "box", nil, nil, "adopt", func() string { return "unused" })); err != nil { t.Fatal(err) } admission := AppAdmission{V: 1, AppID: "app-id", Revision: 1, Epoch: 1, State: "open", Registrations: map[string]Registration{}, Effects: map[string]Effect{}} raw, _ = json.Marshal(admission) if _, err := store.Put(ctx, "apps/acct/app/admission.json", raw, ""); err != nil { t.Fatal(err) } pub, _, err := ed25519.GenerateKey(nil) if err != nil { t.Fatal(err) } w, err := NewControlWriter(store, pub, func() time.Time { return at }) if err != nil { t.Fatal(err) } authority := ProductionAuthority{Store: store, Control: w, ConsoleID: "console", Enrollment: "enrollment", Now: func() time.Time { return at }} if err := authority.TouchSidecarWake(ctx, "acct", "app", "bot", "app-id", "operator", "wake"); err != nil { t.Fatal(err) } raw, etag, err := store.Get(ctx, release.SidecarWakeKey("acct", "app", "bot")) if err != nil { t.Fatal(err) } var signal release.SidecarWake if err = json.Unmarshal(raw, &signal); err != nil { t.Fatal(err) } observed, err := ReadAdmission(ctx, store, "acct", "app", "app-id") if err != nil { t.Fatal(err) } reg, ok := observed.Value.Registrations[signal.RequestID] effect, settled := observed.Value.Effects[signal.RequestID] if !ok || reg.Kind != "wake" || reg.Incarnation != signal.Incarnation || signal.AppID != "app-id" || !settled || effect.State != "complete" || effect.ResultReference != signal.RequestID { t.Fatalf("missing admission/effect: %+v %+v %+v", signal, reg, effect) } if effect.Inputs["admission_epoch"] != "1" || effect.Inputs["created_at"] != at.Format(time.RFC3339Nano) || effect.Inputs["key"] != release.SidecarWakeKey("acct", "app", "bot") { t.Fatalf("wake effect lost durable identity: %+v", effect) } if _, err = w.BeginExclusive(ctx, "acct", "app", "app-id", AppOperation{ID: "delete", Kind: OpDeleting, AppID: "app-id"}, RegistrationOwner{Console: "console", Enrollment: "enrollment", Controller: "controller"}); err != nil { t.Fatal(err) } if err = authority.TouchSidecarWake(ctx, "acct", "app", "bot", "app-id", "operator", "late"); err == nil { t.Fatal("wake crossed closed app gate") } _, after, err := store.Get(ctx, release.SidecarWakeKey("acct", "app", "bot")) if err != nil || after != etag { t.Fatalf("rejected wake mutated signal: %s %v", after, err) } }