diff options
Diffstat (limited to 'packages/meshbay-hub/tests/test_auth_race.py')
| -rw-r--r-- | packages/meshbay-hub/tests/test_auth_race.py | 62 |
1 files changed, 62 insertions, 0 deletions
diff --git a/packages/meshbay-hub/tests/test_auth_race.py b/packages/meshbay-hub/tests/test_auth_race.py new file mode 100644 index 0000000..32e7e49 --- /dev/null +++ b/packages/meshbay-hub/tests/test_auth_race.py @@ -0,0 +1,62 @@ +""" +A sign-out during a token renewal must not leave half a session behind. + +A tab left open overnight is exactly this race: the idle watch signs the browser +out at the same moment the renewal fires — the hub's log has the two requests +one second apart. `refreshAccessToken` came back to an `_auth` that was already +null and wrote `{ ..._auth, token, refreshToken }`. Spreading null is silent, so +what landed in localStorage was a token with **no identity at all**. + +That object is worse than no session. The app believes it is one, renders the +signed-in interface from it, and throws on the first field it reads — +`user.username[0]`, which is "Cannot read properties of undefined (reading +'0')". Every load afterwards is blank, and because it is stored it survives +reloads, clearing the cache and restarting the device. Reported from a phone, +diagnosed from a screenshot of `boot-guard.js` naming the error on screen. + +Two fixes, and both are needed: the sign-out now wins the race, and `loadAuth` +treats an identity-less object as signed out, so a browser already holding one +heals on its next load instead of needing someone to find the reset button. +""" + +import json +import shutil +import subprocess +import sys +from pathlib import Path + +import pytest + +HARNESS = Path(__file__).parent / "harness" / "auth_race_probe.py" + + +@pytest.fixture(scope="module") +def out(): + if shutil.which("google-chrome") is None: + pytest.skip("Chrome is not available") + proc = subprocess.run([sys.executable, str(HARNESS)], + capture_output=True, text=True, timeout=120) + data = json.loads(proc.stdout) + assert "error" not in data, f"probe failed: {proc.stdout}{proc.stderr}" + return data + + +def test_a_sign_out_during_a_renewal_wins(out): + # Not "writes something harmless": writes nothing. The sign-out is the + # later intention and there is nothing in a renewal worth keeping over it. + assert out["afterTheRace"] is None, ( + f"a session survived the sign-out: {out['afterTheRace']}") + + +def test_what_used_to_be_written_is_recognised_as_not_a_session(out): + # The healing half. Without it every browser already holding one stays + # blank for a year, and the only way out is a menu nobody finds. + assert out["poisonedLoads"] is None, out["poisonedLoads"] + assert out["poisonedLeftBehind"] is None, ( + "the identity-less object was left in storage to be read again") + + +def test_a_real_session_still_loads_untouched(out): + # The guard above must not sign anybody out. This is the case that says so. + assert out["goodLoads"] == "someone", out["goodLoads"] + assert out["goodLeftAlone"], "a valid session was cleared" |