def test_add() -> None: now = datetime.now() stats.add( amount=Decimal(10), ts=now, ) expected_stats = { 'avg': Decimal(10), 'count': 1, 'max': Decimal(10), 'min': Decimal(10), 'sum': Decimal(10), } assert stats.get() == expected_stats stats.add( amount=Decimal(-5), ts=now, ) expected_stats = { 'avg': Decimal(2.5), 'count': 2, 'max': Decimal(10), 'min': Decimal(-5), 'sum': Decimal(5), } assert stats.get() == expected_stats
async def post(request: web.Request) -> web.Response: data = await request.json() try: amount = data['amount'] timestamp = data['timestamp'] except KeyError: return web.Response(status=400) try: amount = Decimal(amount) except InvalidOperation: return web.Response(status=400) try: ts = datetime.strptime(timestamp, '%Y-%m-%dT%H:%M:%S.%fZ') except ValueError: return web.Response(status=400) try: stats.add(amount, ts) except StatTooOld: status = 204 logger.warning('Ignored old stat') except StatInTheFuture: status = 422 logger.warning('Ignored stat in the future') else: status = 201 await stats.sweep_at(ts + timedelta(seconds=61)) logger.info('Received transaction') return web.Response(status=status)
async def test_get_rounding( client: _TestClient, amount: Decimal, expected_avg: str, ) -> None: stats.add( amount=amount, ts=datetime.now(), ) response = await client.get('/statistics') assert response.status == 200 data = await response.json() assert data['avg'] == expected_avg
async def test_get_simple( client: _TestClient, stats_api_empty: Dict, ) -> None: response = await client.get('/statistics') assert response.status == 200 data = await response.json() assert data == stats_api_empty stats.add( amount=Decimal(1), ts=datetime.now(), ) response = await client.get('/statistics') assert response.status == 200 data = await response.json() assert data == { 'avg': '1.00', 'count': 1, 'max': '1.00', 'min': '1.00', 'sum': '1.00', }
def test_sweep() -> None: initial_datetime = datetime( year=2018, month=10, day=17, hour=0, minute=0, second=0, ) with freeze_time(initial_datetime) as frozen_datetime: # [ # (10, t0), # ] stats.add( amount=Decimal(10), ts=datetime.now(), ) expected_stats = { 'avg': Decimal(10), 'count': 1, 'max': Decimal(10), 'min': Decimal(10), 'sum': Decimal(10), } assert stats.get() == expected_stats # [ # (10, t0), # (5, t0 + 21), # ] frozen_datetime.tick(delta=timedelta(seconds=21)) stats.sweep() stats.add( amount=Decimal(5), ts=datetime.now(), ) expected_stats = { 'avg': Decimal(7.5), 'count': 2, 'max': Decimal(10), 'min': Decimal(5), 'sum': Decimal(15), } assert stats.get() == expected_stats # [ # (10, t0), # (5, t0 + 21), # (-3, t0 + 42), # ] frozen_datetime.tick(delta=timedelta(seconds=21)) stats.sweep() stats.add( amount=Decimal(-3), ts=datetime.now(), ) expected_stats = { 'avg': Decimal(4), 'count': 3, 'max': Decimal(10), 'min': Decimal(-3), 'sum': Decimal(12), } assert stats.get() == expected_stats # [ # (10, t0), expired # (5, t0 + 21) # (-3, t0 + 42) # (1, t0 + 63) # ] frozen_datetime.tick(delta=timedelta(seconds=21)) stats.sweep() stats.add( amount=Decimal(1), ts=datetime.now(), ) expected_stats = { 'avg': Decimal(1), 'count': 3, 'max': Decimal(5), 'min': Decimal(-3), 'sum': Decimal(3), } assert stats.get() == expected_stats
def test_stat_in_the_future() -> None: with pytest.raises(StatInTheFuture): stats.add(amount=Decimal(10), ts=datetime.now() + timedelta(seconds=1))
def test_stat_too_old() -> None: stats.add(amount=Decimal(10), ts=datetime.now() - timedelta(seconds=59)) with pytest.raises(StatTooOld): stats.add(amount=Decimal(10), ts=datetime.now() - timedelta(seconds=60))