요청 2: 급한 순서를 정해야 하는 요청
요청문: src/stock.mjs에 todayTasks(items, today) 함수를 만들어 줘. 아침에 카페 사장님이 보는 '오늘 할 일' 목록이야. 유통기한이 지난 것, 3일 안에 끝나는 것, 수량이 minQty보다 적은 것을 한 목록으로 합쳐서 급한 것부터 보여 주고 싶어. today는 'YYYY-MM-DD' 문자열이야. test/stock.test.mjs에 테스트도 추가하고 돌려 봐.
diff --git a/src/stock.mjs b/src/stock.mjs
index f81b897..4600db7 100644
--- a/src/stock.mjs
+++ b/src/stock.mjs
@@ -19,3 +19,28 @@ export function orderList(items) {
.filter((it) => it.qty < it.minQty)
.map((it) => ({ name: it.name, need: it.minQty * 2 - it.qty, unit: it.unit }));
}
+
+const SOON_DAYS = 3;
+
+// 한 재료가 여러 사유에 걸리면 한 줄에 합친다: { name, daysLeft, reasons: ['expired'|'expiring'|'low'] }
+function classify(it, today) {
+ const daysLeft = daysBetween(today, it.expires);
+ const reasons = [];
+ if (daysLeft < 0) reasons.push('expired');
+ else if (daysLeft <= SOON_DAYS) reasons.push('expiring');
+ if (it.qty < it.minQty) reasons.push('low');
+ return { name: it.name, qty: it.qty, unit: it.unit, daysLeft, reasons };
+}
+
+// a가 더 급하면 음수, b가 더 급하면 양수, 같으면 0
+export function compareUrgency(a, b) {
+ // TODO(human): 급한 순서 규칙 구현
+ return 0;
+}
+
+export function todayTasks(items, today) {
+ return items
+ .map((it) => classify(it, today))
+ .filter((t) => t.reasons.length > 0)
+ .sort(compareUrgency);
+}
diff --git a/test/stock.test.mjs b/test/stock.test.mjs
index 71de632..0764a33 100644
--- a/test/stock.test.mjs
+++ b/test/stock.test.mjs
@@ -1,5 +1,6 @@
import assert from 'node:assert/strict';
-import { lowStock, orderList, daysBetween } from '../src/stock.mjs';
+import { readFileSync } from 'node:fs';
+import { lowStock, orderList, daysBetween, todayTasks } from '../src/stock.mjs';
const items = [
{ name: '우유', qty: 3, unit: 'L', expires: '2026-10-20', minQty: 5 },
{ name: '원두', qty: 4, unit: 'kg', expires: '2027-01-10', minQty: 2 },
@@ -8,3 +9,21 @@ assert.deepEqual(lowStock(items), ['우유']);
assert.deepEqual(orderList(items), [{ name: '우유', need: 7, unit: 'L' }]);
assert.equal(daysBetween('2026-10-18', '2026-10-20'), 2);
console.log('기본 테스트 통과');
+
+// todayTasks: 무엇이 어떤 사유로 들어가는지 (순서와 무관)
+const data = JSON.parse(readFileSync(new URL('../data.json', import.meta.url)));
+const tasks = todayTasks(data, '2026-10-18');
+const byName = Object.fromEntries(tasks.map((t) => [t.name, t]));
+
+assert.equal(byName['원두'], undefined); // 넉넉하고 유통기한도 멀다
+assert.deepEqual(byName['딸기'].reasons, ['expired']); // 어제 만료 (qty == minQty 는 부족 아님)
+assert.deepEqual(byName['생크림'].reasons, ['expiring']); // 오늘 만료 = 아직 안 지남
+assert.equal(byName['생크림'].daysLeft, 0);
+assert.deepEqual(byName['바닐라 시럽'].reasons, ['expiring']); // 딱 3일 남음 → 포함
+assert.deepEqual(byName['우유'].reasons, ['expiring', 'low']); // 두 사유가 한 줄로 합쳐짐
+assert.deepEqual(byName['치즈케이크'].reasons, ['expiring', 'low']);
+assert.equal(tasks.length, 5); // 중복 없이 5개
+assert.deepEqual(todayTasks([], '2026-10-18'), []);
+console.log('todayTasks 분류 테스트 통과');
+
+// TODO: compareUrgency 구현 후 순서 테스트 추가