"""Unit tests for the password context class. We are primarily testing whether the actions of the PasswordContext matches those in the low level API. """ import random import string from typing import cast import pykeepass from sshecret_admin.services.keepass import PasswordContext def random_string(length: int = 5) -> str: """Generate random string.""" chars = string.ascii_lowercase return "".join(random.choice(chars) for _ in range(length)) def create_random_entries( password_database: pykeepass.PyKeePass, amount: int = 10, prefix: str = "secret", group: pykeepass.group.Group | None = None, ) -> None: """Create some random entries.""" if not group: group = cast(pykeepass.group.Group, password_database.root_group) for n in range(amount): name = f"{prefix}-{n}" username = "NONE" password = random_string(12) password_database.add_entry( destination_group=group, title=name, username=username, password=password, ) password_database.save() def test_add_entry(password_database: pykeepass.PyKeePass) -> None: """Test add entry.""" context = PasswordContext(password_database) context.add_entry("testentry", "testsecret") entry = password_database.find_entries(title="testentry", first=True) assert entry is not None assert isinstance(entry, pykeepass.entry.Entry) assert entry.password == "testsecret" def test_get_secret(password_database: pykeepass.PyKeePass) -> None: """Test get secret.""" context = PasswordContext(password_database) context.add_entry("testentry", "testsecret") secret = context.get_secret("testentry") assert secret is not None assert secret == "testsecret" def test_get_available_secrets(password_database: pykeepass.PyKeePass) -> None: """Test get_available_secrets.""" create_random_entries(password_database, 10) context = PasswordContext(password_database) available_secrets = context.get_available_secrets() assert len(available_secrets) == 10 for n in range(10): assert f"secret-{n}" in available_secrets def test_delete_entry(password_database: pykeepass.PyKeePass) -> None: """Test deletion of entry.""" create_random_entries(password_database, 3) context = PasswordContext(password_database) available_secrets = context.get_available_secrets() assert len(available_secrets) == 3 context.delete_entry("secret-2") entry = password_database.find_entries(title="secret-2", first=True) assert entry is None available_secrets = context.get_available_secrets() assert len(available_secrets) == 2 def test_get_secret_groups(password_database: pykeepass.PyKeePass) -> None: """Test get secret groups.""" # We create a hierarchy of groups to test how that parses. root_group = password_database.root_group first_group = password_database.add_group( root_group, "level_one", notes="A group in the root" ) password_database.add_group( first_group, "level_two", notes="A group one level down" ) # Another group at the root, without a note password_database.add_group(root_group, "free_group") password_database.save() context = PasswordContext(password_database) groups = context.get_secret_groups() assert len(groups) == 3 def test_get_secret_groups_regex(password_database: pykeepass.PyKeePass) -> None: """Get secret groups matching a regex.""" # create some groups matching a pattern for n in range(4): password_database.add_group(password_database.root_group, f"foo-{n}") for n in range(3): parent_group = password_database.find_groups(name="foo-1", first=True) password_database.add_group(parent_group, f"bar-{n}") password_database.save() context = PasswordContext(password_database) foo_groups = context.get_secret_groups("foo-.*") assert len(foo_groups) == 4 bar_groups = context.get_secret_groups("bar-.*") assert len(bar_groups) == 3 def test_get_secret_groups_with_entries(password_database: pykeepass.PyKeePass) -> None: """Get secret groups with entries.""" context = PasswordContext(password_database) context.add_group("test_group", "Test Group") context.add_group("parent_group", "Test Group") context.add_group("nested_group", "Test Group", "parent_group") context.add_entry("free_entry", "test", group_name="test_group") context.add_entry("middle_entry", "test", group_name="parent_group") context.add_entry("lower_entry", "test", group_name="nested_group") groups = context.get_secret_groups() assert len(groups) == 3 for group in groups: assert len(group.entries) == 1 if group.name == "test_group": assert "free_entry" in group.entries elif group.name == "parent_group": assert "middle_entry" in group.entries elif group.name == "nested_group": assert "lower_entry" in group.entries def test_add_group(password_database: pykeepass.PyKeePass) -> None: """Test add_group.""" context = PasswordContext(password_database) context.add_group("test_group", "Test Group") assert password_database.find_groups(name="test_group", first=True) is not None # add a nested group below the first one context.add_group("nested_group", "Nested test group", "test_group") group = password_database.find_groups(name="nested_group", first=True) assert group is not None assert isinstance(group, pykeepass.group.Group) parent_group = group.parentgroup assert parent_group.name == "test_group" def test_set_group_description(password_database: pykeepass.PyKeePass) -> None: """Test setting the group description.""" context = PasswordContext(password_database) context.add_group("test_group", "Test Group") kp_group = password_database.find_groups(name="test_group", first=True) assert isinstance(kp_group, pykeepass.group.Group) assert kp_group.notes == "Test Group" context.set_group_description("test_group", "New Description") kp_group = password_database.find_groups(name="test_group", first=True) assert isinstance(kp_group, pykeepass.group.Group) assert kp_group.notes == "New Description" def test_add_entry_with_group(password_database: pykeepass.PyKeePass) -> None: """Test adding an entry with a group.""" context = PasswordContext(password_database) context.add_group("test_group", "A test group") context.add_entry("test_entry", "test_secret", group_name="test_group") entry = password_database.find_entries(title="test_entry", first=True) assert entry is not None assert isinstance(entry, pykeepass.entry.Entry) assert entry.group.name == "test_group" def test_move_entry(password_database: pykeepass.PyKeePass) -> None: """Test moving entries between groups.""" context = PasswordContext(password_database) context.add_group("test_group", "A test group") context.add_group("test_group_2", "Another test group") context.add_entry("test_entry", "test_secret") entry = password_database.find_entries(title="test_entry", first=True) assert isinstance(entry, pykeepass.entry.Entry) assert entry.group.is_root_group is True context.set_secret_group("test_entry", "test_group") entry = password_database.find_entries(title="test_entry", first=True) assert isinstance(entry, pykeepass.entry.Entry) assert entry.group.is_root_group is False assert entry.group.name == "test_group" context.set_secret_group("test_entry", "test_group_2") entry = password_database.find_entries(title="test_entry", first=True) assert isinstance(entry, pykeepass.entry.Entry) assert entry.group.is_root_group is False assert entry.group.name == "test_group_2" context.set_secret_group("test_entry", None) entry = password_database.find_entries(title="test_entry", first=True) assert isinstance(entry, pykeepass.entry.Entry) assert entry.group.is_root_group is True def test_move_group(password_database: pykeepass.PyKeePass) -> None: """Test moving a group.""" context = PasswordContext(password_database) context.add_group("test_group", "A test group") context.add_group("parent_group", "A parent group") context.move_group("test_group", "parent_group") kp_group = password_database.find_groups(name="test_group", first=True) assert isinstance(kp_group, pykeepass.group.Group) assert kp_group.parentgroup.name == "parent_group" def test_delete_group(password_database: pykeepass.PyKeePass) -> None: """Test group deletion.""" context = PasswordContext(password_database) context.add_group("test_group", "A test group") # Add some entries to this group. kp_group = password_database.find_groups(name="test_group", first=True) assert isinstance(kp_group, pykeepass.group.Group) create_random_entries(password_database, amount=10, group=kp_group) context.delete_group("test_group") kp_group = password_database.find_groups(name="test_group", first=True) assert kp_group is None # Check if the secrets are still there. secrets = context.get_available_secrets() assert len(secrets) == 10 def test_get_specific_group(password_database: pykeepass.PyKeePass) -> None: """Test fetching a specific group.""" context = PasswordContext(password_database) context.add_group("parent", "A parent group") context.add_group("test_group", "A test group", "parent") context.add_group("test_group_2", "A test group") context.add_group("test_group_3", "A test group") context.add_group("Other Group", "A test group") results = context.get_secret_groups("test_group", False) assert len(results) == 1 # Check if the parent reference is available. assert results[0].parent_group is not None