"""Tests on Lab 01's recorded run (October 2026, Apple M4 Max, one uvicorn worker, Postgres 18):
the URL shortener's estimate, the lookup's measured latencies, and the load test at 64 clients.
Run: pytest test_estimate_practice.py"""
from estimate_practice import (littles_L, min_n_bounded, peak_rps, percentile, samples_above,
                               servers_needed)

# 10M daily users, 20 clicks and 0.2 new links each a day, a 3x busiest hour
SHORTENER = dict(daily_users=10e6, reads_per_user=20, writes_per_user=0.2, peak_factor=3)


def test_peak_rps():
    assert round(peak_rps(**SHORTENER)) == 7014  # 6,944 reads + 69 writes a second at peak
    assert round(peak_rps(10e6, 20, 0, 1)) == 2315  # the average clicks a second
    assert peak_rps(**SHORTENER) == 3 * peak_rps(10e6, 20, 0.2, 1)


def test_servers_needed():
    peak = peak_rps(**SHORTENER)
    assert servers_needed(peak, 1000) == 11  # the whiteboard's 1,000 a second per server
    assert servers_needed(peak, 6777) == 2  # the measured flat top at 64 clients
    assert servers_needed(1, 1000) == 1
    # which guess moves it most: 300-5,000 a second per server spans 34 to 3 servers
    assert (servers_needed(peak, 300), servers_needed(peak, 5000)) == (34, 3)
    assert (servers_needed(peak_rps(5e6, 20, 0.2, 3), 1000),
            servers_needed(peak_rps(20e6, 20, 0.2, 3), 1000)) == (6, 21)


def test_percentile_matches_numpy_linear():
    x = [0.4, 0.9, 0.5, 0.7, 1.6, 0.6, 0.55, 2.4, 0.8, 0.45]  # ms, out of order on purpose
    assert round(percentile(x, 50), 4) == 0.65  # halfway between 0.6 and 0.7
    assert round(percentile(x, 90), 4) == 1.68
    assert round(percentile(x, 99), 4) == 2.328  # between the two slowest, not the max
    assert percentile(x, 100) == 2.4 and percentile(x, 0) == 0.4
    assert percentile([3.0], 99) == 3.0


def test_samples_above():
    assert round(samples_above(500, 99.9), 2) == 0.5  # p99.9 from 500 requests is the max
    assert round(samples_above(20000, 99), 1) == 200  # the lookup's p99 rests on 200
    assert round(samples_above(1000, 99), 1) == 10  # enough to pin a p99 down


def test_min_n_bounded():
    assert min_n_bounded(99) == 368  # the lab's number
    # About 3.7 samples above it every time. (The lab's search steps n up 5% at a time, so it
    # prints 3,735 and 37,094 for these two; the exact smallest are a little lower.)
    assert min_n_bounded(99.9) == 3688
    assert 3.6 < samples_above(min_n_bounded(99.9), 99.9) < 3.8
    assert min_n_bounded(50) == 6


def test_littles_law():
    # 64 clients: 6,777 requests a second, 9.42 ms each at the client
    assert round(littles_L(6776.875, 0.0094246)) == 64
    # inside the app: the app's own time per request gives 38.5, as its own counter did
    inside = littles_L(6776.875, 0.0056784)
    assert round(inside, 1) == 38.5
    assert round(64 - inside, 1) == 25.5  # waiting outside the app
    assert round(littles_L(1058.125, 0.00094227), 2) == 1.0  # one client: one in flight
