release: v1.32.0 — hardening and scalability (#580)

* release: prepare v1.32.0

* test: stabilize water capture re-entry
This commit is contained in:
Marc-André Bombeck authored and GitHub committed 2026-07-22 04:14:15 +02:00
1 parent b01822be48
commit 62eb431d66
372 files changed
+25178 -8561

No files matched your search

@@ -28,14 +28,14 @@ vi.mock("@/lib/crypto", () => ({
decrypt: vi.fn((x: string) => x),
}));
vi.mock("@/lib/fitbit/sync", () => ({
vi.mock("@/lib/fitbit/sync-core", () => ({
getValidToken: vi.fn(),
}));
import { POST } from "../route";
import { prisma } from "@/lib/db";
import { checkRateLimit } from "@/lib/rate-limit";
import { getValidToken } from "@/lib/fitbit/sync";
import { getValidToken } from "@/lib/fitbit/sync-core";
interface ApiErrorEnvelope {
data: null;
@@ -4,7 +4,7 @@ import { apiSuccess, apiError } from "@/lib/api-response";
import { checkRateLimit } from "@/lib/rate-limit";
import { annotate } from "@/lib/logging/context";
import { prisma } from "@/lib/db";
import { getValidToken } from "@/lib/fitbit/sync";
import { getValidToken } from "@/lib/fitbit/sync-core";
import { FITBIT_API_BASE } from "@/lib/fitbit/client";
import { safeFetch, SafeFetchError } from "@/lib/safe-fetch";
@@ -4,7 +4,7 @@ import { apiSuccess, apiError } from "@/lib/api-response";
import { checkRateLimit } from "@/lib/rate-limit";
import { annotate } from "@/lib/logging/context";
import { prisma } from "@/lib/db";
import { getValidToken } from "@/lib/google-health/sync";
import { getValidToken } from "@/lib/google-health/sync-core";
import {
GOOGLE_HEALTH_ACTIVITY_PAGE_SIZE,
GOOGLE_HEALTH_API_BASE,
@@ -52,6 +52,7 @@ import { GET } from "../route";
import { prisma } from "@/lib/db";
const userFind = prisma.user.findUnique as ReturnType<typeof vi.fn>;
const whoopFind = prisma.whoopConnection.findUnique as ReturnType<typeof vi.fn>;
type Entry = {
integration: string;
@@ -112,3 +113,29 @@ describe("/api/integrations/status — Polar/Oura fold (04-M2)", () => {
expect(polar.configured).toBe(false);
});
});
describe("/api/integrations/status — WHOOP identity ownership", () => {
beforeEach(() => {
vi.clearAllMocks();
userFind.mockResolvedValue({
whoopClientIdEncrypted: "id",
whoopClientSecretEncrypted: "secret",
});
});
it("reports an identity-cleared duplicate row as disconnected", async () => {
whoopFind.mockResolvedValue({
whoopUserId: null,
tokenExpiresAt: new Date(Date.now() + 3_600_000),
lastSyncedAt: null,
createdAt: new Date("2026-06-01T00:00:00Z"),
backfillCompletedAt: null,
});
const entries = await fetchEntries();
const whoop = entries.find((entry) => entry.integration === "whoop");
expect(whoop?.connected).toBe(false);
expect(whoop?.configured).toBe(true);
});
});
+17 -7
View File
@@ -110,6 +110,7 @@ export const GET = apiHandler(async () => {
where: { userId: user.id },
select: {
tokenExpiresAt: true,
whoopUserId: true,
lastSyncedAt: true,
createdAt: true,
backfillCompletedAt: true,
@@ -152,6 +153,7 @@ export const GET = apiHandler(async () => {
);
const now = Date.now();
const whoopConnected = !!whoopConn?.whoopUserId;
return apiSuccess({
threshold: getPersistentFailureThreshold(),
@@ -196,14 +198,22 @@ export const GET = apiHandler(async () => {
configured:
!!dbUser?.whoopClientIdEncrypted &&
!!dbUser?.whoopClientSecretEncrypted,
connected: !!whoopConn,
connectedAt: whoopConn?.createdAt?.toISOString() ?? null,
legacyLastSyncedAt: whoopConn?.lastSyncedAt?.toISOString() ?? null,
tokenExpiresAt: whoopConn?.tokenExpiresAt?.toISOString() ?? null,
tokenExpired: whoopConn
? whoopConn.tokenExpiresAt.getTime() <= now
connected: whoopConnected,
connectedAt: whoopConnected
? (whoopConn?.createdAt.toISOString() ?? null)
: null,
legacyLastSyncedAt: whoopConnected
? (whoopConn?.lastSyncedAt?.toISOString() ?? null)
: null,
tokenExpiresAt: whoopConnected
? (whoopConn?.tokenExpiresAt.toISOString() ?? null)
: null,
tokenExpired: whoopConnected
? (whoopConn?.tokenExpiresAt.getTime() ?? 0) <= now
: null,
backfillCompleted: whoopConnected
? !!whoopConn?.backfillCompletedAt
: null,
backfillCompleted: whoopConn ? !!whoopConn.backfillCompletedAt : null,
metricFreshness: metricFreshness.whoop ?? [],
} satisfies IntegrationViewModel & WhoopExtras,
{
@@ -28,14 +28,14 @@ vi.mock("@/lib/crypto", () => ({
decrypt: vi.fn((x: string) => x),
}));
vi.mock("@/lib/whoop/sync", () => ({
vi.mock("@/lib/whoop/sync-core", () => ({
getValidToken: vi.fn(),
}));
import { POST } from "../route";
import { prisma } from "@/lib/db";
import { checkRateLimit } from "@/lib/rate-limit";
import { getValidToken } from "@/lib/whoop/sync";
import { getValidToken } from "@/lib/whoop/sync-core";
interface ApiErrorEnvelope {
data: null;
+1 -1
View File
@@ -4,7 +4,7 @@ import { apiSuccess, apiError } from "@/lib/api-response";
import { checkRateLimit } from "@/lib/rate-limit";
import { annotate } from "@/lib/logging/context";
import { prisma } from "@/lib/db";
import { getValidToken } from "@/lib/whoop/sync";
import { getValidToken } from "@/lib/whoop/sync-core";
import { safeFetch, SafeFetchError } from "@/lib/safe-fetch";
export const dynamic = "force-dynamic";