import { describe, it, expect } from 'vitest'; import { toMetric } from '../../../src/modules/recipes/unit-conversion.service.js'; describe('toMetric', () => { describe('metric pass-through', () => { it('passes g through unchanged', () => { const r = toMetric(100, 'g', 'g'); expect(r).toEqual({ ok: true, quantity: 100, unit: 'g' }); }); it('passes ml through unchanged', () => { const r = toMetric(250, 'ml', 'ml'); expect(r).toEqual({ ok: true, quantity: 250, unit: 'ml' }); }); it('passes piece through unchanged', () => { const r = toMetric(2, 'piece', 'piece'); expect(r).toEqual({ ok: true, quantity: 2, unit: 'piece' }); }); it('passes slice through unchanged', () => { const r = toMetric(3, 'slice', 'slice'); expect(r).toEqual({ ok: true, quantity: 3, unit: 'slice' }); }); }); describe('mass conversions', () => { it('converts oz to g for a g-product', () => { const r = toMetric(1, 'oz', 'g'); expect(r).toEqual({ ok: true, quantity: 28.35, unit: 'g' }); }); it('converts lb to g for a g-product', () => { const r = toMetric(1, 'lb', 'g'); expect(r).toEqual({ ok: true, quantity: 453.592, unit: 'g' }); }); it('converts oz to ml using density for a ml-product', () => { // 1 oz = 28.3495 g; density 1.03 g/ml → 27.524... ml const r = toMetric(1, 'oz', 'ml', 1.03); expect(r.ok).toBe(true); if (r.ok) { expect(r.unit).toBe('ml'); expect(r.quantity).toBeCloseTo(27.524, 2); } }); it('returns MISSING_DENSITY for oz → ml when density absent', () => { const r = toMetric(1, 'oz', 'ml'); expect(r).toMatchObject({ ok: false, code: 'MISSING_DENSITY' }); }); it('returns INCOMPATIBLE_UNITS for oz → piece', () => { const r = toMetric(1, 'oz', 'piece'); expect(r).toMatchObject({ ok: false, code: 'INCOMPATIBLE_UNITS' }); }); }); describe('volume conversions', () => { it('converts tsp to ml for a ml-product', () => { const r = toMetric(1, 'tsp', 'ml'); expect(r).toEqual({ ok: true, quantity: 4.929, unit: 'ml' }); }); it('converts tbsp to ml for a ml-product', () => { const r = toMetric(1, 'tbsp', 'ml'); expect(r).toEqual({ ok: true, quantity: 14.787, unit: 'ml' }); }); it('converts fl_oz to ml for a ml-product', () => { const r = toMetric(1, 'fl_oz', 'ml'); expect(r).toEqual({ ok: true, quantity: 29.574, unit: 'ml' }); }); it('converts cup to ml for a ml-product', () => { const r = toMetric(1, 'cup', 'ml'); expect(r).toEqual({ ok: true, quantity: 236.588, unit: 'ml' }); }); it('converts cup to g using density for a g-product', () => { // 1 cup = 236.588 ml; density 1.05 g/ml → 248.417 g const r = toMetric(1, 'cup', 'g', 1.05); expect(r.ok).toBe(true); if (r.ok) { expect(r.unit).toBe('g'); expect(r.quantity).toBeCloseTo(248.417, 2); } }); it('returns MISSING_DENSITY for cup → g when density absent', () => { const r = toMetric(1, 'cup', 'g'); expect(r).toMatchObject({ ok: false, code: 'MISSING_DENSITY' }); }); it('returns INCOMPATIBLE_UNITS for cup → piece', () => { const r = toMetric(1, 'cup', 'piece'); expect(r).toMatchObject({ ok: false, code: 'INCOMPATIBLE_UNITS' }); }); }); describe('mass to ml cross-conversion', () => { it('converts oz to ml using density', () => { // 1 oz = 28.3495 g; density 0.9 g/ml → 28.3495 / 0.9 = 31.499... ml const r = toMetric(1, 'oz', 'ml', 0.9); expect(r.ok).toBe(true); if (r.ok) { expect(r.unit).toBe('ml'); expect(r.quantity).toBeCloseTo(31.499, 1); } }); it('returns MISSING_DENSITY for oz → ml when density absent', () => { const r = toMetric(1, 'oz', 'ml'); expect(r).toMatchObject({ ok: false, code: 'MISSING_DENSITY' }); }); it('returns INCOMPATIBLE_UNITS for oz → piece', () => { const r = toMetric(1, 'oz', 'piece'); expect(r).toMatchObject({ ok: false, code: 'INCOMPATIBLE_UNITS' }); }); }); describe('unknown unit', () => { it('returns INCOMPATIBLE_UNITS for unknown unit', () => { const r = toMetric(1, 'gallon' as never, 'g'); expect(r).toMatchObject({ ok: false, code: 'INCOMPATIBLE_UNITS' }); if (!r.ok) { expect(r.message).toContain('Unknown unit'); } }); }); });