Phase 5 cleanup
This commit is contained in:
parent
5536acd67d
commit
76a516a417
136 changed files with 6322 additions and 1985 deletions
|
|
@ -1,8 +1,5 @@
|
|||
import { describe, it, expect } from 'vitest';
|
||||
import {
|
||||
CabinetEventType,
|
||||
CabinetEventSourceType,
|
||||
} from './cabinet-event.enums.js';
|
||||
import { CabinetEventType, CabinetEventSourceType } from './cabinet-event.enums.js';
|
||||
|
||||
describe(CabinetEventType.name, () => {
|
||||
it('has exactly 6 values', () => {
|
||||
|
|
@ -32,8 +29,6 @@ describe(CabinetEventSourceType.name, () => {
|
|||
['ORGANIZER_UNDO', 'organizer_undo'],
|
||||
['REFILL_LIST', 'refill_list'],
|
||||
])('%s = %s', (key, value) => {
|
||||
expect(
|
||||
CabinetEventSourceType[key as keyof typeof CabinetEventSourceType],
|
||||
).toBe(value);
|
||||
expect(CabinetEventSourceType[key as keyof typeof CabinetEventSourceType]).toBe(value);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,9 +1,5 @@
|
|||
import { describe, it, expect } from 'vitest';
|
||||
import {
|
||||
DosageFrequency,
|
||||
TimeOfDay,
|
||||
OrganizerFillStatus,
|
||||
} from './regimen.enums.js';
|
||||
import { DosageFrequency, TimeOfDay, OrganizerFillStatus } from './regimen.enums.js';
|
||||
|
||||
describe(DosageFrequency.name, () => {
|
||||
it('has exactly 7 values', () => {
|
||||
|
|
@ -48,8 +44,6 @@ describe(OrganizerFillStatus.name, () => {
|
|||
['PARTIAL', 'partial'],
|
||||
['REVERSED', 'reversed'],
|
||||
])('%s = %s', (key, value) => {
|
||||
expect(
|
||||
OrganizerFillStatus[key as keyof typeof OrganizerFillStatus],
|
||||
).toBe(value);
|
||||
expect(OrganizerFillStatus[key as keyof typeof OrganizerFillStatus]).toBe(value);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
import type {
|
||||
CabinetEventType,
|
||||
CabinetEventSourceType,
|
||||
} from '../enums/cabinet-event.enums.js';
|
||||
import type { CabinetEventType, CabinetEventSourceType } from '../enums/cabinet-event.enums.js';
|
||||
|
||||
export interface CabinetEvent {
|
||||
id: string;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
import type {
|
||||
DosageFrequency,
|
||||
TimeOfDay,
|
||||
} from '../enums/regimen.enums.js';
|
||||
import type { DosageFrequency, TimeOfDay } from '../enums/regimen.enums.js';
|
||||
import type { DosageUnit, MedicineForm, StrengthUnit } from '../enums/medicine.enums.js';
|
||||
|
||||
export interface Regimen {
|
||||
|
|
|
|||
|
|
@ -5,10 +5,7 @@ import { DosageFrequency } from '../enums/regimen.enums.js';
|
|||
* For example, TWICE_DAILY returns 2, WEEKLY returns 1/7.
|
||||
* AS_NEEDED returns 0 (excluded from calculations).
|
||||
*/
|
||||
export function getFrequencyMultiplier(
|
||||
frequency: DosageFrequency,
|
||||
customPerDay?: number,
|
||||
): number {
|
||||
export function getFrequencyMultiplier(frequency: DosageFrequency, customPerDay?: number): number {
|
||||
switch (frequency) {
|
||||
case DosageFrequency.DAILY:
|
||||
return 1;
|
||||
|
|
|
|||
|
|
@ -35,15 +35,21 @@ describe('CreateMedicinePriceRecordSchema', () => {
|
|||
});
|
||||
|
||||
it('rejects non-positive price', () => {
|
||||
expect(CreateMedicinePriceRecordSchema.safeParse({ ...validInput, price: 0 }).success).toBe(false);
|
||||
expect(CreateMedicinePriceRecordSchema.safeParse({ ...validInput, price: 0 }).success).toBe(
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects non-positive quantity', () => {
|
||||
expect(CreateMedicinePriceRecordSchema.safeParse({ ...validInput, quantity: -1 }).success).toBe(false);
|
||||
expect(CreateMedicinePriceRecordSchema.safeParse({ ...validInput, quantity: -1 }).success).toBe(
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects invalid unit', () => {
|
||||
expect(CreateMedicinePriceRecordSchema.safeParse({ ...validInput, unit: 'spoon' }).success).toBe(false);
|
||||
expect(
|
||||
CreateMedicinePriceRecordSchema.safeParse({ ...validInput, unit: 'spoon' }).success,
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -71,26 +71,32 @@ export const StoreComparisonResponseSchema = z.object({
|
|||
|
||||
export const MedicineSpendingAnalyticsResponseSchema = z.object({
|
||||
spendingOverTime: z.array(z.object({ period: z.string(), total: z.number() })),
|
||||
topBySpending: z.array(z.object({
|
||||
medicineId: z.string(),
|
||||
medicineName: z.string(),
|
||||
totalSpent: z.number(),
|
||||
avgPricePerUnit: z.number(),
|
||||
})),
|
||||
spendingByStore: z.array(z.object({
|
||||
storeId: z.string(),
|
||||
storeName: z.string(),
|
||||
totalSpent: z.number(),
|
||||
purchaseCount: z.number(),
|
||||
})),
|
||||
priceAlerts: z.array(z.object({
|
||||
medicineId: z.string(),
|
||||
medicineName: z.string(),
|
||||
storeName: z.string(),
|
||||
previousPrice: z.number(),
|
||||
currentPrice: z.number(),
|
||||
changePercent: z.number(),
|
||||
})),
|
||||
topBySpending: z.array(
|
||||
z.object({
|
||||
medicineId: z.string(),
|
||||
medicineName: z.string(),
|
||||
totalSpent: z.number(),
|
||||
avgPricePerUnit: z.number(),
|
||||
}),
|
||||
),
|
||||
spendingByStore: z.array(
|
||||
z.object({
|
||||
storeId: z.string(),
|
||||
storeName: z.string(),
|
||||
totalSpent: z.number(),
|
||||
purchaseCount: z.number(),
|
||||
}),
|
||||
),
|
||||
priceAlerts: z.array(
|
||||
z.object({
|
||||
medicineId: z.string(),
|
||||
medicineName: z.string(),
|
||||
storeName: z.string(),
|
||||
previousPrice: z.number(),
|
||||
currentPrice: z.number(),
|
||||
changePercent: z.number(),
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
export type CreateMedicinePriceRecordInput = z.infer<typeof CreateMedicinePriceRecordSchema>;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,11 @@ import {
|
|||
|
||||
describe('CreatePurchaseItemSchema', () => {
|
||||
it('accepts minimal valid item', () => {
|
||||
const result = CreatePurchaseItemSchema.safeParse({ name: 'Tylenol 30ct', quantity: 30, unit: 'tablet' });
|
||||
const result = CreatePurchaseItemSchema.safeParse({
|
||||
name: 'Tylenol 30ct',
|
||||
quantity: 30,
|
||||
unit: 'tablet',
|
||||
});
|
||||
expect(result.success).toBe(true);
|
||||
if (result.success) expect(result.data.addedToCabinet).toBeUndefined();
|
||||
});
|
||||
|
|
@ -27,19 +31,29 @@ describe('CreatePurchaseItemSchema', () => {
|
|||
});
|
||||
|
||||
it('rejects empty name', () => {
|
||||
expect(CreatePurchaseItemSchema.safeParse({ name: '', quantity: 1, unit: 'tablet' }).success).toBe(false);
|
||||
expect(
|
||||
CreatePurchaseItemSchema.safeParse({ name: '', quantity: 1, unit: 'tablet' }).success,
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('rejects non-positive quantity', () => {
|
||||
expect(CreatePurchaseItemSchema.safeParse({ name: 'X', quantity: 0, unit: 'tablet' }).success).toBe(false);
|
||||
expect(
|
||||
CreatePurchaseItemSchema.safeParse({ name: 'X', quantity: 0, unit: 'tablet' }).success,
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('rejects empty unit', () => {
|
||||
expect(CreatePurchaseItemSchema.safeParse({ name: 'X', quantity: 1, unit: '' }).success).toBe(false);
|
||||
expect(CreatePurchaseItemSchema.safeParse({ name: 'X', quantity: 1, unit: '' }).success).toBe(
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
it('trims name whitespace', () => {
|
||||
const result = CreatePurchaseItemSchema.parse({ name: ' Tylenol ', quantity: 1, unit: 'tablet' });
|
||||
const result = CreatePurchaseItemSchema.parse({
|
||||
name: ' Tylenol ',
|
||||
quantity: 1,
|
||||
unit: 'tablet',
|
||||
});
|
||||
expect(result.name).toBe('Tylenol');
|
||||
});
|
||||
});
|
||||
|
|
@ -53,7 +67,11 @@ describe('CreatePurchaseSchema', () => {
|
|||
});
|
||||
|
||||
it('accepts ordered status', () => {
|
||||
const result = CreatePurchaseSchema.safeParse({ storeId: 'st-1', status: 'ordered', items: [validItem] });
|
||||
const result = CreatePurchaseSchema.safeParse({
|
||||
storeId: 'st-1',
|
||||
status: 'ordered',
|
||||
items: [validItem],
|
||||
});
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
|
|
@ -66,7 +84,10 @@ describe('CreatePurchaseSchema', () => {
|
|||
});
|
||||
|
||||
it('rejects invalid status', () => {
|
||||
expect(CreatePurchaseSchema.safeParse({ storeId: 'st-1', status: 'pending', items: [validItem] }).success).toBe(false);
|
||||
expect(
|
||||
CreatePurchaseSchema.safeParse({ storeId: 'st-1', status: 'pending', items: [validItem] })
|
||||
.success,
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('accepts optional purchasedAt datetime', () => {
|
||||
|
|
@ -79,7 +100,13 @@ describe('CreatePurchaseSchema', () => {
|
|||
});
|
||||
|
||||
it('rejects invalid purchasedAt', () => {
|
||||
expect(CreatePurchaseSchema.safeParse({ storeId: 'st-1', items: [validItem], purchasedAt: 'not-a-date' }).success).toBe(false);
|
||||
expect(
|
||||
CreatePurchaseSchema.safeParse({
|
||||
storeId: 'st-1',
|
||||
items: [validItem],
|
||||
purchasedAt: 'not-a-date',
|
||||
}).success,
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -81,8 +81,8 @@ describe('UpdateRefillListItemSchema', () => {
|
|||
});
|
||||
|
||||
it('accepts actualPrice', () => {
|
||||
const result = UpdateRefillListItemSchema.parse({ actualPrice: 12.50 });
|
||||
expect(result.actualPrice).toBe(12.50);
|
||||
const result = UpdateRefillListItemSchema.parse({ actualPrice: 12.5 });
|
||||
expect(result.actualPrice).toBe(12.5);
|
||||
});
|
||||
|
||||
it('rejects negative actualPrice', () => {
|
||||
|
|
|
|||
|
|
@ -102,10 +102,7 @@ describe('CreateRegimenSchema', () => {
|
|||
it('accepts multiple medications', () => {
|
||||
const result = CreateRegimenSchema.safeParse({
|
||||
...validRegimen,
|
||||
medications: [
|
||||
validMedication,
|
||||
{ ...validMedication, medicineId: 'med-2', dosage: 2 },
|
||||
],
|
||||
medications: [validMedication, { ...validMedication, medicineId: 'med-2', dosage: 2 }],
|
||||
});
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -15,9 +15,7 @@ export const RegimenMedicationInputSchema = z
|
|||
instructions: z.string().max(500).trim().optional(),
|
||||
})
|
||||
.refine(
|
||||
(data) =>
|
||||
data.frequency !== DosageFrequency.CUSTOM ||
|
||||
data.customFrequencyPerDay !== undefined,
|
||||
(data) => data.frequency !== DosageFrequency.CUSTOM || data.customFrequencyPerDay !== undefined,
|
||||
{ message: 'customFrequencyPerDay is required when frequency is custom' },
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -29,15 +29,15 @@ describe('CreateStoreSchema', () => {
|
|||
});
|
||||
|
||||
it('rejects invalid location lat', () => {
|
||||
expect(
|
||||
CreateStoreSchema.safeParse({ name: 'X', location: { lat: 91, lng: 0 } }).success,
|
||||
).toBe(false);
|
||||
expect(CreateStoreSchema.safeParse({ name: 'X', location: { lat: 91, lng: 0 } }).success).toBe(
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects invalid location lng', () => {
|
||||
expect(
|
||||
CreateStoreSchema.safeParse({ name: 'X', location: { lat: 0, lng: 181 } }).success,
|
||||
).toBe(false);
|
||||
expect(CreateStoreSchema.safeParse({ name: 'X', location: { lat: 0, lng: 181 } }).success).toBe(
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
it('trims name whitespace', () => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue