test: the cookie signer and test modules read the never-back clock too
The docker VM's clock steps back: itsdangerous refused a cookie signed in the next second (a random 401 right after a sign-in), and a test compared an app stamp with the raw wall clock.
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
"""The suite's clock (conftest `_clock_never_goes_back`), DB-lane.
|
||||
|
||||
The wall clock of the docker VM the suite runs in steps BACK now and then (measured: up to 0.5 s).
|
||||
These hold the two places that read it besides the app: the session cookie's signature, and a test
|
||||
comparing a stamp the app wrote with "now".
|
||||
"""
|
||||
|
||||
import time
|
||||
from datetime import datetime, timezone
|
||||
from types import SimpleNamespace
|
||||
|
||||
import itsdangerous.timed
|
||||
|
||||
_PW = "correct horse battery"
|
||||
|
||||
|
||||
def test_a_cookie_signed_just_before_the_clock_steps_back_still_signs_in(
|
||||
client, make_user, monkeypatch
|
||||
):
|
||||
# The signature carries whole seconds and itsdangerous refuses one from the future: a step back
|
||||
# across a second boundary refused the cookie a sign-in had just set (a 401 at random).
|
||||
clock = [int(time.time()) + 0.2]
|
||||
monkeypatch.setattr(itsdangerous.timed, "time", SimpleNamespace(time=lambda: clock[0]))
|
||||
make_user(email="clock@example.com", password=_PW)
|
||||
r = client.post("/auth/password-login", json={"email": "clock@example.com", "password": _PW})
|
||||
assert r.status_code == 200
|
||||
clock[0] -= 0.5
|
||||
assert client.get("/api/me/notifications").status_code == 200
|
||||
|
||||
|
||||
def test_a_test_reads_the_clock_the_app_stamps_with(monkeypatch):
|
||||
# A test asserting "the app's stamp is not in the future" compared it with the wall clock, which
|
||||
# may have stepped back since the app read its own.
|
||||
import conftest
|
||||
from app import diagnostics
|
||||
|
||||
# The app reads 12:00:01, then the wall clock steps back half a second (a time past any real one,
|
||||
# so a test reading the wall clock instead is behind the stamp).
|
||||
later = datetime(2099, 1, 1, 12, 0, 1, tzinfo=timezone.utc)
|
||||
stepped_back = datetime(2099, 1, 1, 12, 0, 0, 500000, tzinfo=timezone.utc)
|
||||
readings = iter([later])
|
||||
|
||||
class Stepping(datetime):
|
||||
@classmethod
|
||||
def now(cls, tz=None):
|
||||
return next(readings, stepped_back)
|
||||
|
||||
monkeypatch.setattr(conftest._NeverBack, "clock", Stepping)
|
||||
monkeypatch.setattr(conftest._NeverBack, "last", None)
|
||||
stamped = diagnostics.datetime.now(timezone.utc)
|
||||
assert datetime.now(timezone.utc) >= stamped
|
||||
Reference in New Issue
Block a user