From db4663380b93d19fd30ef5c41dc478f795454cf8 Mon Sep 17 00:00:00 2001 From: Hermes Date: Thu, 21 May 2026 14:07:57 +0000 Subject: [PATCH] CUB-196: fix future timestamps, negative battery clamp, boundary tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Grimm review fixes: - formatRelativeTime: guard future timestamps (clock skew) → 'unknown' - battery display: clamp negative values to 0% - formatTimeLeft: floor fractional seconds - Tests: +4 (future timestamp, negative battery, 15%/50% boundaries) --- src/components/CameraCard.test.tsx | 29 +++++++++++++++++++++++++++++ src/components/CameraCard.tsx | 5 +++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/components/CameraCard.test.tsx b/src/components/CameraCard.test.tsx index 261cd6c..28e955c 100644 --- a/src/components/CameraCard.test.tsx +++ b/src/components/CameraCard.test.tsx @@ -131,4 +131,33 @@ describe('CameraCard', () => { ) expect(screen.getByText('unknown')).toBeInTheDocument() }) + + it('shows "unknown" when last_seen is in the future', () => { + const future = new Date(Date.now() + 86400000).toISOString() // +1 day + render() + expect(screen.getByText('unknown')).toBeInTheDocument() + }) + + // ── Edge cases ────────────────────────────────────────────────────────── + + it('clamps negative battery_pct to 0%', () => { + render() + expect(screen.getByText('0%')).toBeInTheDocument() + }) + + it('shows exact boundary: 15% battery → yellow bar', () => { + const { container } = render( + , + ) + const bar = container.querySelector('[role="progressbar"] div') + expect(bar?.className).toContain('bg-rig-warning') + }) + + it('shows exact boundary: 50% battery → green bar', () => { + const { container } = render( + , + ) + const bar = container.querySelector('[role="progressbar"] div') + expect(bar?.className).toContain('bg-rig-success') + }) }) diff --git a/src/components/CameraCard.tsx b/src/components/CameraCard.tsx index 868a5a1..5f1d59a 100644 --- a/src/components/CameraCard.tsx +++ b/src/components/CameraCard.tsx @@ -8,6 +8,7 @@ function formatRelativeTime(iso: string): string { if (isNaN(then)) return 'unknown' const diffSec = Math.floor((Date.now() - then) / 1000) + if (diffSec < 0) return 'unknown' if (diffSec < 10) return 'just now' if (diffSec < 60) return `${diffSec}s ago` @@ -32,7 +33,7 @@ function batteryColor(pct: number | null): { bar: string; text: string } { function formatTimeLeft(sec: number): string { if (sec <= 0 || !isFinite(sec)) return '--' const m = Math.floor(sec / 60) - const s = sec % 60 + const s = Math.floor(sec % 60) return `${m}m ${s}s left` } @@ -138,7 +139,7 @@ export default function CameraCard({ camera }: CameraCardProps) { Battery - {battery_pct !== null ? `${battery_pct}%` : 'N/A'} + {battery_pct !== null ? `${Math.max(0, battery_pct)}%` : 'N/A'}