fix: restore automatic intake auto-marking (#420)

This commit is contained in:
Daniel Volz
2026-03-12 21:32:51 +01:00
committed by GitHub
parent c13bfad16f
commit 3fda41e501
5 changed files with 212 additions and 25 deletions
@@ -390,25 +390,14 @@ async function checkAndSendIntakeReminders(logger: ServiceLogger): Promise<void>
return; // No users with settings
}
const intakeEligibleSettings = allUserSettings.filter((settings) => {
const emailEnabled = settings.emailEnabled && settings.notificationEmail && settings.emailIntakeReminders;
const shoutrrrEnabled = settings.shoutrrrEnabled && settings.shoutrrrUrl && settings.shoutrrrIntakeReminders;
return Boolean(emailEnabled || shoutrrrEnabled);
});
logger.debug(`[IntakeReminder] Evaluating ${allUserSettings.length} intake profile(s) for auto-marking`);
if (intakeEligibleSettings.length === 0) {
logger.debug("[IntakeReminder] No intake notification channels enabled");
return;
}
logger.debug(`[IntakeReminder] Evaluating ${intakeEligibleSettings.length} intake reminder profile(s)`);
for (const userSettings of intakeEligibleSettings) {
for (const userSettings of allUserSettings) {
await checkAndSendIntakeRemindersForUser(userSettings, logger);
}
}
async function checkAndSendIntakeRemindersForUser(
export async function checkAndSendIntakeRemindersForUser(
settings: UserSettings & { userId: number },
logger: ServiceLogger
): Promise<void> {
@@ -0,0 +1,138 @@
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { db } from "../db/client.js";
import { checkAndSendIntakeRemindersForUser } from "../services/intake-reminder-scheduler.js";
vi.mock("../db/client.js", () => ({
db: {
select: vi.fn(),
insert: vi.fn(),
},
}));
function createLogger() {
return {
debug: vi.fn(),
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
};
}
describe("checkAndSendIntakeRemindersForUser", () => {
const mockedDb = vi.mocked(db);
let originalTz: string | undefined;
beforeEach(() => {
vi.useFakeTimers();
vi.setSystemTime(new Date(2026, 0, 5, 10, 30, 0));
originalTz = process.env.TZ;
process.env.TZ = Intl.DateTimeFormat().resolvedOptions().timeZone;
});
afterEach(() => {
vi.useRealTimers();
vi.restoreAllMocks();
if (originalTz === undefined) {
delete process.env.TZ;
} else {
process.env.TZ = originalTz;
}
});
it("auto-marks due intakes in automatic mode even when all intake reminder channels are disabled", async () => {
const insertedRows: Array<Record<string, unknown>> = [];
const selectMock = vi.mocked(mockedDb.select);
const insertMock = vi.mocked(mockedDb.insert);
selectMock
.mockImplementationOnce(
() =>
({
from: () => ({
where: () => ({
limit: async () => [{ username: "auto-user" }],
}),
}),
}) as never
)
.mockImplementationOnce(
() =>
({
from: () => ({
where: () => ({
orderBy: async () => [
{
id: 7,
userId: 11,
name: "Vitamin D",
genericName: null,
takenByJson: null,
pillWeightMg: null,
doseUnit: "mg",
isObsolete: false,
intakeRemindersEnabled: false,
intakesJson: JSON.stringify([
{
usage: 1,
every: 1,
start: "2026-01-05T08:00:00.000Z",
takenBy: null,
intakeRemindersEnabled: false,
},
]),
usageJson: "[]",
everyJson: "[]",
startJson: "[]",
},
],
}),
}),
}) as never
)
.mockImplementationOnce(
() =>
({
from: () => ({
where: async () => [],
}),
}) as never
);
insertMock.mockImplementation(
() =>
({
values: async (row: Record<string, unknown>) => {
insertedRows.push(row);
},
}) as never
);
const logger = createLogger();
await checkAndSendIntakeRemindersForUser(
{
userId: 11,
language: "en",
stockCalculationMode: "automatic",
emailEnabled: false,
notificationEmail: null,
emailIntakeReminders: false,
shoutrrrEnabled: false,
shoutrrrUrl: null,
shoutrrrIntakeReminders: false,
repeatRemindersEnabled: false,
} as never,
logger as never
);
expect(insertedRows).toHaveLength(1);
expect(insertedRows[0]).toMatchObject({
userId: 11,
doseId: `7-0-${new Date(2026, 0, 5).getTime()}`,
markedBy: null,
takenSource: "automatic",
dismissed: false,
});
expect(logger.info).toHaveBeenCalledWith("[IntakeReminder] Auto-marked 1 due intake dose(s) as taken");
});
});