summaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/tests
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-08-15 00:50:05 +0200
committerChristophe Besson <cbesson@gmail.com>2026-08-15 00:50:05 +0200
commitf4d04741379e77d2ef86cccc93856e590bfe1082 (patch)
treee3a094f9a8778cfcb8b3bd49ca422a28e2434104 /packages/meshbay-hub/tests
parent76724252d08162d4df39090af19796054bf4add8 (diff)
downloadmeshbay-f4d04741379e77d2ef86cccc93856e590bfe1082.tar.gz
feat(logs): keep the username on records the account no longer answers for
The connection log took the name from a join on `users`, and deletion tombstones that row — so every record belonging to a deleted account reported `deleted-3f9a1c`, which is the one answer that helps nobody. The log is kept for a legal retention period precisely so it can say who did what; losing the name at deletion kept the data and lost the point of it. `ip_logs.username` is written as the account is erased, and stays NULL while the account is alive, where the join is better because it cannot go stale. The admin view prefers the stored name when there is one: the join still answers after deletion, just with the tombstone. Releasing the username for re-registration and keeping it in the log are separate things, and the guide now says so. On the node side, the pre-proof audit line records the username the session already knew, instead of leaving the column empty. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-hub/tests')
-rw-r--r--packages/meshbay-hub/tests/test_account_deletion.py37
1 files changed, 37 insertions, 0 deletions
diff --git a/packages/meshbay-hub/tests/test_account_deletion.py b/packages/meshbay-hub/tests/test_account_deletion.py
index 0ae70f4..cddb5d0 100644
--- a/packages/meshbay-hub/tests/test_account_deletion.py
+++ b/packages/meshbay-hub/tests/test_account_deletion.py
@@ -180,3 +180,40 @@ async def test_only_an_admin_may_delete_someone_else(client, db_session):
me = await client.get("/v1/users/me",
headers={"Authorization": f"Bearer {victim_token}"})
assert me.status_code == 200
+
+
+@pytest.mark.asyncio
+async def test_the_log_still_says_who_it_was(client, db_session):
+ """
+ The point of keeping the log is being able to answer who did what. Taking
+ the name from a join meant the answer became "deleted-3f9a1c" the moment
+ anyone deleted their account — for exactly the records that get asked about.
+ """
+ from meshbay_hub.db.models import IPLog
+
+ token, password = await _register(client, "traceable")
+ uid = (await db_session.execute(
+ select(User.id).where(User.username == "traceable"))).scalar_one()
+
+ await client.request("DELETE", "/v1/users/me",
+ headers={"Authorization": f"Bearer {token}"},
+ json={"auth_key": _auth_key(password, "traceable")})
+
+ rows = (await db_session.execute(
+ select(IPLog).where(IPLog.user_id == uid))).scalars().all()
+ assert rows, "registration should have been logged"
+ assert all(r.username == "traceable" for r in rows), \
+ "the log lost the name it exists to record"
+
+ admin_token, _ = await _register(client, "logreader")
+ from meshbay_hub.db.models import User as U
+ admin = (await db_session.execute(
+ select(U).where(U.username == "logreader"))).scalar_one()
+ admin.role = "admin"
+ await db_session.commit()
+
+ r = await client.get(f"/v1/admin/logs?user_id={uid}",
+ headers={"Authorization": f"Bearer {admin_token}"})
+ assert r.status_code == 200, r.text
+ names = {e["username"] for e in r.json()["logs"]}
+ assert names == {"traceable"}, f"admin view shows {names}"