"""Tests for the file store (read/write users, permissions, config).""" import stat import pytest from local_identity.store import ( init_dirs, store_exists, write_user, read_user, list_users, read_config, write_config, _users_dir, _store_dir, ) from local_identity.user import UserRecord, make_test_user ALICE = UserRecord(username="alice", fullname="Alice Smith", email="alice@example.com") BOB = UserRecord(username="bob", fullname="Bob Jones", email="bob@example.com") class TestDirInit: def test_creates_store_dir(self, tmp_store): assert not tmp_store.exists() init_dirs() assert tmp_store.exists() def test_store_dir_mode_700(self, tmp_store): init_dirs() mode = stat.S_IMODE(tmp_store.stat().st_mode) assert mode == 0o700, f"expected 0o700, got 0o{mode:03o}" def test_users_dir_mode_700(self, tmp_store): init_dirs() users = tmp_store / "users" mode = stat.S_IMODE(users.stat().st_mode) assert mode == 0o700, f"expected 0o700, got 0o{mode:03o}" def test_idempotent(self, tmp_store): init_dirs() init_dirs() # should not raise assert tmp_store.exists() def test_store_exists_false_before_init(self, tmp_store): assert not store_exists() def test_store_exists_true_after_init(self, tmp_store): init_dirs() assert store_exists() class TestWriteReadUser: def test_roundtrip(self, tmp_store): init_dirs() write_user(ALICE) loaded = read_user("alice") assert loaded.username == "alice" assert loaded.fullname == "Alice Smith" assert loaded.email == "alice@example.com" def test_file_mode_600(self, tmp_store): init_dirs() write_user(ALICE) path = _users_dir() / "alice.yaml" mode = stat.S_IMODE(path.stat().st_mode) assert mode == 0o600, f"expected 0o600, got 0o{mode:03o}" def test_overwrite_same_user(self, tmp_store): init_dirs() write_user(ALICE) updated = UserRecord(username="alice", fullname="Alice Updated", email="alice@example.com") write_user(updated) assert read_user("alice").fullname == "Alice Updated" def test_read_nonexistent_raises(self, tmp_store): init_dirs() with pytest.raises(FileNotFoundError, match="nobody"): read_user("nobody") class TestListUsers: def test_empty_store(self, tmp_store): init_dirs() assert list_users() == [] def test_lists_all_users(self, tmp_store): init_dirs() write_user(ALICE) write_user(BOB) usernames = {u.username for u in list_users()} assert usernames == {"alice", "bob"} def test_includes_test_users(self, tmp_store): init_dirs() write_user(ALICE) write_user(make_test_user(ALICE, 1)) write_user(make_test_user(ALICE, 2)) usernames = {u.username for u in list_users()} assert usernames == {"alice", "alice1", "alice2"} def test_sorted_by_filename(self, tmp_store): init_dirs() write_user(BOB) write_user(ALICE) names = [u.username for u in list_users()] assert names == sorted(names) class TestConfig: def test_read_config_empty(self, tmp_store): init_dirs() assert read_config() == {} def test_write_read_config(self, tmp_store): init_dirs() write_config({"email": "tegwick@example.com"}) assert read_config()["email"] == "tegwick@example.com" def test_config_mode_600(self, tmp_store): init_dirs() write_config({"email": "x@y.com"}) from local_identity.store import _config_file mode = stat.S_IMODE(_config_file().stat().st_mode) assert mode == 0o600 class TestIdempotency: def test_reinit_produces_same_users(self, tmp_store): init_dirs() write_user(ALICE) write_user(make_test_user(ALICE, 1)) write_user(make_test_user(ALICE, 2)) # Simulate --force re-init with same inputs init_dirs() write_user(ALICE) write_user(make_test_user(ALICE, 1)) write_user(make_test_user(ALICE, 2)) users = {u.username: u for u in list_users()} assert set(users.keys()) == {"alice", "alice1", "alice2"} assert users["alice1"].source_user == "alice" assert users["alice1"].email == "alice+test1@example.com"