feat(scaler): add ingredient scaling with sensible rounding
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
16
src/lib/server/recipes/scaler.ts
Normal file
16
src/lib/server/recipes/scaler.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import type { Ingredient } from '$lib/types';
|
||||||
|
|
||||||
|
export function roundQuantity(q: number): number {
|
||||||
|
if (q === 0) return 0;
|
||||||
|
if (q < 1) return Math.round(q * 100) / 100;
|
||||||
|
if (q < 10) return Math.round(q * 10) / 10;
|
||||||
|
return Math.round(q);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function scaleIngredients(ings: Ingredient[], factor: number): Ingredient[] {
|
||||||
|
if (factor <= 0) throw new Error('factor must be positive');
|
||||||
|
return ings.map((i) => ({
|
||||||
|
...i,
|
||||||
|
quantity: i.quantity === null ? null : roundQuantity(i.quantity * factor)
|
||||||
|
}));
|
||||||
|
}
|
||||||
43
tests/unit/scaler.test.ts
Normal file
43
tests/unit/scaler.test.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import { scaleIngredients, roundQuantity } from '../../src/lib/server/recipes/scaler';
|
||||||
|
import type { Ingredient } from '../../src/lib/types';
|
||||||
|
|
||||||
|
const mk = (q: number | null, unit: string | null, name: string): Ingredient => ({
|
||||||
|
position: 0,
|
||||||
|
quantity: q,
|
||||||
|
unit,
|
||||||
|
name,
|
||||||
|
note: null,
|
||||||
|
raw_text: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('roundQuantity', () => {
|
||||||
|
it.each([
|
||||||
|
[0.333333, 0.33],
|
||||||
|
[12.345, 12],
|
||||||
|
[1.55, 1.6],
|
||||||
|
[100.49, 100],
|
||||||
|
[0.5, 0.5]
|
||||||
|
] as const)('rounds %f to %f', (input, expected) => {
|
||||||
|
expect(roundQuantity(input)).toBe(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('scaleIngredients', () => {
|
||||||
|
it('scales parsed quantities', () => {
|
||||||
|
const scaled = scaleIngredients([mk(200, 'g', 'Mehl'), mk(3, null, 'Eier')], 2);
|
||||||
|
expect(scaled[0].quantity).toBe(400);
|
||||||
|
expect(scaled[1].quantity).toBe(6);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('leaves null quantities alone', () => {
|
||||||
|
const scaled = scaleIngredients([mk(null, null, 'etwas Salz')], 2);
|
||||||
|
expect(scaled[0].quantity).toBeNull();
|
||||||
|
expect(scaled[0].name).toBe('etwas Salz');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rounds sensibly', () => {
|
||||||
|
const scaled = scaleIngredients([mk(100, 'g', 'Butter')], 1 / 3);
|
||||||
|
expect(scaled[0].quantity).toBe(33);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user