package me.khodak.claudeusage import org.junit.Assert.assertNotNull import org.junit.Assert.assertNull import org.junit.Assert.assertTrue import org.junit.Test /** * Pure-JVM tests for PaceCalc.compute. The clock is injected via the `now` parameter, so these are * deterministic and require no Android framework. */ class PaceCalcTest { private val window = PaceCalc.SESSION_WINDOW_MS // 5h @Test fun computeReturnsPaceForValidFutureReset() { // Place "now" halfway through the window (elapsedFraction = 0.5, within [0.03, 1.0)). val resetEpoch = 10_000_000_000L val now = resetEpoch - window / 2 val pace = PaceCalc.compute(usedPct = 25f, resetEpoch = resetEpoch, windowMs = window, now = now) assertNotNull(pace) val p = pace!! // markerPct mirrors elapsedFraction*100 → ~50, and must stay in 0..100. assertTrue("markerPct in range", p.markerPct in 0..100) assertEquals50(p.markerPct) } @Test fun markerPctAlwaysInRangeNearWindowEnd() { // Near the end of the window (95% elapsed, still < 1.0) marker approaches 100, never exceeds. // Kept off the exact boundary so Float rounding of (window-elapsed)/window can't tip it to 1.0. val resetEpoch = 10_000_000_000L val now = resetEpoch - (window * 5L / 100L) // 95% elapsed val pace = PaceCalc.compute(usedPct = 80f, resetEpoch = resetEpoch, windowMs = window, now = now) assertNotNull(pace) assertTrue(pace!!.markerPct in 0..100) } @Test fun computeReturnsNullForPastReset() { // Reset already passed → elapsedFraction >= 1.0 → null. val resetEpoch = 10_000_000_000L val now = resetEpoch + 60_000L assertNull(PaceCalc.compute(usedPct = 50f, resetEpoch = resetEpoch, windowMs = window, now = now)) } @Test fun computeReturnsNullTooEarlyInWindow() { // Less than 3% elapsed → not enough signal → null. val resetEpoch = 10_000_000_000L val now = resetEpoch - window + (window * 0.01f).toLong() // 1% elapsed assertNull(PaceCalc.compute(usedPct = 5f, resetEpoch = resetEpoch, windowMs = window, now = now)) } @Test fun computeReturnsNullForNonPositiveReset() { assertNull(PaceCalc.compute(usedPct = 50f, resetEpoch = 0L, windowMs = window)) assertNull(PaceCalc.compute(usedPct = 50f, resetEpoch = -1L, windowMs = window)) } @Test fun computeReturnsNullForNegativeUsedPct() { val resetEpoch = 10_000_000_000L val now = resetEpoch - window / 2 assertNull(PaceCalc.compute(usedPct = -1f, resetEpoch = resetEpoch, windowMs = window, now = now)) } private fun assertEquals50(markerPct: Int) { // elapsedFraction 0.5 → (0.5*100).toInt() == 50. org.junit.Assert.assertEquals(50, markerPct) } }