"""Tests for PUT /api/0/users/me logic.""" import pytest from hbd.server import users as users_mod def test_hash_password_roundtrip(): h = users_mod.hash_password("mysecret") assert h.startswith("pbkdf2:sha256:") assert users_mod.authenticate.__doc__ is not None # module loaded def test_password_change_requires_correct_current(tmp_path): cfg = tmp_path / ".hb.yaml" initial_hash = users_mod.hash_password("oldpass") cfg.write_text( f"hbd_port: 50004\nusers:\n alice:\n full_name: Alice\n admin: true\n password: {initial_hash}\n" ) users_mod.load_users({"users": {"alice": {"full_name": "Alice", "admin": True, "password": initial_hash}}}) # Correct current password authenticates assert users_mod.authenticate("alice", "oldpass") is not None # Wrong current password does not authenticate assert users_mod.authenticate("alice", "wrongpass") is None def test_put_users_me_writes_new_fields(tmp_path): """Simulate the write path: read config, update user, write back.""" initial_hash = users_mod.hash_password("secret") yaml_content = ( "hbd_port: 50004\n" f"users:\n alice:\n full_name: Old Name\n admin: true\n password: {initial_hash}\n" ) cfg = tmp_path / ".hb.yaml" cfg.write_text(yaml_content) from hbd.server import configio data = configio.read_roundtrip(str(cfg)) # Simulate handler updating full_name and avatar user_entry = dict(data["users"]["alice"]) user_entry["full_name"] = "New Name" user_entry["avatar"] = "/img/alice.png" data["users"]["alice"] = user_entry configio.write_config(str(cfg), data) result = configio.read_roundtrip(str(cfg)) assert result["users"]["alice"]["full_name"] == "New Name" assert result["users"]["alice"]["avatar"] == "/img/alice.png" assert result["users"]["alice"]["password"] == initial_hash # unchanged def test_put_users_me_changes_password(tmp_path): initial_hash = users_mod.hash_password("oldpass") cfg = tmp_path / ".hb.yaml" cfg.write_text( f"hbd_port: 50004\nusers:\n alice:\n full_name: Alice\n password: {initial_hash}\n" ) from hbd.server import configio data = configio.read_roundtrip(str(cfg)) new_hash = users_mod.hash_password("newpass") data["users"]["alice"]["password"] = new_hash configio.write_config(str(cfg), data) result = configio.read_roundtrip(str(cfg)) # Load users from new config and authenticate with new password new_config = {"users": dict(result["users"])} users_mod.load_users(new_config) assert users_mod.authenticate("alice", "newpass") is not None assert users_mod.authenticate("alice", "oldpass") is None def test_put_users_me_notification_channels(tmp_path): cfg = tmp_path / ".hb.yaml" cfg.write_text( "hbd_port: 50004\n" "notification_channels:\n pushover_ops:\n type: pushover\n" "users:\n alice:\n full_name: Alice\n notification_channels: []\n" ) from hbd.server import configio data = configio.read_roundtrip(str(cfg)) data["users"]["alice"]["notification_channels"] = ["pushover_ops"] configio.write_config(str(cfg), data) result = configio.read_roundtrip(str(cfg)) assert result["users"]["alice"]["notification_channels"] == ["pushover_ops"] def test_visible_channels_excludes_private_from_others(): """Private channels owned by another user must not appear in the visible set.""" from hbd.server import settings as settings_mod config = { "notification_channels": { "public_ch": {"type": "pushover", "token": "t", "user": "u"}, "alice_priv": {"type": "email", "owner": "alice", "private": True, "recipients": ["a@b.com"], "sender": "s@b.com", "smtp_server": "s"}, "bob_priv": {"type": "email", "owner": "bob", "private": True, "recipients": ["b@b.com"], "sender": "s@b.com", "smtp_server": "s"}, } } class FakeUser: def __init__(self, username, admin=False): self.username = username self.admin = admin alice = FakeUser("alice") bob = FakeUser("bob") admin = FakeUser("admin", admin=True) # Simulate _visible_channels_for_user logic (mirrors http.py implementation) def visible(user): all_channels = config.get("notification_channels") or {} if user.admin: return set(all_channels.keys()) return { name for name, cfg in all_channels.items() if not cfg.get("private") or cfg.get("owner") == user.username } assert visible(alice) == {"public_ch", "alice_priv"} assert visible(bob) == {"public_ch", "bob_priv"} assert visible(admin) == {"public_ch", "alice_priv", "bob_priv"}