stackchain-dashboard/tests/test_session_store.py
timmy 7034ec55ed
All checks were successful
CI / lint (pull_request) Successful in 34s
CI / build-frontend (pull_request) Successful in 6s
feat: review and revoke active devices (#325)
2026-08-08 17:59:17 +00:00

122 lines
4.9 KiB
Python

import sqlite3
import pytest
from src import session_store
from src.session_store import SessionStore, SessionStoreError
def test_revocation_is_durable_and_scoped_to_one_session(tmp_path):
now = [1_000.0]
database = tmp_path / "sessions.sqlite3"
first = SessionStore(database, clock=lambda: now[0])
first.activate("first-session-secret", 2_000)
first.activate("second-session-secret", 2_000)
reconstructed = SessionStore(database, clock=lambda: now[0])
reconstructed.revoke("first-session-secret")
assert reconstructed.is_active("first-session-secret", 2_000) is False
assert reconstructed.is_active("second-session-secret", 2_000) is True
assert b"first-session-secret" not in database.read_bytes()
assert b"second-session-secret" not in database.read_bytes()
def test_revoke_all_is_durable_and_does_not_block_future_sessions(tmp_path):
now = [1_000.0]
database = tmp_path / "sessions.sqlite3"
first = SessionStore(database, clock=lambda: now[0])
first.activate("phone-session", 2_000)
first.activate("laptop-session", 2_000)
first.revoke_all()
reconstructed = SessionStore(database, clock=lambda: now[0])
assert reconstructed.is_active("phone-session", 2_000) is False
assert reconstructed.is_active("laptop-session", 2_000) is False
reconstructed.activate("new-session", 2_000)
assert reconstructed.is_active("new-session", 2_000) is True
def test_validation_does_not_create_a_missing_registry(tmp_path):
database = tmp_path / "sessions.sqlite3"
store = SessionStore(database, clock=lambda: 1_000.0)
with pytest.raises(SessionStoreError):
store.is_active("unknown-session", 2_000)
assert database.exists() is False
def test_validation_executes_only_a_read_query(tmp_path, monkeypatch):
store = SessionStore(tmp_path / "sessions.sqlite3", clock=lambda: 1_000.0)
store.activate("active-session", 2_000)
statements = []
connect = sqlite3.connect
def traced_connect(*args, **kwargs):
connection = connect(*args, **kwargs)
connection.set_trace_callback(statements.append)
return connection
monkeypatch.setattr(session_store.sqlite3, "connect", traced_connect)
assert store.is_active("active-session", 2_000) is True
assert [statement.split()[0].upper() for statement in statements] == ["SELECT"]
def test_expired_sessions_are_rejected_without_writing_during_validation(tmp_path):
now = [1_000.0]
store = SessionStore(tmp_path / "sessions.sqlite3", clock=lambda: now[0])
store.activate("expiring-session", 1_001)
now[0] = 1_001.0
assert store.is_active("expiring-session", 1_001) is False
with sqlite3.connect(store.path) as connection:
assert connection.execute("SELECT COUNT(*) FROM active_sessions").fetchone() == (1,)
def test_active_devices_are_listed_without_exposing_session_secrets(tmp_path):
store = SessionStore(tmp_path / "sessions.sqlite3", clock=lambda: 1_000.0)
store.activate("phone-session-secret", 2_000, device_label="Pixel 9")
store.activate("laptop-session-secret", 3_000, device_label="Work laptop")
devices = store.list_active("phone-session-secret")
assert [device.device_label for device in devices] == ["Work laptop", "Pixel 9"]
assert [device.current for device in devices] == [False, True]
assert all(device.management_id for device in devices)
assert all(device.created_at == 1_000 for device in devices)
assert "phone-session-secret" not in repr(devices)
def test_revoke_managed_device_removes_only_the_selected_session(tmp_path):
store = SessionStore(tmp_path / "sessions.sqlite3", clock=lambda: 1_000.0)
store.activate("phone", 2_000, device_label="Phone")
store.activate("laptop", 2_000, device_label="Laptop")
phone = next(device for device in store.list_active("laptop") if device.device_label == "Phone")
assert store.revoke_managed(phone.management_id) is True
assert store.is_active("phone", 2_000) is False
assert store.is_active("laptop", 2_000) is True
assert store.revoke_managed(phone.management_id) is False
def test_existing_session_registry_migrates_without_invalidating_sessions(tmp_path):
database = tmp_path / "sessions.sqlite3"
digest = SessionStore._digest("existing-session")
with sqlite3.connect(database) as connection:
connection.execute(
"CREATE TABLE active_sessions (session_hash TEXT PRIMARY KEY, expires_at INTEGER NOT NULL)"
)
connection.execute("INSERT INTO active_sessions VALUES (?, ?)", (digest, 2_000))
store = SessionStore(database, clock=lambda: 1_000.0)
store.activate("new-session", 3_000, device_label="New phone")
assert store.is_active("existing-session", 2_000) is True
devices = store.list_active("existing-session")
assert len(devices) == 2
assert next(device for device in devices if device.current).device_label == "Existing device"