import os import tempfile import unittest from pathlib import Path from unittest.mock import patch from webui import login_lock, users from webui.auth import hash_password class MultiUserTests(unittest.TestCase): def setUp(self): self.temp_dir = tempfile.TemporaryDirectory() self.users_path = Path(self.temp_dir.name) / "webui_users.json" self.lock_path = Path(self.temp_dir.name) / "login-workspace.lock.json" self.accounts = [ {"account_ref": "acc-1", "username": "头像是本人", "unique_id": "111", "targets": [], "enabled": True}, {"account_ref": "acc-2", "username": "你成功捕捉一只野生妖孽", "unique_id": "222", "targets": [], "enabled": True}, {"account_ref": "acc-3", "username": "管理员账号", "unique_id": "333", "targets": [], "enabled": True}, ] self.user_file_patch = patch.object(users, "USERS_FILE", self.users_path) self.user_file_patch.start() self.ensure_patch = patch.object(users, "get_userData", return_value=self.accounts) self.ensure_patch.start() self.save_accounts_patch = patch.object(users, "save_userData") self.save_accounts_patch.start() self.addCleanup(self.ensure_patch.stop) self.addCleanup(self.save_accounts_patch.stop) self.addCleanup(self.user_file_patch.stop) self.addCleanup(self.temp_dir.cleanup) def test_user_creation_auth_and_unique_assignment(self): a, changed = users.ensure_account_refs(self.accounts) self.assertFalse(changed) ref = a[0]["account_ref"] created = users.create_web_user("zxb", "zxb123456", account_refs=[ref]) self.assertEqual([ref], created["account_refs"]) identity = users.authenticate("zxb", "zxb123456") self.assertEqual("user", identity["role"]) self.assertEqual([ref], identity["account_refs"]) self.assertIsNone(users.authenticate("zxb", "wrong")) with self.assertRaises(users.UserStoreError): users.create_web_user("zcf", "zcf123456", account_refs=[ref]) def test_visible_accounts_and_admin_reassignment(self): accounts, _ = users.ensure_account_refs(self.accounts) first_ref = accounts[0]["account_ref"] second_ref = accounts[1]["account_ref"] users.create_web_user("zxb", "secret", account_refs=[first_ref]) principal = {"role": "user", "account_refs": [first_ref]} self.assertEqual([first_ref], [a["account_ref"] for a in users.get_visible_accounts(principal, accounts)]) users.update_web_user("zxb", account_refs=[second_ref]) self.assertEqual([second_ref], users.find_web_user("zxb")["account_refs"]) self.assertTrue(users.delete_web_user("zxb")) self.assertEqual([], users.get_web_users()) def test_fifo_queue_promotes_after_active_release(self): with patch.object(login_lock, "LOCK_PATH", self.lock_path): first = login_lock.request_workspace(username="zxb", session_id="s1", account_ref="a1", mode="relogin") second = login_lock.request_workspace(username="zcf", session_id="s2", account_ref="", mode="add") self.assertEqual("active", first["state"]) self.assertEqual("queued", second["state"]) self.assertEqual("add", second["request"]["mode"]) self.assertEqual(1, second["position"]) self.assertEqual("queued", login_lock.workspace_status(username="zcf", session_id="s2")["state"]) released = login_lock.begin_release(username="zxb", session_id="s1", ticket=first["request"]["ticket"], account_ref="a1") self.assertIsNotNone(released) promoted = login_lock.finish_transition() self.assertEqual("zcf", promoted["username"]) self.assertEqual("active", login_lock.workspace_status(username="zcf", session_id="s2")["state"]) def test_login_workspace_is_serialized_and_expires(self): with patch.object(login_lock, "LOCK_PATH", self.lock_path), patch.object(login_lock, "LOCK_TTL_SECONDS", 1): ok, lock = login_lock.acquire(username="zxb", session_id="s1", account_ref="a1") self.assertTrue(ok) self.assertTrue(login_lock.owns(lock, username="zxb", session_id="s1", account_ref="a1")) blocked, current = login_lock.acquire(username="zcf", session_id="s2", account_ref="a2") self.assertFalse(blocked) self.assertEqual("zxb", current["username"]) self.assertTrue(login_lock.refresh(username="zxb", session_id="s1", account_ref="a1")) self.assertTrue(login_lock.release(username="zxb", session_id="s1")) self.assertIsNone(login_lock.get_lock()) if __name__ == "__main__": unittest.main()