From 9f32e5ca24fd19cfa0258c10b1c791ee7b1a66ff Mon Sep 17 00:00:00 2001 From: QuantCode Agent Date: Wed, 24 Jun 2026 17:00:57 +0000 Subject: [PATCH] fix: repair failing tests across calculator, string, task, date, and validator utils - calculator: divide() now throws on division by zero - string-utils: implement truncate() and fix wordCount() whitespace handling - task-manager: implement missing remove/update/sortBy methods - date-utils: fix formatRelative off-by-one (Math.round day boundaries) - validator: isEmail accepts long TLDs; isUrl accepts URLs with ports --- src/calculator.ts | 2 +- src/date-utils.ts | 6 ++---- src/string-utils.ts | 20 +++++++++++++++----- src/task-manager.ts | 24 ++++++++++++++++-------- src/validator.ts | 9 +++------ 5 files changed, 37 insertions(+), 24 deletions(-) diff --git a/src/calculator.ts b/src/calculator.ts index 68b894d..8fa3de6 100644 --- a/src/calculator.ts +++ b/src/calculator.ts @@ -15,7 +15,7 @@ export function multiply(a: number, b: number): number { return a * b } -// BUG: Division by zero is not handled export function divide(a: number, b: number): number { + if (b === 0) throw new Error("Division by zero") return a / b } diff --git a/src/date-utils.ts b/src/date-utils.ts index 37272a7..7327082 100644 --- a/src/date-utils.ts +++ b/src/date-utils.ts @@ -5,16 +5,14 @@ /** * Format a date as a human-readable relative string. * e.g. "2 days ago", "just now", "in 3 hours" - * - * BUG: off-by-one — uses Math.floor where Math.round is needed for days, - * causing "1 day ago" to appear for anything from 12h to 47h. + * Day boundaries are rounded (e.g. 36 hours → "2 days ago"). */ export function formatRelative(date: Date, now: Date = new Date()): string { const diffMs = now.getTime() - date.getTime() const diffSec = diffMs / 1000 const diffMin = diffSec / 60 const diffHours = diffMin / 60 - const diffDays = Math.floor(diffHours / 24) // BUG: should be Math.round + const diffDays = Math.round(diffHours / 24) if (Math.abs(diffSec) < 60) return "just now" if (Math.abs(diffMin) < 60) { diff --git a/src/string-utils.ts b/src/string-utils.ts index 63fba18..7632cfe 100644 --- a/src/string-utils.ts +++ b/src/string-utils.ts @@ -11,10 +11,21 @@ export function reverse(str: string): string { return str.split("").reverse().join("") } -// TODO: implement truncate — should truncate at a word boundary, with "..." -// counting toward maxLength. Return unchanged if str.length <= maxLength. export function truncate(str: string, maxLength: number): string { - throw new Error("not implemented") + if (str.length <= maxLength) return str + const budget = maxLength - 3 + if (budget <= 0) return str.slice(0, maxLength) + const words = str.split(" ") + let result = "" + for (const word of words) { + const candidate = result ? result + " " + word : word + if (candidate.length <= budget) { + result = candidate + } else { + break + } + } + return (result || str.slice(0, budget)) + "..." } export function slugify(str: string): string { @@ -24,8 +35,7 @@ export function slugify(str: string): string { .replace(/^-|-$/g, "") } -// BUG: This doesn't handle multiple consecutive spaces export function wordCount(str: string): number { if (!str.trim()) return 0 - return str.split(" ").length + return str.trim().split(/\s+/).length } diff --git a/src/task-manager.ts b/src/task-manager.ts index a920e85..9436e10 100644 --- a/src/task-manager.ts +++ b/src/task-manager.ts @@ -52,20 +52,28 @@ export class TaskManager { return true } - // TODO: implement — remove a task by id, return true if removed, false if not found remove(id: string): boolean { - throw new Error("not implemented") + if (!this.tasks.has(id)) return false + this.tasks.delete(id) + return true } - // TODO: implement — update title/description/priority of a task - // return true if updated, false if not found update(id: string, changes: Partial>): boolean { - throw new Error("not implemented") + const task = this.tasks.get(id) + if (!task) return false + if (changes.title !== undefined) task.title = changes.title + if (changes.description !== undefined) task.description = changes.description + if (changes.priority !== undefined) task.priority = changes.priority + return true } - // TODO: implement — return all tasks sorted by the given field - // priority sort order: high > medium > low sortBy(field: "priority" | "createdAt" | "status"): Task[] { - throw new Error("not implemented") + const priorityOrder: Record = { high: 0, medium: 1, low: 2 } + const statusOrder: Record = { pending: 0, in_progress: 1, completed: 2 } + return Array.from(this.tasks.values()).sort((a, b) => { + if (field === "priority") return priorityOrder[a.priority] - priorityOrder[b.priority] + if (field === "createdAt") return a.createdAt.getTime() - b.createdAt.getTime() + return statusOrder[a.status] - statusOrder[b.status] + }) } } diff --git a/src/validator.ts b/src/validator.ts index 27bf385..9fd05a0 100644 --- a/src/validator.ts +++ b/src/validator.ts @@ -4,13 +4,10 @@ /** * Returns true if the string is a valid email address. - * - * BUG: the regex does not allow subdomains (e.g. user@mail.example.com fails) - * and rejects valid TLDs longer than 4 chars (e.g. .museum, .travel). + * Supports subdomains and TLDs of any length. */ export function isEmail(value: string): boolean { - // BUG: too restrictive — missing subdomain support and long TLDs - return /^[^\s@]+@[^\s@]+\.[a-zA-Z]{2,4}$/.test(value) + return /^[^\s@]+@[^\s@]+\.[a-zA-Z]{2,}$/.test(value) } /** @@ -22,7 +19,7 @@ export function isUrl(value: string): boolean { try { const url = new URL(value) // BUG: only allows http/https but also rejects valid port usage - return (url.protocol === "http:" || url.protocol === "https:") && url.port === "" + return url.protocol === "http:" || url.protocol === "https:" } catch { return false }