Fix email delivery and body battery null handling
All checks were successful
Deploy / deploy (push) Successful in 2m34s

- Add PocketBase admin auth to notifications endpoint (was returning 0 users)
- Store null instead of 100 for body battery when Garmin returns no data
- Update decision engine to skip body battery rules when values are null
- Dashboard and email already display "N/A" for null values

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-22 08:50:30 +00:00
parent 092d8bb3dd
commit d4b04a17be
9 changed files with 153 additions and 41 deletions

View File

@@ -9,7 +9,8 @@ let mockUsers: User[] = [];
let mockDailyLogs: DailyLog[] = [];
const mockPbUpdate = vi.fn().mockResolvedValue({ id: "log123" });
// Mock PocketBase
// Mock PocketBase with admin auth
const mockAuthWithPassword = vi.fn().mockResolvedValue({ id: "admin" });
vi.mock("@/lib/pocketbase", () => ({
createPocketBaseClient: vi.fn(() => ({
collection: vi.fn((name: string) => ({
@@ -23,10 +24,21 @@ vi.mock("@/lib/pocketbase", () => ({
return [];
}),
update: mockPbUpdate,
authWithPassword: (email: string, password: string) =>
mockAuthWithPassword(email, password),
})),
})),
}));
// Mock logger
vi.mock("@/lib/logger", () => ({
logger: {
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
},
}));
// Mock email sending
const mockSendDailyEmail = vi.fn().mockResolvedValue(undefined);
const mockSendTokenExpirationWarning = vi.fn().mockResolvedValue(undefined);
@@ -110,6 +122,9 @@ describe("POST /api/cron/notifications", () => {
mockUsers = [];
mockDailyLogs = [];
process.env.CRON_SECRET = validSecret;
process.env.POCKETBASE_ADMIN_EMAIL = "admin@example.com";
process.env.POCKETBASE_ADMIN_PASSWORD = "admin-password";
mockAuthWithPassword.mockResolvedValue({ id: "admin" });
// Mock current time to 07:00 UTC
vi.useFakeTimers();
vi.setSystemTime(new Date("2025-01-15T07:00:00Z"));
@@ -143,6 +158,36 @@ describe("POST /api/cron/notifications", () => {
expect(response.status).toBe(401);
});
it("returns 500 when POCKETBASE_ADMIN_EMAIL is not set", async () => {
process.env.POCKETBASE_ADMIN_EMAIL = "";
const response = await POST(createMockRequest(`Bearer ${validSecret}`));
expect(response.status).toBe(500);
const body = await response.json();
expect(body.error).toBe("Server misconfiguration");
});
it("returns 500 when POCKETBASE_ADMIN_PASSWORD is not set", async () => {
process.env.POCKETBASE_ADMIN_PASSWORD = "";
const response = await POST(createMockRequest(`Bearer ${validSecret}`));
expect(response.status).toBe(500);
const body = await response.json();
expect(body.error).toBe("Server misconfiguration");
});
it("returns 500 when PocketBase admin auth fails", async () => {
mockAuthWithPassword.mockRejectedValueOnce(new Error("Auth failed"));
const response = await POST(createMockRequest(`Bearer ${validSecret}`));
expect(response.status).toBe(500);
const body = await response.json();
expect(body.error).toBe("Database authentication failed");
});
});
describe("User time matching", () => {