Phases 6-7
This commit is contained in:
parent
76a516a417
commit
029940b079
111 changed files with 17247 additions and 447 deletions
|
|
@ -0,0 +1,158 @@
|
|||
import { describe, it, expect } from 'vitest';
|
||||
import { FreshnessCalculatorService } from './freshness-calculator.service.js';
|
||||
import { ItemStatus, FreshnessUrgency, FreshnessSource, StorageLocation } from '@meshitrack/shared';
|
||||
|
||||
describe(FreshnessCalculatorService.name, () => {
|
||||
const service = new FreshnessCalculatorService();
|
||||
|
||||
const baseItem = {
|
||||
status: ItemStatus.SEALED,
|
||||
storageLocation: StorageLocation.FRIDGE,
|
||||
purchaseDate: new Date('2024-01-01'),
|
||||
expirationDate: undefined,
|
||||
openedDate: undefined,
|
||||
preparedDate: undefined,
|
||||
};
|
||||
|
||||
const rule = {
|
||||
shelfLifeDays: 14,
|
||||
openedLifeDays: 7,
|
||||
freezerLifeDays: 90,
|
||||
};
|
||||
|
||||
describe('calculate', () => {
|
||||
it('uses packaging expiration date when present', () => {
|
||||
const item = { ...baseItem, expirationDate: new Date('2099-12-31') };
|
||||
const result = service.calculate(item, rule);
|
||||
expect(result.source).toBe(FreshnessSource.PACKAGING);
|
||||
expect(result.estimatedExpiryDate).toEqual(new Date('2099-12-31'));
|
||||
});
|
||||
|
||||
it('falls back to 7-day default when no rule provided', () => {
|
||||
const result = service.calculate(baseItem, null);
|
||||
expect(result.source).toBe(FreshnessSource.RULE);
|
||||
const expected = new Date('2024-01-01');
|
||||
expected.setDate(expected.getDate() + 7);
|
||||
expect(result.estimatedExpiryDate).toEqual(expected);
|
||||
});
|
||||
|
||||
it('uses freezerLifeDays for freezer storage', () => {
|
||||
const item = { ...baseItem, storageLocation: StorageLocation.FREEZER };
|
||||
const result = service.calculate(item, rule);
|
||||
expect(result.source).toBe(FreshnessSource.RULE);
|
||||
const expected = new Date('2024-01-01');
|
||||
expected.setDate(expected.getDate() + 90);
|
||||
expect(result.estimatedExpiryDate).toEqual(expected);
|
||||
});
|
||||
|
||||
it('uses shelfLifeDays for sealed items in freezer without freezerLifeDays', () => {
|
||||
const item = { ...baseItem, storageLocation: StorageLocation.FREEZER };
|
||||
const ruleNoFreezer = { shelfLifeDays: 14, openedLifeDays: 7 };
|
||||
const result = service.calculate(item, ruleNoFreezer);
|
||||
const expected = new Date('2024-01-01');
|
||||
expected.setDate(expected.getDate() + 14);
|
||||
expect(result.estimatedExpiryDate).toEqual(expected);
|
||||
});
|
||||
|
||||
it('uses openedLifeDays for opened items', () => {
|
||||
const openedDate = new Date('2024-01-05');
|
||||
const item = {
|
||||
...baseItem,
|
||||
status: ItemStatus.OPENED,
|
||||
openedDate,
|
||||
};
|
||||
const result = service.calculate(item, rule);
|
||||
const expected = new Date('2024-01-05');
|
||||
expected.setDate(expected.getDate() + 7);
|
||||
expect(result.estimatedExpiryDate).toEqual(expected);
|
||||
});
|
||||
|
||||
it('uses openedLifeDays for prepared items with openedDate', () => {
|
||||
const openedDate = new Date('2024-01-05');
|
||||
const item = {
|
||||
...baseItem,
|
||||
status: ItemStatus.PREPARED,
|
||||
openedDate,
|
||||
};
|
||||
const result = service.calculate(item, rule);
|
||||
const expected = new Date('2024-01-05');
|
||||
expected.setDate(expected.getDate() + 7);
|
||||
expect(result.estimatedExpiryDate).toEqual(expected);
|
||||
});
|
||||
|
||||
it('uses shelfLifeDays for opened item without openedDate', () => {
|
||||
const item = { ...baseItem, status: ItemStatus.OPENED };
|
||||
const result = service.calculate(item, rule);
|
||||
const expected = new Date('2024-01-01');
|
||||
expected.setDate(expected.getDate() + 14);
|
||||
expect(result.estimatedExpiryDate).toEqual(expected);
|
||||
});
|
||||
|
||||
it('uses shelfLifeDays for sealed items', () => {
|
||||
const result = service.calculate(baseItem, rule);
|
||||
const expected = new Date('2024-01-01');
|
||||
expected.setDate(expected.getDate() + 14);
|
||||
expect(result.estimatedExpiryDate).toEqual(expected);
|
||||
});
|
||||
|
||||
it('computes daysRemaining and urgency', () => {
|
||||
const future = new Date();
|
||||
future.setDate(future.getDate() + 10);
|
||||
const item = { ...baseItem, expirationDate: future };
|
||||
const result = service.calculate(item, rule);
|
||||
expect(result.daysRemaining).toBeGreaterThan(5);
|
||||
expect(result.urgency).toBe(FreshnessUrgency.FRESH);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isActive', () => {
|
||||
it('returns true for sealed', () => {
|
||||
expect(service.isActive('sealed')).toBe(true);
|
||||
});
|
||||
it('returns true for opened', () => {
|
||||
expect(service.isActive('opened')).toBe(true);
|
||||
});
|
||||
it('returns true for prepared', () => {
|
||||
expect(service.isActive('prepared')).toBe(true);
|
||||
});
|
||||
it('returns false for consumed', () => {
|
||||
expect(service.isActive('consumed')).toBe(false);
|
||||
});
|
||||
it('returns false for discarded', () => {
|
||||
expect(service.isActive('discarded')).toBe(false);
|
||||
});
|
||||
it('returns false for expired', () => {
|
||||
expect(service.isActive('expired')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('mapUrgency', () => {
|
||||
it('returns FRESH for > 5 days', () => {
|
||||
expect(service.mapUrgency(6)).toBe(FreshnessUrgency.FRESH);
|
||||
});
|
||||
it('returns USE_SOON for 2-5 days', () => {
|
||||
expect(service.mapUrgency(3)).toBe(FreshnessUrgency.USE_SOON);
|
||||
});
|
||||
it('returns URGENT for 0-1 days', () => {
|
||||
expect(service.mapUrgency(1)).toBe(FreshnessUrgency.URGENT);
|
||||
});
|
||||
it('returns CHECK for -1 to -3 days', () => {
|
||||
expect(service.mapUrgency(-1)).toBe(FreshnessUrgency.CHECK);
|
||||
});
|
||||
it('returns EXPIRED for < -3 days', () => {
|
||||
expect(service.mapUrgency(-4)).toBe(FreshnessUrgency.EXPIRED);
|
||||
});
|
||||
it('returns USE_SOON for exactly 2', () => {
|
||||
expect(service.mapUrgency(2)).toBe(FreshnessUrgency.USE_SOON);
|
||||
});
|
||||
it('returns USE_SOON for exactly 5', () => {
|
||||
expect(service.mapUrgency(5)).toBe(FreshnessUrgency.USE_SOON);
|
||||
});
|
||||
it('returns URGENT for exactly 0', () => {
|
||||
expect(service.mapUrgency(0)).toBe(FreshnessUrgency.URGENT);
|
||||
});
|
||||
it('returns CHECK for exactly -3', () => {
|
||||
expect(service.mapUrgency(-3)).toBe(FreshnessUrgency.CHECK);
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue