This commit is contained in:
2023-03-25 22:26:34 +01:00
commit f6017f751c
28 changed files with 6167 additions and 0 deletions

0
tests/__init__.py Normal file
View File

344
tests/conftest.py Normal file
View File

@ -0,0 +1,344 @@
from collections import defaultdict
from functools import wraps
from pathlib import Path
from random import choice
from random import randint
from shutil import copytree
from string import ascii_letters
from string import digits
from string import hexdigits
from string import punctuation
from subprocess import PIPE
from subprocess import Popen
from tempfile import NamedTemporaryFile
from tempfile import TemporaryDirectory
from typing import Any
from typing import Callable
from typing import Dict
from typing import Generator
from typing import List
from typing import Optional
from typing import Set
from pytest import fixture
from libkeyringctl.keyring import convert_certificate
from libkeyringctl.keyring import export
from libkeyringctl.keyring import get_fingerprints_from_keyring_files
from libkeyringctl.sequoia import certify
from libkeyringctl.sequoia import key_extract_certificate
from libkeyringctl.sequoia import key_generate
from libkeyringctl.sequoia import keyring_merge
from libkeyringctl.sequoia import packet_join
from libkeyringctl.types import Fingerprint
from libkeyringctl.types import Uid
from libkeyringctl.types import Username
from libkeyringctl.util import cwd
from libkeyringctl.util import simplify_uid
from libkeyringctl.util import system
test_keys: Dict[Username, List[Path]] = defaultdict(list)
test_key_revocation: Dict[Username, List[Path]] = defaultdict(list)
test_certificates: Dict[Username, List[Path]] = defaultdict(list)
test_certificate_uids: Dict[Username, List[List[Uid]]] = defaultdict(list)
test_keyring_certificates: Dict[Username, List[Path]] = defaultdict(list)
test_main_fingerprints: Set[Fingerprint] = set()
test_all_fingerprints: Set[Fingerprint] = set()
@fixture(autouse=True)
def reset_storage() -> None:
test_keys.clear()
test_key_revocation.clear()
test_certificates.clear()
test_certificate_uids.clear()
test_keyring_certificates.clear()
test_main_fingerprints.clear()
test_all_fingerprints.clear()
def create_certificate(
username: Username,
uids: List[Uid],
keyring_type: str = "packager",
func: Optional[Callable[..., Any]] = None,
) -> Callable[..., Any]:
def decorator(decorated_func: Callable[..., None]) -> Callable[..., Any]:
@wraps(decorated_func)
def wrapper(working_dir: Path, *args: Any, **kwargs: Any) -> None:
key_directory = working_dir / "secret" / f"{username}"
key_directory.mkdir(parents=True, exist_ok=True)
key_file: Path = key_directory / f"{username}.asc"
key_generate(uids=uids, outfile=key_file)
test_keys[username].append(key_file)
certificate_directory = working_dir / "certificate" / f"{username}"
certificate_directory.mkdir(parents=True, exist_ok=True)
keyring_root: Path = working_dir / "keyring"
keyring_root.mkdir(parents=True, exist_ok=True)
certificate_file: Path = certificate_directory / f"{username}.asc"
key_extract_certificate(key=key_file, output=certificate_file)
test_certificates[username].append(certificate_file)
test_certificate_uids[username].append(uids)
key_revocation_packet = key_file.parent / f"{key_file.name}.rev"
key_revocation_joined = key_file.parent / f"{key_file.name}.joined.rev"
key_revocation_cert = key_file.parent / f"{key_file.name}.cert.rev"
packet_join(packets=[certificate_file, key_revocation_packet], output=key_revocation_joined)
keyring_merge(certificates=[key_revocation_joined], output=key_revocation_cert)
test_key_revocation[username].append(key_revocation_cert)
target_dir = keyring_root / keyring_type
for fingerprint in get_fingerprints_from_keyring_files(
working_dir=working_dir, source=[certificate_file]
).keys():
test_all_fingerprints.add(fingerprint)
decomposed_path: Path = convert_certificate(
working_dir=working_dir,
certificate=certificate_file,
keyring_dir=keyring_root / keyring_type,
fingerprint_filter=test_all_fingerprints,
)
user_dir = decomposed_path.parent
(target_dir / user_dir.name).mkdir(parents=True, exist_ok=True)
copytree(src=user_dir, dst=(target_dir / user_dir.name), dirs_exist_ok=True)
test_keyring_certificates[username].append(target_dir / user_dir.name / decomposed_path.name)
certificate_fingerprint: Fingerprint = Fingerprint(decomposed_path.name)
if "main" == keyring_type:
test_main_fingerprints.add(certificate_fingerprint)
test_all_fingerprints.add(certificate_fingerprint)
decorated_func(working_dir=working_dir, *args, **kwargs)
return wrapper
if not func:
return decorator
return decorator(func)
def create_uid_certification(
issuer: Username, certified: Username, uid: Uid, func: Optional[Callable[[Any], None]] = None
) -> Callable[..., Any]:
def decorator(decorated_func: Callable[..., None]) -> Callable[..., Any]:
@wraps(decorated_func)
def wrapper(working_dir: Path, *args: Any, **kwargs: Any) -> None:
key: Path = test_keys[issuer][0]
certificate: Path = test_certificates[certified][0]
fingerprint: Fingerprint = Fingerprint(test_keyring_certificates[certified][0].name)
issuer_fingerprint: Fingerprint = Fingerprint(test_keyring_certificates[issuer][0].name)
simplified_uid = simplify_uid(Uid(uid))
output: Path = (
working_dir
/ "keyring"
/ "packager"
/ certified
/ fingerprint
/ "uid"
/ simplified_uid
/ "certification"
/ f"{issuer_fingerprint}.asc"
)
output.parent.mkdir(parents=True, exist_ok=True)
certify(key, certificate, uid, output)
decorated_func(working_dir=working_dir, *args, **kwargs)
return wrapper
if not func:
return decorator
return decorator(func)
def create_key_revocation(
username: Username,
keyring_type: str = "packager",
func: Optional[Callable[..., Any]] = None,
) -> Callable[..., Any]:
def decorator(decorated_func: Callable[..., None]) -> Callable[..., Any]:
@wraps(decorated_func)
def wrapper(working_dir: Path, *args: Any, **kwargs: Any) -> None:
revocation = test_key_revocation[username][0]
keyring_root: Path = working_dir / "keyring"
keyring_root.mkdir(parents=True, exist_ok=True)
target_dir = keyring_root / keyring_type
decomposed_path: Path = convert_certificate(
working_dir=working_dir,
certificate=revocation,
keyring_dir=keyring_root / keyring_type,
fingerprint_filter=test_all_fingerprints,
)
user_dir = decomposed_path.parent
(target_dir / user_dir.name).mkdir(parents=True, exist_ok=True)
copytree(src=user_dir, dst=(target_dir / user_dir.name), dirs_exist_ok=True)
decorated_func(working_dir=working_dir, *args, **kwargs)
return wrapper
if not func:
return decorator
return decorator(func)
def create_signature_revocation(
issuer: Username, certified: Username, uid: Uid, func: Optional[Callable[[Any], None]] = None
) -> Callable[..., Any]:
def decorator(decorated_func: Callable[..., None]) -> Callable[..., Any]:
@wraps(decorated_func)
def wrapper(working_dir: Path, *args: Any, **kwargs: Any) -> None:
issuer_key: Path = test_keys[issuer][0]
keyring_root: Path = working_dir / "keyring"
keyring_certificate: Path = test_keyring_certificates[certified][0]
certified_fingerprint = keyring_certificate.name
with NamedTemporaryFile(dir=str(working_dir), prefix=f"{certified}", suffix=".asc") as certificate:
certificate_path: Path = Path(certificate.name)
export(
working_dir=working_dir,
keyring_root=keyring_root,
sources=[keyring_certificate],
output=certificate_path,
)
with TemporaryDirectory(prefix="gnupg") as gnupg_home:
env = {"GNUPGHOME": gnupg_home}
print(
system(
[
"gpg",
"--no-auto-check-trustdb",
"--import",
f"{str(issuer_key)}",
f"{str(certificate_path)}",
],
env=env,
)
)
uid_confirmations = ""
for cert_uid in test_certificate_uids[certified][0]:
if uid == cert_uid:
uid_confirmations += "y\n"
else:
uid_confirmations += "n\n"
commands = Popen(["echo", "-e", f"{uid_confirmations}y\n0\ny\n\ny\ny\nsave\n"], stdout=PIPE)
system(
[
"gpg",
"--no-auto-check-trustdb",
"--command-fd",
"0",
"--expert",
"--yes",
"--batch",
"--edit-key",
f"{certified_fingerprint}",
"revsig",
"save",
],
_stdin=commands.stdout,
env=env,
)
revoked_certificate = system(["gpg", "--armor", "--export", f"{certified_fingerprint}"], env=env)
certificate.truncate(0)
certificate.seek(0)
certificate.write(revoked_certificate.encode())
certificate.flush()
target_dir = keyring_root / "packager"
decomposed_path: Path = convert_certificate(
working_dir=working_dir,
certificate=certificate_path,
keyring_dir=target_dir,
fingerprint_filter=test_all_fingerprints,
)
user_dir = decomposed_path.parent
(target_dir / user_dir.name).mkdir(parents=True, exist_ok=True)
copytree(src=user_dir, dst=(target_dir / user_dir.name), dirs_exist_ok=True)
decorated_func(working_dir=working_dir, *args, **kwargs)
return wrapper
if not func:
return decorator
return decorator(func)
@fixture(scope="function")
def working_dir() -> Generator[Path, None, None]:
with TemporaryDirectory(prefix="arch-keyringctl-test-") as tempdir:
path: Path = Path(tempdir)
with cwd(path):
yield path
@fixture(scope="function")
def keyring_dir(working_dir: Path) -> Generator[Path, None, None]:
yield working_dir / "keyring"
def random_string(length: int, chars: str) -> str:
return "".join(choice(chars) for x in range(length))
@fixture(scope="function", params=[16, 40], ids=["short_id", "long_id"])
def valid_fingerprint(request: Any) -> Generator[str, None, None]:
yield random_string(length=request.param, chars="ABCDEF" + digits)
@fixture(scope="function", params=[16, 40], ids=["short_id", "long_id"])
def valid_subkey_fingerprint(request: Any) -> Generator[str, None, None]:
yield random_string(length=request.param, chars="ABCDEF" + digits)
@fixture(
scope="function",
params=[
(
16,
ascii_letters + hexdigits + punctuation,
),
(
40,
ascii_letters + hexdigits + punctuation,
),
(
randint(0, 15),
"ABCDEF" + digits,
),
(
randint(17, 39),
"ABCDEF" + digits,
),
(
randint(41, 100),
"ABCDEF" + digits,
),
],
ids=[
"short_id_wrong_chars",
"long_id_wrong_chars",
"right_chars_shorter_than_short_id",
"right_chars_shorter_than_long_id_longer_than_short_id",
"right_chars_longer_than_long_id",
],
)
def invalid_fingerprint(request: Any) -> Generator[str, None, None]:
yield random_string(length=request.param[0], chars=request.param[1])

45
tests/test_git.py Normal file
View File

@ -0,0 +1,45 @@
from pathlib import Path
from typing import List
from typing import Optional
from unittest.mock import Mock
from unittest.mock import patch
from pytest import mark
from libkeyringctl import git
@mark.parametrize(
"git_path, base, paths",
[
(None, None, None),
(Path("git_path"), None, None),
(Path("git_path"), "base", None),
(Path("git_path"), "base", [Path("foo"), Path("bar")]),
],
)
@patch("libkeyringctl.git.system")
def test_git_changed_files(
system_mock: Mock,
git_path: Optional[Path],
base: Optional[str],
paths: Optional[List[Path]],
) -> None:
system_mock.return_value = "create some thing foo\n" "delete some thing bar\n" "some thing baz\n"
assert git.git_changed_files(git_path=git_path, base=base, paths=paths) == (
[Path("foo")],
[Path("bar")],
[Path("baz")],
)
name, args, kwargs = system_mock.mock_calls[0]
if git_path:
assert "-C" in args[0]
assert str(git_path) in args[0]
if base:
assert base in args[0]
if paths:
assert "--" in args[0]
for path in paths:
assert str(path) in args[0]

804
tests/test_keyring.py Normal file
View File

@ -0,0 +1,804 @@
from collections import defaultdict
from contextlib import nullcontext as does_not_raise
from copy import deepcopy
from datetime import datetime
from pathlib import Path
from random import choice
from string import digits
from typing import ContextManager
from typing import Dict
from typing import List
from typing import Optional
from typing import Set
from unittest.mock import Mock
from unittest.mock import patch
from pytest import mark
from pytest import raises
from libkeyringctl import keyring
from libkeyringctl.keyring import PACKET_FILENAME_DATETIME_FORMAT
from libkeyringctl.types import Fingerprint
from libkeyringctl.types import TrustFilter
from libkeyringctl.types import Uid
from libkeyringctl.types import Username
from .conftest import create_certificate
from .conftest import create_key_revocation
from .conftest import create_signature_revocation
from .conftest import create_uid_certification
from .conftest import test_all_fingerprints
from .conftest import test_certificates
from .conftest import test_keyring_certificates
from .conftest import test_keys
from .conftest import test_main_fingerprints
def test_is_pgp_fingerprint(
valid_fingerprint: str,
invalid_fingerprint: str,
) -> None:
assert keyring.is_pgp_fingerprint(string=valid_fingerprint) is True
assert keyring.is_pgp_fingerprint(string=invalid_fingerprint) is False
@mark.parametrize(
"create_paths, create_paths_in_keyring_dir",
[
(True, False),
(True, True),
(False, True),
(False, False),
],
)
def test_transform_username_to_keyring_path(
create_paths: bool,
create_paths_in_keyring_dir: bool,
working_dir: Path,
keyring_dir: Path,
) -> None:
paths = [Path("test")]
input_paths = deepcopy(paths)
for index, path in enumerate(paths):
path_in_working_dir = working_dir / path
if create_paths:
path_in_working_dir.mkdir()
if create_paths_in_keyring_dir:
(keyring_dir / path).mkdir(parents=True)
paths[index] = path_in_working_dir
modified_paths = deepcopy(paths)
keyring.transform_username_to_keyring_path(keyring_dir=keyring_dir, paths=paths)
for index, path in enumerate(paths):
if create_paths or (not create_paths and not create_paths_in_keyring_dir):
assert path == modified_paths[index]
if not create_paths and create_paths_in_keyring_dir:
assert path == keyring_dir / input_paths[index]
@mark.parametrize(
"fingerprint_path, create_paths, create_paths_in_keyring_dir",
[
(True, True, False),
(True, True, True),
(True, False, True),
(True, False, False),
(False, True, False),
(False, True, True),
(False, False, True),
(False, False, False),
],
)
def test_transform_fingerprint_to_keyring_path(
fingerprint_path: bool,
create_paths: bool,
create_paths_in_keyring_dir: bool,
working_dir: Path,
keyring_dir: Path,
valid_fingerprint: str,
) -> None:
paths = [Path(valid_fingerprint) if fingerprint_path else Path("test")]
input_paths = deepcopy(paths)
keyring_subdir = keyring_dir / "type" / "username"
for index, path in enumerate(paths):
path_in_working_dir = working_dir / path
if create_paths:
path_in_working_dir.mkdir()
if create_paths_in_keyring_dir:
(keyring_subdir / path).mkdir(parents=True)
paths[index] = path_in_working_dir
modified_paths = deepcopy(paths)
keyring.transform_fingerprint_to_keyring_path(keyring_root=keyring_dir, paths=paths)
for index, path in enumerate(paths):
if create_paths or (not fingerprint_path and not create_paths):
assert path == modified_paths[index]
if not create_paths and fingerprint_path and create_paths_in_keyring_dir:
assert path == keyring_subdir / input_paths[index]
@mark.parametrize(
"valid_current_packet_fingerprint, packet_type, issuer, expectation",
[
(True, "KeyRevocation", "self", does_not_raise()),
(True, "DirectKey", "self", does_not_raise()),
(True, "GenericCertification", "self", does_not_raise()),
(True, "KeyRevocation", None, does_not_raise()),
(True, "CertificationRevocation", None, does_not_raise()),
(True, "CertificationRevocation", "self", does_not_raise()),
(True, "DirectKey", None, does_not_raise()),
(True, "GenericCertification", None, does_not_raise()),
(True, "KeyRevocation", "foo", raises(Exception)),
(True, "DirectKey", "foo", does_not_raise()),
(True, "GenericCertification", "foo", does_not_raise()),
(True, "foo", "foo", does_not_raise()),
(True, "foo", "self", raises(Exception)),
(False, "KeyRevocation", True, raises(Exception)),
(False, "DirectKey", True, raises(Exception)),
(False, "GenericCertification", True, raises(Exception)),
(False, "CertificationRevocation", True, raises(Exception)),
],
)
@patch("libkeyringctl.keyring.get_fingerprint_from_partial")
@patch("libkeyringctl.keyring.packet_dump_field")
def test_convert_pubkey_signature_packet(
packet_dump_field_mock: Mock,
get_fingerprint_from_partial_mock: Mock,
valid_current_packet_fingerprint: bool,
packet_type: str,
issuer: Optional[str],
expectation: ContextManager[str],
working_dir: Path,
valid_fingerprint: Fingerprint,
) -> None:
packet = working_dir / "packet"
key_revocations: Dict[Fingerprint, Path] = {}
direct_revocations: Dict[Fingerprint, List[Path]] = defaultdict(list)
direct_sigs: Dict[Fingerprint, List[Path]] = defaultdict(list)
current_packet_fingerprint = None
if valid_current_packet_fingerprint:
current_packet_fingerprint = valid_fingerprint
packet_dump_field_mock.return_value = packet_type
if issuer == "self":
get_fingerprint_from_partial_mock.return_value = valid_fingerprint
else:
get_fingerprint_from_partial_mock.return_value = None if issuer is None else Fingerprint(issuer)
with expectation:
keyring.convert_pubkey_signature_packet(
packet=packet,
certificate_fingerprint=valid_fingerprint,
fingerprint_filter=None,
current_packet_fingerprint=current_packet_fingerprint,
key_revocations=key_revocations,
direct_revocations=direct_revocations,
direct_sigs=direct_sigs,
)
if issuer is None or current_packet_fingerprint is None:
assert not direct_revocations and not direct_sigs and not key_revocations
else:
if packet_type == "KeyRevocation":
assert key_revocations[valid_fingerprint] == packet
elif packet_type in ["CertificationRevocation"]:
if issuer != "self":
assert not direct_revocations
else:
assert direct_revocations[valid_fingerprint if issuer == "self" else Fingerprint(issuer)] == [
packet
]
elif packet_type in ["DirectKey", "GenericCertification"]:
if issuer != "self":
assert not direct_sigs
else:
assert direct_sigs[valid_fingerprint if issuer == "self" else Fingerprint(issuer)] == [packet]
@mark.parametrize(
"valid_current_packet_uid, packet_type, provide_issuer, issuer_in_filter, expectation",
[
(True, "CertificationRevocation", "self", True, does_not_raise()),
(True, "CertificationRevocation", "self", False, does_not_raise()),
(True, "SomeCertification", "self", True, does_not_raise()),
(True, "SomeCertification", "self", False, does_not_raise()),
(True, "CertificationRevocation", None, True, does_not_raise()),
(True, "CertificationRevocation", None, False, does_not_raise()),
(True, "SomeCertification", None, True, does_not_raise()),
(True, "SomeCertification", None, False, does_not_raise()),
(False, "CertificationRevocation", "self", True, raises(Exception)),
(False, "CertificationRevocation", "self", False, raises(Exception)),
(False, "SomeCertification", "self", True, raises(Exception)),
(False, "SomeCertification", "self", False, raises(Exception)),
(True, "foo", "self", True, raises(Exception)),
(True, "foo", "self", False, raises(Exception)),
],
)
@patch("libkeyringctl.keyring.get_fingerprint_from_partial")
@patch("libkeyringctl.keyring.packet_dump_field")
def test_convert_uid_signature_packet(
packet_dump_field_mock: Mock,
get_fingerprint_from_partial_mock: Mock,
valid_current_packet_uid: bool,
packet_type: str,
provide_issuer: Optional[str],
issuer_in_filter: bool,
expectation: ContextManager[str],
working_dir: Path,
valid_fingerprint: Fingerprint,
) -> None:
packet = working_dir / "packet"
certifications: Dict[Uid, Dict[Fingerprint, List[Path]]] = defaultdict(lambda: defaultdict(list))
revocations: Dict[Uid, Dict[Fingerprint, List[Path]]] = defaultdict(lambda: defaultdict(list))
current_packet_uid = None
issuer = None
fingerprint_filter: Set[Fingerprint] = {Fingerprint("foo")}
if valid_current_packet_uid:
current_packet_uid = Uid("Foobar McFooface <foo@barmcfoofa.ce>")
packet_dump_field_mock.return_value = packet_type
if provide_issuer == "self":
issuer = valid_fingerprint
else:
if provide_issuer is not None:
issuer = Fingerprint(provide_issuer)
get_fingerprint_from_partial_mock.return_value = issuer
if issuer_in_filter and issuer is not None:
fingerprint_filter.add(issuer)
with expectation:
keyring.convert_uid_signature_packet(
packet=packet,
current_packet_uid=current_packet_uid,
fingerprint_filter=fingerprint_filter,
certifications=certifications,
revocations=revocations,
)
if not valid_current_packet_uid or issuer is None:
assert not certifications and not revocations
else:
if packet_type == "CertificationRevocation" and valid_current_packet_uid and issuer_in_filter:
assert revocations[current_packet_uid][issuer] == [packet] # type: ignore
elif packet_type.endswith("Certification") and issuer_in_filter:
assert certifications[current_packet_uid][issuer] == [packet] # type: ignore
elif packet_type.endswith("Certification") and not issuer_in_filter:
assert not certifications
@mark.parametrize(
"valid_current_packet_fingerprint, packet_type, issuer, expectation",
[
(True, "SubkeyBinding", "self", does_not_raise()),
(True, "SubkeyRevocation", "self", does_not_raise()),
(True, "SubkeyBinding", None, does_not_raise()),
(True, "SubkeyRevocation", None, does_not_raise()),
(True, "SubkeyBinding", "foo", raises(Exception)),
(True, "SubkeyRevocation", "foo", raises(Exception)),
(False, "SubkeyBinding", "self", raises(Exception)),
(False, "SubkeyRevocation", "self", raises(Exception)),
(True, "foo", "self", raises(Exception)),
],
)
@patch("libkeyringctl.keyring.get_fingerprint_from_partial")
@patch("libkeyringctl.keyring.packet_dump_field")
def test_convert_subkey_signature_packet(
packet_dump_field_mock: Mock,
get_fingerprint_from_partial_mock: Mock,
valid_current_packet_fingerprint: bool,
packet_type: str,
issuer: Optional[str],
expectation: ContextManager[str],
working_dir: Path,
valid_fingerprint: Fingerprint,
) -> None:
packet = working_dir / "packet"
subkey_bindings: Dict[Fingerprint, List[Path]] = defaultdict(list)
subkey_revocations: Dict[Fingerprint, List[Path]] = defaultdict(list)
current_packet_fingerprint = None
if valid_current_packet_fingerprint:
current_packet_fingerprint = valid_fingerprint
packet_dump_field_mock.return_value = packet_type
if issuer == "self":
get_fingerprint_from_partial_mock.return_value = valid_fingerprint
else:
get_fingerprint_from_partial_mock.return_value = None if issuer is None else Fingerprint(issuer)
with expectation:
keyring.convert_subkey_signature_packet(
packet=packet,
certificate_fingerprint=valid_fingerprint,
current_packet_fingerprint=current_packet_fingerprint,
fingerprint_filter=None,
subkey_bindings=subkey_bindings,
subkey_revocations=subkey_revocations,
)
if issuer is None or not valid_current_packet_fingerprint:
assert not subkey_bindings and not subkey_revocations
else:
if packet_type == "SubkeyBinding" and issuer == "self":
assert subkey_bindings[valid_fingerprint] == [packet]
elif packet_type == "SubkeyRevocation" and issuer == "self":
assert subkey_revocations[valid_fingerprint] == [packet]
@mark.parametrize(
"valid_certificate_fingerprint, current_packet_mode, expectation",
[
(True, "pubkey", does_not_raise()),
(True, "uid", does_not_raise()),
(True, "subkey", does_not_raise()),
(True, "uattr", does_not_raise()),
(False, "pubkey", raises(Exception)),
(False, "uid", raises(Exception)),
(False, "subkey", raises(Exception)),
(False, "uattr", raises(Exception)),
(True, "foo", raises(Exception)),
],
)
@patch("libkeyringctl.keyring.convert_pubkey_signature_packet")
@patch("libkeyringctl.keyring.convert_uid_signature_packet")
@patch("libkeyringctl.keyring.convert_subkey_signature_packet")
def test_convert_signature_packet(
convert_subkey_signature_packet_mock: Mock,
convert_uid_signature_packet_mock: Mock,
convert_pubkey_signature_packet_mock: Mock,
valid_certificate_fingerprint: bool,
current_packet_mode: str,
expectation: ContextManager[str],
valid_fingerprint: Fingerprint,
) -> None:
certificate_fingerprint = None
key_revocations: Dict[Fingerprint, Path] = {}
direct_revocations: Dict[Fingerprint, List[Path]] = defaultdict(list)
direct_sigs: Dict[Fingerprint, List[Path]] = defaultdict(list)
certifications: Dict[Uid, Dict[Fingerprint, List[Path]]] = defaultdict(lambda: defaultdict(list))
revocations: Dict[Uid, Dict[Fingerprint, List[Path]]] = defaultdict(lambda: defaultdict(list))
subkey_bindings: Dict[Fingerprint, List[Path]] = defaultdict(list)
subkey_revocations: Dict[Fingerprint, List[Path]] = defaultdict(list)
if valid_certificate_fingerprint:
certificate_fingerprint = valid_fingerprint
with expectation:
keyring.convert_signature_packet(
packet=Path("foo"),
current_packet_mode=current_packet_mode,
certificate_fingerprint=certificate_fingerprint,
fingerprint_filter=None,
key_revocations=key_revocations,
current_packet_fingerprint=None,
current_packet_uid=None,
direct_revocations=direct_revocations,
direct_sigs=direct_sigs,
certifications=certifications,
revocations=revocations,
subkey_bindings=subkey_bindings,
subkey_revocations=subkey_revocations,
)
if current_packet_mode == "pubkey":
convert_pubkey_signature_packet_mock.assert_called_once()
elif current_packet_mode == "uid":
convert_uid_signature_packet_mock.assert_called_once()
elif current_packet_mode == "subkey":
convert_subkey_signature_packet_mock.assert_called_once()
@mark.parametrize(
"packet, packet_split, packet_dump_field, name_override, expectation",
[
(
Path("foo.asc"),
[
Path("--PublicKey"),
Path("--Signature"),
Path("--UserID"),
Path("--UserAttribute"),
Path("--PublicSubkey"),
Path("--Signature"),
],
[
"".join(choice("ABCDEF" + digits) for _ in range(40)),
"foo <foo@bar.com>",
"".join(choice("ABCDEF" + digits) for _ in range(40)),
],
"bar",
does_not_raise(),
),
(
Path("foo.asc"),
[
Path("--PublicKey"),
Path("--Signature"),
Path("--UserID"),
Path("--UserID"),
],
[
"".join(choice("ABCDEF" + digits) for _ in range(40)),
"foo <foo@bar.com>",
"foo <foo@bar.com>",
],
"bar",
raises(Exception),
),
(
Path("foo.asc"),
[
Path("--SecretKey"),
],
[],
None,
raises(Exception),
),
(
Path("foo.asc"),
[
Path("foo"),
],
[],
None,
raises(Exception),
),
(
Path("foo.asc"),
[
Path("--PublicKey"),
],
[
None,
],
"bar",
raises(Exception),
),
],
)
@patch("libkeyringctl.keyring.persist_key_material")
@patch("libkeyringctl.keyring.packet_split")
@patch("libkeyringctl.keyring.convert_signature_packet")
@patch("libkeyringctl.keyring.packet_dump_field")
@patch("libkeyringctl.keyring.derive_username_from_fingerprint")
def test_convert_certificate(
derive_username_from_fingerprint_mock: Mock,
packet_dump_field_mock: Mock,
convert_signature_packet_mock: Mock,
packet_split_mock: Mock,
persist_key_material_mock: Mock,
packet: Path,
packet_split: List[Path],
packet_dump_field: List[str],
name_override: Optional[Username],
expectation: ContextManager[str],
working_dir: Path,
keyring_dir: Path,
) -> None:
packet_split_mock.return_value = packet_split
packet_dump_field_mock.side_effect = packet_dump_field
with expectation:
keyring.convert_certificate(
working_dir=working_dir,
certificate=packet,
keyring_dir=keyring_dir,
name_override=name_override,
fingerprint_filter=None,
)
@patch("libkeyringctl.keyring.latest_certification")
@patch("libkeyringctl.keyring.packet_join")
def test_persist_subkey_revocations(
packet_join_mock: Mock,
latest_certification_mock: Mock,
working_dir: Path,
keyring_dir: Path,
valid_fingerprint: Fingerprint,
) -> None:
revocation_packet = working_dir / "latest_revocation.asc"
latest_certification_mock.return_value = revocation_packet
subkey_revocations: Dict[Fingerprint, List[Path]] = {
valid_fingerprint: [revocation_packet, working_dir / "earlier_revocation.asc"]
}
keyring.persist_subkey_revocations(
key_dir=keyring_dir,
subkey_revocations=subkey_revocations,
issuer=valid_fingerprint,
)
packet_join_mock.assert_called_once_with(
packets=[revocation_packet],
output=keyring_dir / "subkey" / valid_fingerprint / "revocation" / f"{valid_fingerprint}.asc",
force=True,
)
@patch("libkeyringctl.keyring.packet_signature_creation_time")
@patch("libkeyringctl.keyring.packet_join")
def test_persist_directkey_revocations(
packet_join_mock: Mock,
packet_signature_creation_time_mock: Mock,
working_dir: Path,
keyring_dir: Path,
valid_fingerprint: Fingerprint,
) -> None:
revocation_packet = working_dir / "latest_revocation.asc"
directkey_revocations: Dict[Fingerprint, List[Path]] = {valid_fingerprint: [revocation_packet]}
dt = datetime(2000, 1, 12, 11, 22, 33)
packet_signature_creation_time_mock.return_value = dt
keyring.persist_direct_key_revocations(
key_dir=keyring_dir,
direct_key_revocations=directkey_revocations,
)
packet_join_mock.assert_called_once_with(
packets=[revocation_packet],
output=keyring_dir
/ "directkey"
/ "revocation"
/ valid_fingerprint
/ f"{dt.strftime(PACKET_FILENAME_DATETIME_FORMAT)}.asc",
force=True,
)
@create_certificate(username=Username("foobar"), uids=[Uid("foobar <foo@bar.xyz>")])
def test_convert(working_dir: Path, keyring_dir: Path) -> None:
keyring.convert(
working_dir=working_dir,
keyring_root=keyring_dir,
sources=test_certificates[Username("foobar")],
target_dir=keyring_dir / "packager",
)
with raises(Exception):
keyring.convert(
working_dir=working_dir,
keyring_root=keyring_dir,
sources=test_keys[Username("foobar")],
target_dir=keyring_dir / "packager",
)
@create_certificate(username=Username("main"), uids=[Uid("main <foo@bar.xyz>")], keyring_type="main")
@create_certificate(username=Username("foobar"), uids=[Uid("foobar <foo@bar.xyz>")])
@create_uid_certification(issuer=Username("main"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
@create_signature_revocation(issuer=Username("main"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
def test_clean_keyring(working_dir: Path, keyring_dir: Path) -> None:
# first pass clean up certification
keyring.clean_keyring(keyring=keyring_dir)
# second pass skipping clean up because lack of certification
keyring.clean_keyring(keyring=keyring_dir)
@create_certificate(username=Username("main"), uids=[Uid("main <foo@bar.xyz>")], keyring_type="main")
@create_certificate(username=Username("other_main"), uids=[Uid("other main <foo@bar.xyz>")], keyring_type="main")
@create_certificate(username=Username("foobar"), uids=[Uid("foobar <foo@bar.xyz>")])
def test_export_ownertrust(working_dir: Path, keyring_dir: Path) -> None:
output = working_dir / "build"
keyring.export_ownertrust(
certs=[keyring_dir / "main"],
keyring_root=keyring_dir,
output=output,
)
with open(file=output, mode="r") as output_file:
for line in output_file.readlines():
assert line.split(":")[0] in test_main_fingerprints
@create_certificate(username=Username("main"), uids=[Uid("main <foo@bar.xyz>")], keyring_type="main")
@create_certificate(username=Username("foobar"), uids=[Uid("foobar <foo@bar.xyz>")])
@create_key_revocation(username=Username("foobar"))
def test_export_revoked(working_dir: Path, keyring_dir: Path) -> None:
output = working_dir / "build"
keyring.export_revoked(
certs=[keyring_dir / "packager"],
keyring_root=keyring_dir,
main_keys=test_main_fingerprints,
output=output,
)
revoked_fingerprints = test_all_fingerprints - test_main_fingerprints
with open(file=output, mode="r") as output_file:
for line in output_file.readlines():
assert line.strip() in revoked_fingerprints
@mark.parametrize("path_exists", [(True), (False)])
@create_certificate(username=Username("main"), uids=[Uid("main <foo@bar.xyz>")], keyring_type="main")
@create_certificate(username=Username("foobar"), uids=[Uid("foobar <foo@bar.xyz>")])
@create_uid_certification(issuer=Username("main"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
@create_key_revocation(username=Username("foobar"))
def test_get_packets_from_path(working_dir: Path, keyring_dir: Path, path_exists: bool) -> None:
if not path_exists:
assert keyring.get_packets_from_path(path=working_dir / "nope") == []
else:
for username, paths in test_keyring_certificates.items():
for path in paths:
keyring.get_packets_from_path(path=path)
@mark.parametrize("path_exists", [(True), (False)])
@patch("libkeyringctl.keyring.get_packets_from_path")
def test_get_packets_from_listing(get_packets_from_path_mock: Mock, working_dir: Path, path_exists: bool) -> None:
path = working_dir / "path"
if not path_exists:
assert keyring.get_packets_from_listing(path=path) == []
else:
get_packets_from_path_mock.return_value = []
sub_path = path / "sub"
sub_path.mkdir(parents=True)
assert keyring.get_packets_from_listing(path=path) == []
get_packets_from_path_mock.assert_called_once_with(sub_path)
@create_certificate(username=Username("main"), uids=[Uid("main <foo@bar.xyz>")], keyring_type="main")
@create_certificate(username=Username("foobar"), uids=[Uid("foobar <foo@bar.xyz>")])
@create_uid_certification(issuer=Username("main"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
@create_key_revocation(username=Username("foobar"))
def test_export(working_dir: Path, keyring_dir: Path) -> None:
output_file = working_dir / "output"
empty_dir = working_dir / "empty"
empty_dir.mkdir()
assert not keyring.export(working_dir=working_dir, keyring_root=empty_dir, sources=None, output=output_file)
assert not output_file.exists()
keyring.export(working_dir=working_dir, keyring_root=keyring_dir, sources=None, output=output_file)
assert output_file.exists()
@create_certificate(username=Username("main"), uids=[Uid("main <foo@bar.xyz>")], keyring_type="main")
@create_certificate(username=Username("foobar"), uids=[Uid("foobar <foo@bar.xyz>")])
@create_uid_certification(issuer=Username("main"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
@create_key_revocation(username=Username("foobar"))
def test_build(working_dir: Path, keyring_dir: Path) -> None:
output_dir = working_dir / "output"
with raises(FileNotFoundError):
empty_dir = working_dir / "empty"
empty_dir.mkdir()
keyring.build(working_dir=working_dir, keyring_root=empty_dir, target_dir=output_dir)
keyring.build(working_dir=working_dir, keyring_root=keyring_dir, target_dir=output_dir)
assert (
(output_dir / "archlinux.gpg").exists()
and (output_dir / "archlinux-trusted").exists()
and (output_dir / "archlinux-revoked").exists()
)
@mark.parametrize(
"create_dir, duplicate_fingerprints, expectation",
[
(True, False, does_not_raise()),
(True, True, raises(Exception)),
(False, False, does_not_raise()),
(False, True, does_not_raise()),
],
)
def test_derive_username_from_fingerprint(
create_dir: bool,
duplicate_fingerprints: bool,
expectation: ContextManager[str],
keyring_dir: Path,
valid_fingerprint: str,
) -> None:
username = "username"
other_username = "other_user"
typed_keyring_dir = keyring_dir / "type"
if create_dir:
(typed_keyring_dir / username / valid_fingerprint).mkdir(parents=True)
if duplicate_fingerprints:
(typed_keyring_dir / other_username / valid_fingerprint).mkdir(parents=True)
with expectation:
returned_username = keyring.derive_username_from_fingerprint(
keyring_dir=typed_keyring_dir,
certificate_fingerprint=Fingerprint(valid_fingerprint),
)
if create_dir and not duplicate_fingerprints:
assert returned_username == username
else:
assert returned_username is None
@create_certificate(username=Username("main"), uids=[Uid("main <foo@bar.xyz>")], keyring_type="main")
@create_certificate(username=Username("foobar"), uids=[Uid("foobar <foo@bar.xyz>")])
def test_list_keyring(working_dir: Path, keyring_dir: Path) -> None:
packager_fingerprints = test_all_fingerprints - test_main_fingerprints
with patch("builtins.print") as print_mock:
keyring.list_keyring(keyring_root=keyring_dir, sources=None, main_keys=False)
print_args = [mock_call[1][0] for mock_call in print_mock.mock_calls]
for fingerprint in packager_fingerprints:
assert any([fingerprint in print_arg for print_arg in print_args])
with patch("builtins.print") as print_mock:
keyring.list_keyring(keyring_root=keyring_dir, sources=None, main_keys=True)
print_args = [mock_call[1][0] for mock_call in print_mock.mock_calls]
for fingerprint in test_main_fingerprints:
assert any([fingerprint in print_arg for print_arg in print_args])
for name, paths in test_keyring_certificates.items():
if all(["main" in str(path) for path in paths]):
for path in paths:
with patch("builtins.print") as print_mock:
keyring.list_keyring(keyring_root=keyring_dir, sources=[path], main_keys=True)
print_args = [mock_call[1][0] for mock_call in print_mock.mock_calls]
assert name in print_args[0] and path.stem in print_args[0]
elif all(["packager" in str(path) for path in paths]):
for path in paths:
with patch("builtins.print") as print_mock:
keyring.list_keyring(keyring_root=keyring_dir, sources=[path], main_keys=False)
print_args = [mock_call[1][0] for mock_call in print_mock.mock_calls]
assert name in print_args[0] and path.stem in print_args[0]
with patch("builtins.print") as print_mock:
keyring.list_keyring(
keyring_root=keyring_dir, sources=paths, main_keys=False, trust_filter=TrustFilter.revoked
)
print_args = [mock_call[1][0] for mock_call in print_mock.mock_calls]
assert not print_args
@create_certificate(username=Username("main"), uids=[Uid("main <foo@bar.xyz>")], keyring_type="main")
@create_certificate(username=Username("foobar"), uids=[Uid("foobar <foo@bar.xyz>")])
def test_inspect_keyring(working_dir: Path, keyring_dir: Path) -> None:
inspect_string = keyring.inspect_keyring(working_dir=working_dir, keyring_root=keyring_dir, sources=None)
for fingerprint in test_all_fingerprints:
assert fingerprint in inspect_string
for name, paths in test_keyring_certificates.items():
if all(["main" in str(path) for path in paths]):
for path in paths:
inspect_string = keyring.inspect_keyring(
working_dir=working_dir,
keyring_root=keyring_dir,
sources=[path],
)
assert path.stem in inspect_string
elif all(["packager" in str(path) for path in paths]):
for path in paths:
inspect_string = keyring.inspect_keyring(
working_dir=working_dir,
keyring_root=keyring_dir,
sources=[path],
)
assert path.stem in inspect_string
def test_get_fingerprints_from_paths(keyring_dir: Path, valid_fingerprint: str, valid_subkey_fingerprint: str) -> None:
fingerprint_dir = keyring_dir / "type" / "username" / valid_fingerprint
fingerprint_dir.mkdir(parents=True)
(fingerprint_dir / (fingerprint_dir.name + ".asc")).touch()
fingerprint_subkey_dir = fingerprint_dir / "subkey" / valid_subkey_fingerprint
fingerprint_subkey_dir.mkdir(parents=True)
fingerprint_subkey_asc = fingerprint_subkey_dir / (fingerprint_subkey_dir.name + ".asc")
fingerprint_subkey_asc.touch()
assert keyring.get_fingerprints_from_paths(sources=[fingerprint_subkey_dir]) == {
Fingerprint(valid_subkey_fingerprint)
}
assert keyring.get_fingerprints_from_paths(sources=[fingerprint_dir]) == {Fingerprint(valid_fingerprint)}

368
tests/test_sequoia.py Normal file
View File

@ -0,0 +1,368 @@
from contextlib import nullcontext as does_not_raise
from datetime import datetime
from datetime import timedelta
from datetime import timezone
from pathlib import Path
from tempfile import TemporaryDirectory
from typing import ContextManager
from typing import Dict
from typing import Optional
from unittest.mock import Mock
from unittest.mock import patch
from pytest import mark
from pytest import raises
from libkeyringctl import sequoia
from libkeyringctl.types import Fingerprint
from libkeyringctl.types import PacketKind
from libkeyringctl.types import Uid
from libkeyringctl.types import Username
@mark.parametrize(
"create_subdir, preserve_filename",
[
(False, True),
(False, False),
(True, True),
(True, False),
],
)
@patch("libkeyringctl.sequoia.system")
@patch("libkeyringctl.sequoia.mkdtemp")
def test_keyring_split(mkdtemp_mock: Mock, system_mock: Mock, create_subdir: bool, preserve_filename: bool) -> None:
with TemporaryDirectory() as tmp_dir_name:
tmp_dir = Path(tmp_dir_name)
keyring_tmp_dir = tmp_dir / "keyring"
keyring_tmp_dir.mkdir()
mkdtemp_mock.return_value = keyring_tmp_dir.absolute()
if create_subdir:
keyring_sub_dir = keyring_tmp_dir / "foo"
keyring_sub_dir.mkdir()
returned = sequoia.keyring_split(
working_dir=tmp_dir,
keyring=Path("foo"),
preserve_filename=preserve_filename,
)
if create_subdir:
assert returned == [keyring_sub_dir]
else:
assert returned == []
@mark.parametrize(
"output",
[
None,
Path("output"),
],
)
@patch("libkeyringctl.sequoia.system")
def test_keyring_merge(system_mock: Mock, output: Optional[Path]) -> None:
certificates = [Path("foo"), Path("bar")]
system_mock.return_value = "return"
assert sequoia.keyring_merge(certificates=certificates, output=output) == "return"
name, args, kwargs = system_mock.mock_calls[0]
for cert in certificates:
assert str(cert) in args[0]
if output:
assert "--output" in args[0] and str(output) in args[0]
@patch("libkeyringctl.sequoia.system")
@patch("libkeyringctl.sequoia.mkdtemp")
def test_packet_split(mkdtemp_mock: Mock, system_mock: Mock) -> None:
certificate = Path("certificate")
with TemporaryDirectory() as tmp_dir_name:
tmp_dir = Path(tmp_dir_name)
keyring_tmp_dir = tmp_dir / "keyring"
keyring_tmp_dir.mkdir()
mkdtemp_mock.return_value = keyring_tmp_dir.absolute()
keyring_sub_dir = keyring_tmp_dir / "foo"
keyring_sub_dir.mkdir()
assert sequoia.packet_split(working_dir=tmp_dir, certificate=certificate) == [keyring_sub_dir]
name, args, kwargs = system_mock.mock_calls[0]
assert str(certificate) == args[0][-1]
@mark.parametrize("output, force", [(None, True), (None, False), (Path("output"), True), (Path("output"), False)])
@patch("libkeyringctl.sequoia.system")
def test_packet_join(system_mock: Mock, output: Optional[Path], force: bool) -> None:
packets = [Path("packet1"), Path("packet2")]
system_return = "return"
system_mock.return_value = system_return
assert sequoia.packet_join(packets, output=output, force=force) == system_return
name, args, kwargs = system_mock.mock_calls[0]
for packet in packets:
assert str(packet) in args[0]
if force:
assert "--force" == args[0][1]
if output:
assert "--output" in args[0] and str(output) in args[0]
@mark.parametrize(
"certifications_in_result, certifications, fingerprints",
[
("something: 0123456789123456789012345678901234567890\n", True, None),
("something: 0123456789123456789012345678901234567890\n", False, None),
(
"something: 0123456789123456789012345678901234567890\n",
True,
{Fingerprint("0123456789123456789012345678901234567890"): Username("foo")},
),
(
"something: 0123456789123456789012345678901234567890\n",
False,
{Fingerprint("0123456789123456789012345678901234567890"): Username("foo")},
),
(
"something: 5678901234567890\n",
True,
{Fingerprint("0123456789123456789012345678901234567890"): Username("foo")},
),
(
"something: 5678901234567890\n",
False,
{Fingerprint("0123456789123456789012345678901234567890"): Username("foo")},
),
],
)
@patch("libkeyringctl.sequoia.system")
def test_inspect(
system_mock: Mock,
certifications_in_result: str,
certifications: bool,
fingerprints: Optional[Dict[Fingerprint, Username]],
) -> None:
packet = Path("packet")
result_header = "result\n"
if certifications:
system_mock.return_value = result_header + "\n" + certifications_in_result
else:
system_mock.return_value = result_header
returned = sequoia.inspect(packet=packet, certifications=certifications, fingerprints=fingerprints)
if fingerprints and certifications:
for fingerprint, username in fingerprints.items():
assert f"{fingerprint[24:]} {username}" in returned
assert result_header in returned
@patch("libkeyringctl.sequoia.system")
def test_packet_dump(system_mock: Mock) -> None:
system_mock.return_value = "return"
assert sequoia.packet_dump(packet=Path("packet")) == "return"
system_mock.called_once_with(["sq", "packet", "dump", "packet"])
@mark.parametrize(
"packet_dump_return, query, result, expectation",
[
(
"""
Signature Packet
Version: 4
Type: SubkeyBinding
Hash algo: SHA512
""",
"Type",
"SubkeyBinding",
does_not_raise(),
),
(
"""
Signature Packet
Version: 4
Type: SubkeyBinding
Hash algo: SHA512
Hashed area:
Signature creation time: 2022-12-31 15:53:59 UTC
Issuer: BBBBBB
Unhashed area:
Issuer: 42424242
""",
"Unhashed area.Issuer",
"42424242",
does_not_raise(),
),
(
"""
Signature Packet
Version: 4
Type: SubkeyBinding
Hash algo: SHA512
Hashed area:
Signature creation time: 2022-12-31 15:53:59 UTC
Unhashed area:
Issuer: 42424242
""",
"Hashed area|Unhashed area.Issuer",
"42424242",
does_not_raise(),
),
(
"""
Signature Packet
Version: 4
Type: SubkeyBinding
Hash algo: SHA1
Hashed area:
Signature creation time: 2022-12-31
""",
"*.Signature creation time",
"2022-12-31",
does_not_raise(),
),
(
"""
Signature Packet
a:
b:
x: foo
b:
b:
c: bar
""",
"*.b.c",
"bar",
does_not_raise(),
),
(
"""
Signature Packet
a:
b:
x:
y:
z: foo
b:
b:
x:
y:
z: foo
w:
w: foo
k:
i:
c: bar
""",
"*.b.*.*.c",
"bar",
does_not_raise(),
),
(
"""
Signature Packet
a:
c:
b: foo
a:
b: bar
""",
"a.b",
"bar",
does_not_raise(),
),
(
"""
Signature Packet
Version: 4
Type: SubkeyBinding
Hash algo: SHA512
Hashed area:
Signature creation time: 2022-12-31 15:53:59 UTC
Unhashed area:
Issuer: 42424242
Issuer: BBBBBBBB
""",
"Hashed area.Issuer",
None,
raises(Exception),
),
],
)
@patch("libkeyringctl.sequoia.packet_dump")
def test_packet_dump_field(
packet_dump_mock: Mock,
packet_dump_return: str,
query: str,
result: str,
expectation: ContextManager[str],
) -> None:
packet_dump_mock.return_value = packet_dump_return
with expectation:
assert sequoia.packet_dump_field(packet=Path("packet"), query=query) == result
@patch("libkeyringctl.sequoia.packet_dump_field")
def test_packet_signature_creation_time(packet_dump_field_mock: Mock) -> None:
creation_time = "2021-10-31 00:48:09 UTC"
packet_dump_field_mock.return_value = creation_time
assert sequoia.packet_signature_creation_time(packet=Path("packet")) == datetime.strptime(
creation_time, "%Y-%m-%d %H:%M:%S %Z"
)
@patch("libkeyringctl.sequoia.packet_dump")
def test_packet_kinds(packet_dump_mock: Mock) -> None:
lines = [
"Type1 something",
" foo",
"Type2",
"WARNING",
"Type3 other",
" bar",
]
path = Path("foo")
packet_dump_mock.return_value = "\n".join(lines)
assert sequoia.packet_kinds(packet=path) == [PacketKind("Type1"), PacketKind("Type2"), PacketKind("Type3")]
@patch("libkeyringctl.sequoia.packet_signature_creation_time")
def test_latest_certification(packet_signature_creation_time_mock: Mock) -> None:
now = datetime.now(tz=timezone.utc)
later = now + timedelta(days=1)
early_cert = Path("cert1")
later_cert = Path("cert2")
packet_signature_creation_time_mock.side_effect = [now, later]
assert sequoia.latest_certification(certifications=[early_cert, later_cert]) == later_cert
packet_signature_creation_time_mock.side_effect = [later, now]
assert sequoia.latest_certification(certifications=[later_cert, early_cert]) == later_cert
@mark.parametrize("output", [(None), (Path("output"))])
@patch("libkeyringctl.sequoia.system")
def test_key_extract_certificate(system_mock: Mock, output: Optional[Path]) -> None:
system_mock.return_value = "return"
assert sequoia.key_extract_certificate(key=Path("key"), output=output) == "return"
name, args, kwargs = system_mock.mock_calls[0]
if output:
assert str(output) == args[0][-1]
@mark.parametrize("output", [(None), (Path("output"))])
@patch("libkeyringctl.sequoia.system")
def test_certify(system_mock: Mock, output: Optional[Path]) -> None:
system_mock.return_value = "return"
assert sequoia.certify(key=Path("key"), certificate=Path("cert"), uid=Uid("uid"), output=output) == "return"
name, args, kwargs = system_mock.mock_calls[0]
if output:
assert str(output) == args[0][-1]

381
tests/test_trust.py Normal file
View File

@ -0,0 +1,381 @@
from pathlib import Path
from typing import List
from unittest.mock import Mock
from unittest.mock import patch
from pytest import mark
from pytest import raises
from libkeyringctl.trust import certificate_trust
from libkeyringctl.trust import certificate_trust_from_paths
from libkeyringctl.trust import filter_by_trust
from libkeyringctl.trust import format_trust_label
from libkeyringctl.trust import trust_color
from libkeyringctl.trust import trust_icon
from libkeyringctl.types import Color
from libkeyringctl.types import Fingerprint
from libkeyringctl.types import Trust
from libkeyringctl.types import TrustFilter
from libkeyringctl.types import Uid
from libkeyringctl.types import Username
from .conftest import create_certificate
from .conftest import create_key_revocation
from .conftest import create_signature_revocation
from .conftest import create_uid_certification
from .conftest import test_all_fingerprints
from .conftest import test_keyring_certificates
from .conftest import test_main_fingerprints
@mark.parametrize(
"sources",
[
([Path("foobar")]),
([Path("foobar"), Path("quxdoo")]),
],
)
@patch("libkeyringctl.trust.certificate_trust")
def test_certificate_trust_from_paths(
certificate_trust_mock: Mock,
sources: List[Path],
working_dir: Path,
) -> None:
certificate_trust_mock.return_value = Trust.full
for source in sources:
source.mkdir(parents=True, exist_ok=True)
cert = source / "foo.asc"
cert.touch()
trusts = certificate_trust_from_paths(
sources=sources, main_keys=test_main_fingerprints, all_fingerprints=test_all_fingerprints
)
for i, source in enumerate(sources):
name, args, kwargs = certificate_trust_mock.mock_calls[i]
assert kwargs["certificate"] == source
assert kwargs["main_keys"] == test_main_fingerprints
assert kwargs["all_fingerprints"] == test_all_fingerprints
fingerprint = Fingerprint(source.name)
assert Trust.full == trusts[fingerprint]
assert len(trusts) == len(sources)
@create_certificate(username=Username("foobar"), uids=[Uid("foobar <foo@bar.xyz>")], keyring_type="main")
def test_certificate_trust_main_key_has_full_trust(working_dir: Path, keyring_dir: Path) -> None:
trust = certificate_trust(
test_keyring_certificates[Username("foobar")][0],
test_main_fingerprints,
test_all_fingerprints,
)
assert Trust.full == trust
@create_certificate(username=Username("foobar"), uids=[Uid("foobar <foo@bar.xyz>")], keyring_type="main")
@create_key_revocation(username=Username("foobar"), keyring_type="main")
def test_certificate_trust_main_key_revoked(working_dir: Path, keyring_dir: Path) -> None:
trust = certificate_trust(
test_keyring_certificates[Username("foobar")][0],
test_main_fingerprints,
test_all_fingerprints,
)
assert Trust.revoked == trust
@create_certificate(username=Username("foobar"), uids=[Uid("foobar <foo@bar.xyz>")], keyring_type="main")
@create_key_revocation(username=Username("foobar"), keyring_type="main")
def test_certificate_trust_main_key_revoked_unknown_fingerprint_lookup(working_dir: Path, keyring_dir: Path) -> None:
fingerprint = Fingerprint(test_keyring_certificates[Username("foobar")][0].name)
revocation = list((keyring_dir / "main" / "foobar" / fingerprint / "revocation").iterdir())[0]
revocation.rename(revocation.parent / "12341234.asc")
with raises(Exception):
certificate_trust(
test_keyring_certificates[Username("foobar")][0],
test_main_fingerprints,
{Fingerprint("12341234")},
)
@create_certificate(username=Username("foobar"), uids=[Uid("foobar <foo@bar.xyz>")], keyring_type="main")
@create_key_revocation(username=Username("foobar"), keyring_type="main")
def test_certificate_trust_main_key_revoked_unknown_self_revocation(working_dir: Path, keyring_dir: Path) -> None:
fingerprint = Fingerprint(test_keyring_certificates[Username("foobar")][0].name)
revocation = list((keyring_dir / "main" / "foobar" / fingerprint / "revocation").iterdir())[0]
revocation.rename(revocation.parent / "12341234.asc")
with raises(Exception):
certificate_trust(
test_keyring_certificates[Username("foobar")][0],
test_main_fingerprints,
set(),
)
@create_certificate(username=Username("main"), uids=[Uid("main <foo@bar.xyz>")])
@create_certificate(username=Username("foobar"), uids=[Uid("foobar <foo@bar.xyz>")])
def test_certificate_trust_no_signature_is_unknown(working_dir: Path, keyring_dir: Path) -> None:
trust = certificate_trust(
test_keyring_certificates[Username("foobar")][0],
test_main_fingerprints,
test_all_fingerprints,
)
assert Trust.unknown == trust
@create_certificate(username=Username("main"), uids=[Uid("main <foo@bar.xyz>")], keyring_type="main")
@create_certificate(username=Username("foobar"), uids=[Uid("foobar <foo@bar.xyz>")])
@create_uid_certification(issuer=Username("main"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
def test_certificate_trust_one_signature_is_marginal(working_dir: Path, keyring_dir: Path) -> None:
trust = certificate_trust(
test_keyring_certificates[Username("foobar")][0],
test_main_fingerprints,
test_all_fingerprints,
)
assert Trust.marginal == trust
@create_certificate(username=Username("main"), uids=[Uid("main <foo@bar.xyz>")], keyring_type="main")
@create_certificate(username=Username("not_main"), uids=[Uid("main <foo@bar.xyz>")])
@create_certificate(username=Username("foobar"), uids=[Uid("foobar <foo@bar.xyz>")])
@create_uid_certification(issuer=Username("not_main"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
def test_certificate_trust_one_none_main_signature_gives_no_trust(working_dir: Path, keyring_dir: Path) -> None:
trust = certificate_trust(
test_keyring_certificates[Username("foobar")][0],
test_main_fingerprints,
test_all_fingerprints,
)
assert Trust.unknown == trust
@create_certificate(username=Username("main1"), uids=[Uid("main1 <foo@bar.xyz>")], keyring_type="main")
@create_certificate(username=Username("main2"), uids=[Uid("main2 <foo@bar.xyz>")], keyring_type="main")
@create_certificate(username=Username("main3"), uids=[Uid("main3 <foo@bar.xyz>")], keyring_type="main")
@create_certificate(username=Username("foobar"), uids=[Uid("foobar <foo@bar.xyz>")])
@create_uid_certification(issuer=Username("main1"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
@create_uid_certification(issuer=Username("main2"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
@create_uid_certification(issuer=Username("main3"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
def test_certificate_trust_three_main_signature_gives_full_trust(working_dir: Path, keyring_dir: Path) -> None:
trust = certificate_trust(
test_keyring_certificates[Username("foobar")][0],
test_main_fingerprints,
test_all_fingerprints,
)
assert Trust.full == trust
@create_certificate(username=Username("main1"), uids=[Uid("main1 <foo@bar.xyz>")], keyring_type="main")
@create_certificate(username=Username("main2"), uids=[Uid("main2 <foo@bar.xyz>")], keyring_type="main")
@create_certificate(username=Username("main3"), uids=[Uid("main3 <foo@bar.xyz>")], keyring_type="main")
@create_certificate(username=Username("foobar"), uids=[Uid("foobar <foo@bar.xyz>")])
@create_uid_certification(issuer=Username("main1"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
@create_uid_certification(issuer=Username("main2"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
@create_uid_certification(issuer=Username("main3"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
@create_key_revocation(username=Username("main3"), keyring_type="main")
def test_certificate_trust_three_main_signature_one_revoked(working_dir: Path, keyring_dir: Path) -> None:
trust = certificate_trust(
test_keyring_certificates[Username("foobar")][0],
test_main_fingerprints,
test_all_fingerprints,
)
assert Trust.marginal == trust
@create_certificate(username=Username("main"), uids=[Uid("main <foo@bar.xyz>")], keyring_type="main")
@create_certificate(username=Username("foobar"), uids=[Uid("foobar <foo@bar.xyz>")])
@create_key_revocation(username=Username("foobar"))
def test_certificate_trust_revoked_key(working_dir: Path, keyring_dir: Path) -> None:
trust = certificate_trust(
test_keyring_certificates[Username("foobar")][0],
test_main_fingerprints,
test_all_fingerprints,
)
assert Trust.revoked == trust
@create_certificate(username=Username("main"), uids=[Uid("main <foo@bar.xyz>")], keyring_type="main")
@create_certificate(username=Username("foobar"), uids=[Uid("foobar <foo@bar.xyz>")])
@create_uid_certification(issuer=Username("main"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
@create_signature_revocation(issuer=Username("main"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
def test_certificate_trust_one_signature_revoked(working_dir: Path, keyring_dir: Path) -> None:
trust = certificate_trust(
test_keyring_certificates[Username("foobar")][0],
test_main_fingerprints,
test_all_fingerprints,
)
assert Trust.revoked == trust
@create_certificate(username=Username("main1"), uids=[Uid("main1 <foo@bar.xyz>")], keyring_type="main")
@create_certificate(username=Username("main2"), uids=[Uid("main2 <foo@bar.xyz>")], keyring_type="main")
@create_certificate(username=Username("main3"), uids=[Uid("main3 <foo@bar.xyz>")], keyring_type="main")
@create_certificate(username=Username("foobar"), uids=[Uid("foobar <foo@bar.xyz>")])
@create_uid_certification(issuer=Username("main1"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
@create_uid_certification(issuer=Username("main2"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
@create_uid_certification(issuer=Username("main3"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
@create_signature_revocation(issuer=Username("main3"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
def test_certificate_trust_revoked_if_below_full(working_dir: Path, keyring_dir: Path) -> None:
trust = certificate_trust(
test_keyring_certificates[Username("foobar")][0],
test_main_fingerprints,
test_all_fingerprints,
)
assert Trust.revoked == trust
@create_certificate(username=Username("main1"), uids=[Uid("main1 <foo@bar.xyz>")], keyring_type="main")
@create_certificate(username=Username("main2"), uids=[Uid("main2 <foo@bar.xyz>")], keyring_type="main")
@create_certificate(username=Username("main3"), uids=[Uid("main3 <foo@bar.xyz>")], keyring_type="main")
@create_certificate(username=Username("main4"), uids=[Uid("main4 <foo@bar.xyz>")], keyring_type="main")
@create_certificate(username=Username("foobar"), uids=[Uid("foobar <foo@bar.xyz>")])
@create_uid_certification(issuer=Username("main1"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
@create_uid_certification(issuer=Username("main2"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
@create_uid_certification(issuer=Username("main3"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
@create_uid_certification(issuer=Username("main4"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
@create_signature_revocation(issuer=Username("main4"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
def test_certificate_trust_full_remains_if_enough_sigs_present(working_dir: Path, keyring_dir: Path) -> None:
trust = certificate_trust(
test_keyring_certificates[Username("foobar")][0],
test_main_fingerprints,
test_all_fingerprints,
)
assert Trust.full == trust
@create_certificate(username=Username("main1"), uids=[Uid("main1 <foo@bar.xyz>")], keyring_type="main")
@create_certificate(username=Username("main2"), uids=[Uid("main2 <foo@bar.xyz>")], keyring_type="main")
@create_certificate(username=Username("main3"), uids=[Uid("main3 <foo@bar.xyz>")], keyring_type="main")
@create_certificate(username=Username("foobar"), uids=[Uid("foobar <foo@bar.xyz>"), Uid("old <old@old.old>")])
@create_uid_certification(issuer=Username("main1"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
@create_uid_certification(issuer=Username("main2"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
@create_signature_revocation(issuer=Username("foobar"), certified=Username("foobar"), uid=Uid("old <old@old.old>"))
def test_certificate_trust_not_revoked_if_only_one_uid_is_self_revoked(working_dir: Path, keyring_dir: Path) -> None:
trust = certificate_trust(
test_keyring_certificates[Username("foobar")][0],
test_main_fingerprints,
test_all_fingerprints,
)
assert Trust.marginal == trust
@create_certificate(username=Username("foobar"), uids=[Uid("foobar <foo@bar.xyz>"), Uid("old <old@old.old>")])
@create_signature_revocation(issuer=Username("foobar"), certified=Username("foobar"), uid=Uid("old <old@old.old>"))
def test_certificate_trust_unknown_if_only_contains_self_revoked(working_dir: Path, keyring_dir: Path) -> None:
trust = certificate_trust(
test_keyring_certificates[Username("foobar")][0],
test_main_fingerprints,
test_all_fingerprints,
)
assert Trust.unknown == trust
@create_certificate(username=Username("main1"), uids=[Uid("main1 <foo@bar.xyz>")], keyring_type="main")
@create_certificate(username=Username("foobar"), uids=[Uid("foobar <foo@bar.xyz>"), Uid("old <old@old.old>")])
@create_uid_certification(issuer=Username("main1"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
def test_certificate_trust_missing_signature_fingerprint_lookup(working_dir: Path, keyring_dir: Path) -> None:
with raises(Exception):
certificate_trust(
test_keyring_certificates[Username("foobar")][0],
test_main_fingerprints,
set(),
)
@create_certificate(username=Username("foobar"), uids=[Uid("old <old@old.old>")])
@create_signature_revocation(issuer=Username("foobar"), certified=Username("foobar"), uid=Uid("old <old@old.old>"))
def test_certificate_trust_missing_revocation_fingerprint_lookup(working_dir: Path, keyring_dir: Path) -> None:
with raises(Exception):
certificate_trust(
test_keyring_certificates[Username("foobar")][0],
test_main_fingerprints,
set(),
)
@create_certificate(username=Username("main1"), uids=[Uid("main1 <foo@bar.xyz>")], keyring_type="main")
@create_certificate(username=Username("foobar"), uids=[Uid("foobar <foo@bar.xyz>")])
@create_certificate(username=Username("packager"), uids=[Uid("packager <packager@bar.xyz>")])
@create_uid_certification(issuer=Username("main1"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
@create_uid_certification(issuer=Username("packager"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
@create_signature_revocation(issuer=Username("packager"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
def test_certificate_trust_ignore_3rd_party_revocation(working_dir: Path, keyring_dir: Path) -> None:
trust = certificate_trust(
test_keyring_certificates[Username("foobar")][0],
test_main_fingerprints,
test_all_fingerprints,
)
assert Trust.marginal == trust
@mark.parametrize(
"trust, result",
[
(Trust.revoked, Color.RED),
(Trust.full, Color.GREEN),
(Trust.marginal, Color.YELLOW),
(Trust.unknown, Color.YELLOW),
],
)
def test_trust_color(trust: Trust, result: Color) -> None:
assert trust_color(trust) == result
@mark.parametrize(
"trust, result",
[
(Trust.revoked, ""),
(Trust.full, ""),
(Trust.marginal, "~"),
(Trust.unknown, "~"),
(None, "?"),
],
)
def test_trust_icon(trust: Trust, result: str) -> None:
assert trust_icon(trust) == result
@mark.parametrize(
"trust",
[
Trust.revoked,
Trust.full,
Trust.marginal,
Trust.unknown,
],
)
@patch("libkeyringctl.trust.trust_icon")
@patch("libkeyringctl.trust.trust_color")
def test_format_trust_label(trust_color_mock: Mock, trust_icon_mock: Mock, trust: Trust) -> None:
trust_icon_mock.return_value = "ICON"
trust_color_mock.return_value = Color.GREEN
assert f"{Color.GREEN.value}ICON {trust.name}{Color.RST.value}" == format_trust_label(trust)
@mark.parametrize(
"trust, trust_filter, result",
[
(Trust.revoked, TrustFilter.unknown, False),
(Trust.full, TrustFilter.unknown, False),
(Trust.marginal, TrustFilter.unknown, False),
(Trust.unknown, TrustFilter.unknown, True),
(Trust.revoked, TrustFilter.marginal, False),
(Trust.full, TrustFilter.marginal, False),
(Trust.marginal, TrustFilter.marginal, True),
(Trust.unknown, TrustFilter.marginal, False),
(Trust.revoked, TrustFilter.full, False),
(Trust.full, TrustFilter.full, True),
(Trust.marginal, TrustFilter.full, False),
(Trust.unknown, TrustFilter.full, False),
(Trust.revoked, TrustFilter.revoked, True),
(Trust.full, TrustFilter.revoked, False),
(Trust.marginal, TrustFilter.revoked, False),
(Trust.unknown, TrustFilter.revoked, False),
(Trust.revoked, TrustFilter.unrevoked, False),
(Trust.full, TrustFilter.unrevoked, True),
(Trust.marginal, TrustFilter.unrevoked, True),
(Trust.unknown, TrustFilter.unrevoked, True),
(Trust.revoked, TrustFilter.all, True),
(Trust.full, TrustFilter.all, True),
(Trust.marginal, TrustFilter.all, True),
(Trust.unknown, TrustFilter.all, True),
],
)
def test_filter_by_trust(trust: Trust, trust_filter: TrustFilter, result: bool) -> None:
assert filter_by_trust(trust=trust, trust_filter=trust_filter) == result

198
tests/test_util.py Normal file
View File

@ -0,0 +1,198 @@
from os import chdir
from os import getcwd
from pathlib import Path
from tempfile import NamedTemporaryFile
from tempfile import TemporaryDirectory
from typing import Dict
from typing import List
from unittest.mock import Mock
from unittest.mock import patch
from pytest import mark
from pytest import raises
from libkeyringctl import util
from libkeyringctl.types import Fingerprint
from libkeyringctl.types import Trust
def test_cwd() -> None:
with TemporaryDirectory() as tmp_dir_name:
tmp_dir = Path(tmp_dir_name)
test_dir = tmp_dir / "test"
test_dir.mkdir()
chdir(tmp_dir)
with util.cwd(new_dir=test_dir):
assert getcwd() == str(test_dir)
assert getcwd() == str(tmp_dir)
@mark.parametrize(
"input_list, output_list",
[
([Path("/foo"), Path("/bar/foo"), Path("/foo/20")], [Path("/foo/20"), Path("/foo"), Path("/bar/foo")]),
],
)
def test_natural_sort_path(input_list: List[Path], output_list: List[Path]) -> None:
assert util.natural_sort_path(_list=input_list) == output_list
@mark.parametrize(
"raise_on_cmd, exit_on_error",
[
(False, True),
(False, False),
(True, True),
(True, False),
],
)
@patch("libkeyringctl.util.exit")
@patch("libkeyringctl.util.check_output")
def test_system(check_output_mock: Mock, exit_mock: Mock, raise_on_cmd: bool, exit_on_error: bool) -> None:
if raise_on_cmd:
check_output_mock.side_effect = util.CalledProcessError(returncode=1, cmd="foo", output=b"output")
with raises(util.CalledProcessError):
util.system(["foo"], exit_on_error=exit_on_error)
if exit_on_error:
exit_mock.assert_called_once_with(1)
else:
check_output_mock.return_value = b"output"
assert util.system(["foo"], exit_on_error=exit_on_error) == "output"
def test_absolute_path() -> None:
with TemporaryDirectory() as tmp_dir_name:
tmp_dir = Path(tmp_dir_name)
test_dir = tmp_dir / "test"
test_dir.mkdir()
chdir(tmp_dir)
assert util.absolute_path(path="test") == test_dir
def test_transform_fd_to_tmpfile() -> None:
with TemporaryDirectory() as tmp_dir_name:
tmp_dir = Path(tmp_dir_name)
with NamedTemporaryFile(dir=tmp_dir) as tmp_file:
tmp_file_fd = tmp_file.fileno()
util.transform_fd_to_tmpfile(
working_dir=tmp_dir,
sources=[Path("/foo"), Path(f"/proc/self/fd/{tmp_file_fd}")],
)
def test_get_cert_paths() -> None:
with TemporaryDirectory() as tmp_dir_name:
tmp_dir = Path(tmp_dir_name)
cert_dir1 = tmp_dir / "cert1"
cert_dir1.mkdir()
cert1 = cert_dir1 / "cert1.asc"
cert1.touch()
cert_dir2 = tmp_dir / "cert2"
cert_dir2.mkdir()
cert2 = cert_dir2 / "cert2.asc"
cert2.touch()
assert util.get_cert_paths(paths=[tmp_dir]) == {cert_dir1, cert_dir2}
def test_get_parent_cert_paths() -> None:
with TemporaryDirectory() as tmp_dir_name:
tmp_dir = Path(tmp_dir_name)
keyring_dir = tmp_dir / "keyring"
group_dir = keyring_dir / "parent"
user_dir = group_dir / "parent"
cert_dir1 = user_dir / "cert1"
cert_dir1.mkdir(parents=True)
cert1 = cert_dir1 / "cert1.asc"
cert1.touch()
cert_dir2 = cert_dir1 / "cert2"
cert_dir2.mkdir(parents=True)
cert2 = cert_dir2 / "cert2.asc"
cert2.touch()
assert util.get_parent_cert_paths(paths=[cert1, cert2]) == {cert_dir1}
@mark.parametrize(
"fingerprints, fingerprint, result",
[
(
[Fingerprint("foo"), Fingerprint("bar")],
Fingerprint("foo"),
True,
),
(
[Fingerprint("foo"), Fingerprint("bar")],
Fingerprint("baz"),
False,
),
],
)
def test_contains_fingerprint(fingerprints: List[Fingerprint], fingerprint: Fingerprint, result: bool) -> None:
assert util.contains_fingerprint(fingerprints=fingerprints, fingerprint=fingerprint) is result
@mark.parametrize(
"fingerprints, fingerprint, result",
[
([Fingerprint("blahfoo"), Fingerprint("blahbar")], Fingerprint("foo"), Fingerprint("blahfoo")),
([Fingerprint("blahfoo"), Fingerprint("blahbar")], Fingerprint("blahfoo"), Fingerprint("blahfoo")),
(
[Fingerprint("bazfoo"), Fingerprint("bazbar")],
Fingerprint("baz"),
None,
),
],
)
def test_get_fingerprint_from_partial(fingerprints: List[Fingerprint], fingerprint: Fingerprint, result: bool) -> None:
assert util.get_fingerprint_from_partial(fingerprints=fingerprints, fingerprint=fingerprint) is result
@mark.parametrize(
"trusts, trust, result",
[
(
{Fingerprint("foo"): Trust.full, Fingerprint("bar"): Trust.marginal},
Trust.full,
[Fingerprint("foo")],
),
(
{Fingerprint("foo"): Trust.full, Fingerprint("bar"): Trust.full},
Trust.full,
[Fingerprint("foo"), Fingerprint("bar")],
),
(
{Fingerprint("foo"): Trust.full, Fingerprint("bar"): Trust.marginal},
Trust.unknown,
[],
),
(
{},
Trust.unknown,
[],
),
],
)
def test_filter_fingerprints_by_trust(
trusts: Dict[Fingerprint, Trust], trust: Trust, result: List[Fingerprint]
) -> None:
assert util.filter_fingerprints_by_trust(trusts=trusts, trust=trust) == result
@mark.parametrize(
"_str, result",
[
("foobar", "foobar"),
("", ""),
("bbàáâãğț aa", "bbaaaagt_aa"),
("<>", ""),
("!#$%^&*()_☃", "___________"),
("_-.+@", "_-.+@"),
],
)
def test_simplify_ascii(_str: str, result: str) -> None:
assert util.simplify_ascii(_str=_str) == result