From 436e7df72a00866eed5b6f9633aee65865841ef3 Mon Sep 17 00:00:00 2001 From: akihidem Date: Tue, 9 Jun 2026 11:13:21 +0900 Subject: [PATCH] fix(deepwork): implement todaysAccum stub + add tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit todaysAccum は公開 export なのに throw する潜在欠陥だった(TODO v0.2)。当日(now のローカル日付)の span を startedAt 基準で絞り、min を合計して返す実装に。ymd を work_state.ts から export して重複を回避。テスト4件追加(除外/空/丸め/跨ぎ)。 Co-Authored-By: Claude Opus 4.8 (1M context) --- src/infer/deepwork.ts | 11 ++++++--- src/infer/work_state.ts | 2 +- tests/todays-accum.test.ts | 49 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 tests/todays-accum.test.ts diff --git a/src/infer/deepwork.ts b/src/infer/deepwork.ts index bf03e05..3bcf8df 100644 --- a/src/infer/deepwork.ts +++ b/src/infer/deepwork.ts @@ -7,15 +7,20 @@ * cogsync (調査) report/02-cognition.md 参照。 */ +import { ymd } from "./work_state.ts"; + export type DeepWorkAccum = { date: string; // YYYY-MM-DD totalMin: number; spans: { startedAt: Date; endedAt: Date; min: number }[]; }; -export function todaysAccum(_now: Date, _spans: DeepWorkAccum["spans"]): DeepWorkAccum { - // TODO v0.2 - throw new Error("todaysAccum not implemented (v0.2)"); +export function todaysAccum(now: Date, spans: DeepWorkAccum["spans"]): DeepWorkAccum { + // 「当日」= now のローカル日付。span はその開始日 (startedAt) でその日に属すと判定する。 + const date = ymd(now); + const todays = spans.filter((s) => ymd(s.startedAt) === date); + const totalMin = Math.round(todays.reduce((sum, s) => sum + s.min, 0)); + return { date, totalMin, spans: todays }; } export function shouldWarnDailyCap(_accum: DeepWorkAccum, _capMin: number): boolean { diff --git a/src/infer/work_state.ts b/src/infer/work_state.ts index bc84371..bdfbd5b 100644 --- a/src/infer/work_state.ts +++ b/src/infer/work_state.ts @@ -231,7 +231,7 @@ function emptyBuckets(): DeepWorkBuckets { return { manual: 0, auto: 0, bypass: 0 }; } -function ymd(d: Date): string { +export function ymd(d: Date): string { const y = d.getFullYear(); const m = String(d.getMonth() + 1).padStart(2, "0"); const day = String(d.getDate()).padStart(2, "0"); diff --git a/tests/todays-accum.test.ts b/tests/todays-accum.test.ts new file mode 100644 index 0000000..5022660 --- /dev/null +++ b/tests/todays-accum.test.ts @@ -0,0 +1,49 @@ +import { test } from "node:test"; +import { strict as assert } from "node:assert"; +import { todaysAccum } from "../src/infer/deepwork.ts"; + +// Date はローカル成分で構築する(ymd がローカル日付を使うため、TZ 非依存にする)。 +const span = (y: number, mo: number, d: number, h: number, mi: number, min: number) => ({ + startedAt: new Date(y, mo - 1, d, h, mi), + endedAt: new Date(y, mo - 1, d, h, mi + min), + min, +}); + +test("todaysAccum: 当日の span だけ合計し、前日・翌日は除外する", () => { + const now = new Date(2026, 4, 9, 12, 0); // 2026-05-09 ローカル + const spans = [ + span(2026, 5, 9, 9, 0, 30), // 当日 +30 + span(2026, 5, 9, 13, 0, 15), // 当日 +15 + span(2026, 5, 8, 23, 0, 45), // 前日 → 除外 + span(2026, 5, 10, 1, 0, 20), // 翌日 → 除外 + ]; + const acc = todaysAccum(now, spans); + assert.equal(acc.date, "2026-05-09"); + assert.equal(acc.totalMin, 45); // 30 + 15 のみ + assert.equal(acc.spans.length, 2); +}); + +test("todaysAccum: span が無ければ totalMin=0 / spans=[](throw しない)", () => { + const now = new Date(2026, 4, 9, 12, 0); + const acc = todaysAccum(now, []); + assert.equal(acc.date, "2026-05-09"); + assert.equal(acc.totalMin, 0); + assert.deepEqual(acc.spans, []); +}); + +test("todaysAccum: 端数分は合計後に四捨五入する", () => { + const now = new Date(2026, 4, 9, 12, 0); + const spans = [span(2026, 5, 9, 9, 0, 1.4), span(2026, 5, 9, 10, 0, 1.4)]; + const acc = todaysAccum(now, spans); + assert.equal(acc.totalMin, 3); // 2.8 → 3 +}); + +test("todaysAccum: startedAt が当日なら endedAt が翌日でも当日に丸ごと計上(跨ぎ仕様)", () => { + const now = new Date(2026, 4, 9, 12, 0); + const spans = [ + { startedAt: new Date(2026, 4, 9, 23, 50), endedAt: new Date(2026, 4, 10, 0, 20), min: 30 }, + ]; + const acc = todaysAccum(now, spans); + assert.equal(acc.totalMin, 30); // startedAt 基準で当日に全量 + assert.equal(acc.spans.length, 1); +});