package console import ( "bytes" "context" "crypto/ed25519" "net/http" "net/http/httptest" "reflect" "strings" "testing" "time" "github.com/carlosframework/platform/internal/hostclaim" "github.com/carlosframework/platform/internal/hostfloor" "github.com/carlosframework/platform/internal/hostwrite" "github.com/carlosframework/platform/internal/release" "github.com/carlosframework/platform/internal/svcrole" ) func resetRequest(t *testing.T, s *Server, url, token, body string) (int, string) { t.Helper() req := httptest.NewRequest(http.MethodPost, url, bytes.NewBufferString(body)) if token != "" { req.Header.Set("Authorization", "Bearer "+token) } req.Header.Set("Content-Type", "application/json") rec := httptest.NewRecorder() s.mux.ServeHTTP(rec, req) return rec.Code, rec.Body.String() } func TestHostWakeResetMountedOperatorGates(t *testing.T) { st := openGatedStore(t) now := time.Now() user, device, err := st.NewCLIAuth("reset", "127.0.0.1", now) if err != nil { t.Fatal(err) } if err = st.ApproveCLIAuth(user, "paul@keymail.dev", now); err != nil { t.Fatal(err) } _, token, _, err := st.PollCLIAuth(device, now) if err != nil { t.Fatal(err) } _, key, err := ed25519.GenerateKey(nil) if err != nil { t.Fatal(err) } store := release.NewDirStore(t.TempDir()) if err := hostfloor.Publish(t.Context(), store, release.RootSigner(key), "bucket", hostfloor.Floor{Min: 1, MinRelease: "v1.0.0"}); err != nil { t.Fatal(err) } s, err := NewServer(Config{Origin: "https://console.example", Keymail: "https://keymail.example", HostClaim: true, Key: key, Bucket: "bucket", Releases: store}, st) if err != nil { t.Fatal(err) } url := "/api/cli/ops/hosts/shop.example.test/resolve" calls := 0 s.cfg.WakeRetryReset = &WakeRetryResetDoor{Claims: release.NewDirStore(t.TempDir()), Authority: func(context.Context) error { calls++; return hostwrite.ErrUnavailable }, StopAttempt: func(context.Context, hostclaim.Observed, hostclaim.WakeRetryAttempt) error { return nil }, NewID: func() string { return "new" }} if status, _ := resetRequest(t, s, url, "", "{}"); status != 401 { t.Fatal(status) } ghostStatus, ghostBody := resetRequest(t, s, "/not-found", token, "{}") status, body := resetRequest(t, s, url, token, "{}") if status != ghostStatus || body != ghostBody || calls != 0 { t.Fatalf("nonoperator %d %q calls=%d", status, body, calls) } if err := st.SetOperator("paul@keymail.dev", true); err != nil { t.Fatal(err) } service := serviceToken(t, st, OpsAccountID, "reset-service", "", svcrole.Publish) if status, _ := resetRequest(t, s, url, service, "{}"); status == 200 || calls != 0 { t.Fatalf("service status=%d calls=%d", status, calls) } if status, _ := resetRequest(t, s, url, token, `{"action":"reset-wake-retry","incarnation":"i","episode":"e","request_id":"r"}`); status != 409 || calls != 1 { t.Fatalf("operator status=%d calls=%d", status, calls) } for _, bad := range []string{ `{"action":"reset-wake-retry","unknown":true}`, `{"action":"reset-wake-retry"} {}`, `{"action":"reset-wake-retry","request_id":"` + strings.Repeat("x", 16<<10) + `"}`, } { if status, _ := resetRequest(t, s, url, token, bad); status != 400 || calls != 1 { t.Fatalf("malformed reset status=%d calls=%d", status, calls) } } // Other resolution actions still use main's dispatcher and authority. for _, action := range []string{"declare-lost", "readmit"} { if status, _ := resetRequest(t, s, url, token, `{"action":"`+action+`"}`); status != 409 || calls != 1 { t.Fatalf("%s reached wake reset: status=%d calls=%d", action, status, calls) } } savedStore := s.cfg.Releases s.cfg.Releases = release.NewDirStore(t.TempDir()) if status, _ := resetRequest(t, s, url, token, `{"action":"reset-wake-retry"}`); status != 503 || calls != 1 { t.Fatalf("missing floor reached reset: status=%d calls=%d", status, calls) } s.cfg.Releases = savedStore s.cfg.HostClaim = false if status, _ := resetRequest(t, s, url, token, "{}"); status != 404 || calls != 1 { t.Fatalf("disabled status=%d calls=%d", status, calls) } } func TestHostWakeResetExactAttemptAndAudit(t *testing.T) { ctx := context.Background() st := release.NewDirStore(t.TempDir()) now := time.Date(2026, 10, 6, 12, 0, 0, 0, time.UTC) host := "shop.bab.oncarlos.com" in := hostclaim.NewHolder{Family: "instance", Key: "apps/bab/jam/instances/" + host + ".json", Account: "bab", App: "jam", Host: host, Region: "ie", RecordRegion: "ie", CreatedAt: now} absent, _ := hostclaim.Read(ctx, st, host) o, err := hostclaim.Update(ctx, st, absent, hostclaim.BeginCreate(in, []byte(`{"schema":1,"host":"shop.bab.oncarlos.com","account":"bab","app":"jam","region":"ie","created_at":"2026-10-06T12:00:00Z"}`), "create", "console", now)) if err != nil { t.Fatal(err) } o, err = hostclaim.Update(ctx, st, o, hostclaim.CompleteCreate("create", "box")) if err != nil { t.Fatal(err) } attempt := hostclaim.WakeRetryAttempt{ID: "a", Box: "box", Enrollment: "enroll", BootID: "boot", ControllerID: "controller", InvocationID: "inv", LaunchInputs: "inputs"} o, err = hostclaim.Update(ctx, st, o, hostclaim.ReserveWakeRetryAttempt(hostclaim.WakeRetryReservation{HolderIncarnation: o.Claim.Holder.Incarnation, EpisodeID: "episode", Attempt: attempt, Now: now})) if err != nil { t.Fatal(err) } request := wakeResetRequest{Action: "reset-wake-retry", Incarnation: o.Claim.Holder.Incarnation, Episode: "episode", RequestID: "request"} door := WakeRetryResetDoor{Claims: st, Authority: func(context.Context) error { return nil }, StopAttempt: func(context.Context, hostclaim.Observed, hostclaim.WakeRetryAttempt) error { return hostwrite.ErrUnavailable }, NewID: func() string { return "replacement" }} if err = door.reset(ctx, host, "operator@example.test", request); err == nil { t.Fatal("unsettled attempt reset") } fresh, _ := hostclaim.Read(ctx, st, host) if fresh.ETag != o.ETag { t.Fatal("failure changed retry") } door.StopAttempt = func(_ context.Context, _ hostclaim.Observed, a hostclaim.WakeRetryAttempt) error { if a != attempt { t.Fatal("wrong attempt stopped") } return nil } if err = door.reset(ctx, host, "operator@example.test", request); err != nil { t.Fatal(err) } fresh, _ = hostclaim.Read(ctx, st, host) retry := fresh.Claim.Holder.WakeRetry if retry.EpisodeID != "replacement" || retry.AttemptsReserved != 0 || retry.ResetRequest != `{"Actor":"operator@example.test","RequestID":"request"}` { t.Fatalf("reset=%+v", retry) } if fresh.Claim.Holder.Incarnation != o.Claim.Holder.Incarnation || fresh.Claim.Holder.Stamp != o.Claim.Holder.Stamp || fresh.Claim.Holder.Session != o.Claim.Holder.Session || !reflect.DeepEqual(fresh.Claim.Op, o.Claim.Op) { t.Fatal("reset changed execution authority") } if err = door.reset(ctx, host, "operator@example.test", request); err == nil { t.Fatal("stale episode reset successor") } }