Fix intensity goal defaults for PocketBase 0 values
All checks were successful
Deploy / deploy (push) Successful in 2m39s

PocketBase number fields default to 0, not null. Using ?? (nullish
coalescing) caused 0 to be preserved instead of using the default value.
Changed to || so 0 is treated as falsy and falls back to defaults.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-17 07:14:39 +00:00
parent 8956e04eca
commit ed14aea0ea
2 changed files with 81 additions and 5 deletions

View File

@@ -4,9 +4,11 @@ import { describe, expect, it, vi } from "vitest";
import {
createPocketBaseClient,
DEFAULT_INTENSITY_GOALS,
getCurrentUser,
isAuthenticated,
loadAuthFromCookies,
mapRecordToUser,
} from "./pocketbase";
describe("isAuthenticated", () => {
@@ -221,3 +223,76 @@ describe("createPocketBaseClient", () => {
expect(client.authStore).toBeDefined();
});
});
describe("mapRecordToUser intensity goal defaults", () => {
const createMockRecord = (overrides: Record<string, unknown> = {}) => ({
id: "user123",
email: "test@example.com",
garminConnected: false,
garminOauth1Token: "",
garminOauth2Token: "",
garminTokenExpiresAt: "",
garminRefreshTokenExpiresAt: "",
calendarToken: "token",
lastPeriodDate: "2025-01-01",
cycleLength: 28,
notificationTime: "08:00",
timezone: "UTC",
activeOverrides: [],
created: "2024-01-01T00:00:00Z",
updated: "2024-01-01T00:00:00Z",
...overrides,
});
it("uses default when intensityGoalMenstrual is 0", () => {
const record = createMockRecord({ intensityGoalMenstrual: 0 });
// biome-ignore lint/suspicious/noExplicitAny: test mock
const user = mapRecordToUser(record as any);
expect(user.intensityGoalMenstrual).toBe(DEFAULT_INTENSITY_GOALS.menstrual);
});
it("uses default when intensityGoalFollicular is 0", () => {
const record = createMockRecord({ intensityGoalFollicular: 0 });
// biome-ignore lint/suspicious/noExplicitAny: test mock
const user = mapRecordToUser(record as any);
expect(user.intensityGoalFollicular).toBe(
DEFAULT_INTENSITY_GOALS.follicular,
);
});
it("uses default when intensityGoalOvulation is 0", () => {
const record = createMockRecord({ intensityGoalOvulation: 0 });
// biome-ignore lint/suspicious/noExplicitAny: test mock
const user = mapRecordToUser(record as any);
expect(user.intensityGoalOvulation).toBe(DEFAULT_INTENSITY_GOALS.ovulation);
});
it("uses default when intensityGoalEarlyLuteal is 0", () => {
const record = createMockRecord({ intensityGoalEarlyLuteal: 0 });
// biome-ignore lint/suspicious/noExplicitAny: test mock
const user = mapRecordToUser(record as any);
expect(user.intensityGoalEarlyLuteal).toBe(
DEFAULT_INTENSITY_GOALS.earlyLuteal,
);
});
it("uses default when intensityGoalLateLuteal is 0", () => {
const record = createMockRecord({ intensityGoalLateLuteal: 0 });
// biome-ignore lint/suspicious/noExplicitAny: test mock
const user = mapRecordToUser(record as any);
expect(user.intensityGoalLateLuteal).toBe(
DEFAULT_INTENSITY_GOALS.lateLuteal,
);
});
it("preserves non-zero intensity goal values", () => {
const record = createMockRecord({
intensityGoalMenstrual: 100,
intensityGoalFollicular: 200,
});
// biome-ignore lint/suspicious/noExplicitAny: test mock
const user = mapRecordToUser(record as any);
expect(user.intensityGoalMenstrual).toBe(100);
expect(user.intensityGoalFollicular).toBe(200);
});
});

View File

@@ -110,20 +110,21 @@ export function mapRecordToUser(record: RecordModel): User {
lastPeriodDate: parseDate(record.lastPeriodDate),
cycleLength: record.cycleLength as number,
// Intensity goals with defaults for existing users
// Using || instead of ?? because PocketBase defaults number fields to 0
intensityGoalMenstrual:
(record.intensityGoalMenstrual as number) ??
(record.intensityGoalMenstrual as number) ||
DEFAULT_INTENSITY_GOALS.menstrual,
intensityGoalFollicular:
(record.intensityGoalFollicular as number) ??
(record.intensityGoalFollicular as number) ||
DEFAULT_INTENSITY_GOALS.follicular,
intensityGoalOvulation:
(record.intensityGoalOvulation as number) ??
(record.intensityGoalOvulation as number) ||
DEFAULT_INTENSITY_GOALS.ovulation,
intensityGoalEarlyLuteal:
(record.intensityGoalEarlyLuteal as number) ??
(record.intensityGoalEarlyLuteal as number) ||
DEFAULT_INTENSITY_GOALS.earlyLuteal,
intensityGoalLateLuteal:
(record.intensityGoalLateLuteal as number) ??
(record.intensityGoalLateLuteal as number) ||
DEFAULT_INTENSITY_GOALS.lateLuteal,
notificationTime: record.notificationTime as string,
timezone: record.timezone as string,