Fix intensity goal defaults for PocketBase 0 values
All checks were successful
Deploy / deploy (push) Successful in 2m39s
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:
@@ -4,9 +4,11 @@ import { describe, expect, it, vi } from "vitest";
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
createPocketBaseClient,
|
createPocketBaseClient,
|
||||||
|
DEFAULT_INTENSITY_GOALS,
|
||||||
getCurrentUser,
|
getCurrentUser,
|
||||||
isAuthenticated,
|
isAuthenticated,
|
||||||
loadAuthFromCookies,
|
loadAuthFromCookies,
|
||||||
|
mapRecordToUser,
|
||||||
} from "./pocketbase";
|
} from "./pocketbase";
|
||||||
|
|
||||||
describe("isAuthenticated", () => {
|
describe("isAuthenticated", () => {
|
||||||
@@ -221,3 +223,76 @@ describe("createPocketBaseClient", () => {
|
|||||||
expect(client.authStore).toBeDefined();
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -110,20 +110,21 @@ export function mapRecordToUser(record: RecordModel): User {
|
|||||||
lastPeriodDate: parseDate(record.lastPeriodDate),
|
lastPeriodDate: parseDate(record.lastPeriodDate),
|
||||||
cycleLength: record.cycleLength as number,
|
cycleLength: record.cycleLength as number,
|
||||||
// Intensity goals with defaults for existing users
|
// Intensity goals with defaults for existing users
|
||||||
|
// Using || instead of ?? because PocketBase defaults number fields to 0
|
||||||
intensityGoalMenstrual:
|
intensityGoalMenstrual:
|
||||||
(record.intensityGoalMenstrual as number) ??
|
(record.intensityGoalMenstrual as number) ||
|
||||||
DEFAULT_INTENSITY_GOALS.menstrual,
|
DEFAULT_INTENSITY_GOALS.menstrual,
|
||||||
intensityGoalFollicular:
|
intensityGoalFollicular:
|
||||||
(record.intensityGoalFollicular as number) ??
|
(record.intensityGoalFollicular as number) ||
|
||||||
DEFAULT_INTENSITY_GOALS.follicular,
|
DEFAULT_INTENSITY_GOALS.follicular,
|
||||||
intensityGoalOvulation:
|
intensityGoalOvulation:
|
||||||
(record.intensityGoalOvulation as number) ??
|
(record.intensityGoalOvulation as number) ||
|
||||||
DEFAULT_INTENSITY_GOALS.ovulation,
|
DEFAULT_INTENSITY_GOALS.ovulation,
|
||||||
intensityGoalEarlyLuteal:
|
intensityGoalEarlyLuteal:
|
||||||
(record.intensityGoalEarlyLuteal as number) ??
|
(record.intensityGoalEarlyLuteal as number) ||
|
||||||
DEFAULT_INTENSITY_GOALS.earlyLuteal,
|
DEFAULT_INTENSITY_GOALS.earlyLuteal,
|
||||||
intensityGoalLateLuteal:
|
intensityGoalLateLuteal:
|
||||||
(record.intensityGoalLateLuteal as number) ??
|
(record.intensityGoalLateLuteal as number) ||
|
||||||
DEFAULT_INTENSITY_GOALS.lateLuteal,
|
DEFAULT_INTENSITY_GOALS.lateLuteal,
|
||||||
notificationTime: record.notificationTime as string,
|
notificationTime: record.notificationTime as string,
|
||||||
timezone: record.timezone as string,
|
timezone: record.timezone as string,
|
||||||
|
|||||||
Reference in New Issue
Block a user