From 3e58b10e6fae75911cd4b729aa5e4a256ecb6462 Mon Sep 17 00:00:00 2001 From: Ihor Kalnytskyi Date: Wed, 8 May 2024 21:15:19 +0300 Subject: [PATCH] Add type annotations to tests Even though type annotations are nasty, they do bring one significant benefit - the code completion in text editors becomes much better. This patch adds type annotations to python modules in the 'tests' directory. --- pyproject.toml | 3 +- tests/conftest.py | 7 +- tests/test_keychain_password_store.py | 17 +- tests/test_keychain_shell.py | 20 ++- tests/test_keychain_system.py | 36 +++- tests/test_plugin.py | 247 +++++++++++++++++--------- 6 files changed, 224 insertions(+), 106 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 395d819..03ba7e6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,9 +44,10 @@ target-version = "py38" [tool.ruff.lint] select = ["ALL"] -ignore = ["ANN", "D", "PTH", "PLR", "PT005", "ISC001", "INP001", "S603", "S607", "COM812"] +ignore = ["D", "PTH", "PLR", "PT005", "ISC001", "INP001", "S603", "S607", "COM812", "FA100", "ANN101"] [tool.ruff.lint.per-file-ignores] +"src/*" = ["ANN"] "src/httpie_credential_store/_keychain.py" = ["S602"] "tests/*" = ["S101", "INP001"] diff --git a/tests/conftest.py b/tests/conftest.py index bb63428..4096426 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,8 +1,13 @@ +import pathlib +import typing + import pytest @pytest.fixture(scope="session", autouse=True) -def _httpie_config_dir(tmp_path_factory: pytest.TempPathFactory): +def _httpie_config_dir( + tmp_path_factory: pytest.TempPathFactory, +) -> typing.Generator[pathlib.Path, None, None]: """Set path to HTTPie configuration directory.""" # HTTPie can optionally read a path to configuration directory from diff --git a/tests/test_keychain_password_store.py b/tests/test_keychain_password_store.py index 195a181..609c976 100644 --- a/tests/test_keychain_password_store.py +++ b/tests/test_keychain_password_store.py @@ -7,10 +7,15 @@ import sys import tempfile import textwrap +import typing import pytest +if typing.TYPE_CHECKING: + from httpie_credential_store._keychain import PasswordStoreKeychain + + _is_macos = sys.platform == "darwin" @@ -29,13 +34,13 @@ # override 'tmp_path' fixture to return much shorter path to a temporary # directory. @pytest.fixture() - def tmp_path(): + def tmp_path() -> typing.Generator[pathlib.Path, None, None]: with tempfile.TemporaryDirectory() as path: yield pathlib.Path(path) @pytest.fixture() -def gpg_key_id(monkeypatch, tmp_path): +def gpg_key_id(monkeypatch: pytest.MonkeyPatch, tmp_path: pathlib.Path) -> str: """Return a Key ID of just generated GPG key.""" gpghome = tmp_path.joinpath(".gnupg") @@ -68,7 +73,7 @@ def gpg_key_id(monkeypatch, tmp_path): @pytest.fixture(autouse=True) -def password_store_dir(monkeypatch, tmp_path): +def password_store_dir(monkeypatch: pytest.MonkeyPatch, tmp_path: pathlib.Path) -> pathlib.Path: """Set password-store home directory to a temporary one.""" passstore = tmp_path.joinpath(".password-store") @@ -77,7 +82,7 @@ def password_store_dir(monkeypatch, tmp_path): @pytest.fixture() -def testkeychain(): +def testkeychain() -> "PasswordStoreKeychain": """Keychain instance under test.""" # For the same reasons as in tests/test_plugin.py, all imports that trigger @@ -88,7 +93,7 @@ def testkeychain(): return _keychain.PasswordStoreKeychain() -def test_secret_retrieved(testkeychain, gpg_key_id): +def test_secret_retrieved(testkeychain: "PasswordStoreKeychain", gpg_key_id: str) -> None: """The keychain returns stored secret, no bullshit.""" subprocess.check_call(["pass", "init", gpg_key_id]) @@ -97,7 +102,7 @@ def test_secret_retrieved(testkeychain, gpg_key_id): assert testkeychain.get(name="service/user") == "f00b@r" -def test_secret_not_found(testkeychain): +def test_secret_not_found(testkeychain: "PasswordStoreKeychain") -> None: """LookupError is raised when no secrets are found in the keychain.""" with pytest.raises(LookupError) as excinfo: diff --git a/tests/test_keychain_shell.py b/tests/test_keychain_shell.py index bef4eeb..ba7460a 100644 --- a/tests/test_keychain_shell.py +++ b/tests/test_keychain_shell.py @@ -1,12 +1,18 @@ """Tests shell keychain provider.""" import os +import pathlib +import typing import pytest +if typing.TYPE_CHECKING: + from httpie_credential_store._keychain import ShellKeychain + + @pytest.fixture() -def testkeychain(): +def testkeychain() -> "ShellKeychain": """Keychain instance under test.""" # For the same reasons as in tests/test_plugin.py, all imports that trigger @@ -17,7 +23,7 @@ def testkeychain(): return _keychain.ShellKeychain() -def test_secret_retrieved(testkeychain, tmp_path): +def test_secret_retrieved(testkeychain: "ShellKeychain", tmp_path: pathlib.Path) -> None: """The keychain returns stored secret, no bullshit.""" secrettxt = tmp_path.joinpath("secret.txt") @@ -25,7 +31,7 @@ def test_secret_retrieved(testkeychain, tmp_path): assert testkeychain.get(command=f"cat {secrettxt}") == "p@ss" -def test_secret_retrieved_pipe(testkeychain, tmp_path): +def test_secret_retrieved_pipe(testkeychain: "ShellKeychain", tmp_path: pathlib.Path) -> None: """The keychain returns stored secret even when pipes are used.""" secrettxt = tmp_path.joinpath("secret.txt") @@ -35,7 +41,7 @@ def test_secret_retrieved_pipe(testkeychain, tmp_path): assert testkeychain.get(command=command) == "p@ss" -def test_secret_not_found(testkeychain, tmp_path): +def test_secret_not_found(testkeychain: "ShellKeychain", tmp_path: pathlib.Path) -> None: """LookupError is raised when no secrets are found in the keychain.""" secrettxt = tmp_path.joinpath("secret.txt") @@ -49,6 +55,10 @@ def test_secret_not_found(testkeychain, tmp_path): @pytest.mark.parametrize(("args", "kwargs"), [pytest.param(["echo p@ss"], {}, id="args")]) -def test_keywords_only_arguments(testkeychain, args, kwargs): +def test_keywords_only_arguments( + testkeychain: "ShellKeychain", + args: typing.List[str], + kwargs: typing.Mapping[str, str], +) -> None: with pytest.raises(TypeError): testkeychain.get(*args, **kwargs) diff --git a/tests/test_keychain_system.py b/tests/test_keychain_system.py index ebf9356..397cbe9 100644 --- a/tests/test_keychain_system.py +++ b/tests/test_keychain_system.py @@ -1,26 +1,36 @@ """Tests system keychain provider.""" +import typing + import keyring +import keyring.backend +import keyring.compat import pytest +if typing.TYPE_CHECKING: + from httpie_credential_store._keychain import SystemKeychain + + class _InmemoryKeyring(keyring.backend.KeyringBackend): """Keyring backend that stores secrets in-memory.""" - priority = 1 + @keyring.compat.properties.classproperty + def priority(self) -> float: + return 1.0 - def __init__(self): + def __init__(self) -> None: self._keyring = {} - def get_password(self, service, username): + def get_password(self, service: str, username: str) -> typing.Optional[str]: return self._keyring.get((service, username)) - def set_password(self, service, username, password): + def set_password(self, service: str, username: str, password: str) -> None: self._keyring[(service, username)] = password @pytest.fixture(autouse=True) -def keyring_backend(): +def keyring_backend() -> typing.Generator[keyring.backend.KeyringBackend, None, None]: """Temporary set in-memory keyring as current backend.""" prev_backend = keyring.get_keyring() @@ -30,7 +40,7 @@ def keyring_backend(): @pytest.fixture() -def testkeychain(): +def testkeychain() -> "SystemKeychain": """Keychain instance under test.""" # For the same reasons as in tests/test_plugin.py, all imports that trigger @@ -41,14 +51,17 @@ def testkeychain(): return _keychain.SystemKeychain() -def test_secret_retrieved(testkeychain, keyring_backend): +def test_secret_retrieved( + testkeychain: "SystemKeychain", + keyring_backend: keyring.backend.KeyringBackend, +) -> None: """The keychain returns stored secret, no bullshit.""" keyring_backend.set_password("testsvc", "testuser", "p@ss") assert testkeychain.get(service="testsvc", username="testuser") == "p@ss" -def test_secret_not_found(testkeychain): +def test_secret_not_found(testkeychain: "SystemKeychain") -> None: """LookupError is raised when no secrets are found in the keychain.""" with pytest.raises(LookupError) as excinfo: @@ -66,7 +79,12 @@ def test_secret_not_found(testkeychain): pytest.param(["testsvc"], {"username": "testuser"}, id="args-kwargs"), ], ) -def test_keywords_only_arguments(testkeychain, keyring_backend, args, kwargs): +def test_keywords_only_arguments( + testkeychain: "SystemKeychain", + keyring_backend: keyring.backend.KeyringBackend, + args: typing.List[str], + kwargs: typing.Mapping[str, str], +) -> None: keyring_backend.set_password("testsvc", "testuser", "p@ss") with pytest.raises(TypeError): diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 0d6fc54..ad2ba31 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -2,9 +2,10 @@ import io import json -import os +import pathlib import re import sys +import typing from urllib.request import parse_http_list, parse_keqv_list @@ -15,13 +16,18 @@ _is_windows = sys.platform == "win32" +HttpieRunT = typing.Callable[[typing.List[typing.Union[str, bytes]]], int] +StoreSetT = typing.Callable[..., None] + + class _DigestAuthHeader: """Assert that a given Authorization header has expected digest parameters.""" - def __init__(self, parameters): + def __init__(self, parameters: typing.Mapping[str, typing.Any]) -> None: self._parameters = parameters - def __eq__(self, authorization_header_value): + def __eq__(self, authorization_header_value: object) -> bool: + assert isinstance(authorization_header_value, str) auth_type, auth_value = authorization_header_value.split(maxsplit=1) assert auth_type.lower() == "digest" assert parse_keqv_list(parse_http_list(auth_value)) == self._parameters @@ -31,18 +37,21 @@ def __eq__(self, authorization_header_value): class _RegExp: """Assert that a given string meets some expectations.""" - def __init__(self, pattern, flags=0): + def __init__(self, pattern: str, flags: int = 0) -> None: self._regex = re.compile(pattern, flags) - def __eq__(self, actual): + def __eq__(self, actual: object) -> bool: + assert isinstance(actual, str) return bool(self._regex.match(actual)) - def __repr__(self): + def __repr__(self) -> str: return self._regex.pattern @pytest.fixture(autouse=True) -def httpie_config_dir(_httpie_config_dir): +def httpie_config_dir( + _httpie_config_dir: pathlib.Path, +) -> typing.Generator[pathlib.Path, None, None]: """Return a path to HTTPie configuration directory.""" yield _httpie_config_dir @@ -55,36 +64,35 @@ def httpie_config_dir(_httpie_config_dir): @pytest.fixture() -def credentials_file(httpie_config_dir): +def credentials_file(httpie_config_dir: pathlib.Path) -> pathlib.Path: """Return a path to credentials file.""" - return os.path.join(httpie_config_dir, "credentials.json") + return httpie_config_dir / "credentials.json" @pytest.fixture() -def set_credentials(credentials_file): +def store_set(credentials_file: pathlib.Path) -> StoreSetT: """Render given credentials to credentials.json.""" - def render(credentials, mode=0o600): - with open(credentials_file, "w", encoding="UTF-8") as f: - f.write(json.dumps(credentials, indent=4)) - os.chmod(credentials_file, mode) + def render(credentials: typing.Union[typing.Mapping, typing.List], mode: int = 0o600) -> None: + credentials_file.write_text(json.dumps(credentials, indent=4)) + credentials_file.chmod(mode) return render @pytest.fixture() -def httpie_stderr(): +def httpie_stderr() -> io.StringIO: """Return captured standard error stream of HTTPie.""" return io.StringIO() @pytest.fixture() -def httpie_run(httpie_stderr): +def httpie_run(httpie_stderr: io.StringIO) -> HttpieRunT: """Run HTTPie from within this process.""" - def main(args): + def main(args: typing.List[typing.Union[str, bytes]]) -> int: # Imports of HTTPie internals must be local because otherwise they # won't take into account patched HTTPIE_CONFIG_DIR environment # variable. @@ -99,7 +107,7 @@ def main(args): @responses.activate -def test_basic_auth_plugin(httpie_run): +def test_basic_auth_plugin(httpie_run: HttpieRunT) -> None: """The plugin neither breaks nor overwrites existing auth plugins.""" httpie_run(["-A", "basic", "-a", "user:p@ss", "http://example.com"]) @@ -112,7 +120,7 @@ def test_basic_auth_plugin(httpie_run): @responses.activate -def test_store_auth_deactivated_by_default(httpie_run): +def test_store_auth_deactivated_by_default(httpie_run: HttpieRunT) -> None: """The plugin is deactivated by default.""" httpie_run(["http://example.com"]) @@ -125,10 +133,10 @@ def test_store_auth_deactivated_by_default(httpie_run): @responses.activate -def test_store_auth_basic(httpie_run, set_credentials): +def test_store_auth_basic(httpie_run: HttpieRunT, store_set: StoreSetT) -> None: """The plugin works for HTTP basic auth.""" - set_credentials( + store_set( [ { "url": "http://example.com", @@ -150,13 +158,17 @@ def test_store_auth_basic(httpie_run, set_credentials): @responses.activate -def test_store_auth_basic_keychain(httpie_run, set_credentials, tmp_path): +def test_store_auth_basic_keychain( + httpie_run: HttpieRunT, + store_set: StoreSetT, + tmp_path: pathlib.Path, +) -> None: """The plugin retrieves secrets from keychain for HTTP basic auth.""" secrettxt = tmp_path.joinpath("secret.txt") secrettxt.write_text("p@ss", encoding="UTF-8") - set_credentials( + store_set( [ { "url": "http://example.com", @@ -181,7 +193,7 @@ def test_store_auth_basic_keychain(httpie_run, set_credentials, tmp_path): @responses.activate -def test_store_auth_digest(httpie_run, set_credentials): +def test_store_auth_digest(httpie_run: HttpieRunT, store_set: StoreSetT) -> None: """The plugin works for HTTP digest auth.""" responses.add( @@ -198,7 +210,7 @@ def test_store_auth_digest(httpie_run, set_credentials): }, ) - set_credentials( + store_set( [ { "url": "http://example.com", @@ -241,10 +253,10 @@ def test_store_auth_digest(httpie_run, set_credentials): @responses.activate -def test_store_auth_token(httpie_run, set_credentials): +def test_store_auth_token(httpie_run: HttpieRunT, store_set: StoreSetT) -> None: """The plugin works for HTTP token auth.""" - set_credentials( + store_set( [ { "url": "http://example.com", @@ -265,10 +277,10 @@ def test_store_auth_token(httpie_run, set_credentials): @responses.activate -def test_store_auth_token_scheme(httpie_run, set_credentials): +def test_store_auth_token_scheme(httpie_run: HttpieRunT, store_set: StoreSetT) -> None: """The plugin works for HTTP token auth with custom scheme.""" - set_credentials( + store_set( [ { "url": "http://example.com", @@ -290,13 +302,17 @@ def test_store_auth_token_scheme(httpie_run, set_credentials): @responses.activate -def test_store_auth_token_keychain(httpie_run, set_credentials, tmp_path): +def test_store_auth_token_keychain( + httpie_run: HttpieRunT, + store_set: StoreSetT, + tmp_path: pathlib.Path, +) -> None: """The plugin retrieves secrets from keychain for HTTP token auth.""" secrettxt = tmp_path.joinpath("secret.txt") secrettxt.write_text("token-can-be-anything", encoding="UTF-8") - set_credentials( + store_set( [ { "url": "http://example.com", @@ -320,10 +336,10 @@ def test_store_auth_token_keychain(httpie_run, set_credentials, tmp_path): @responses.activate -def test_store_auth_header(httpie_run, set_credentials): +def test_store_auth_header(httpie_run: HttpieRunT, store_set: StoreSetT) -> None: """The plugin works for HTTP header auth.""" - set_credentials( + store_set( [ { "url": "http://example.com", @@ -345,13 +361,17 @@ def test_store_auth_header(httpie_run, set_credentials): @responses.activate -def test_store_auth_header_keychain(httpie_run, set_credentials, tmp_path): +def test_store_auth_header_keychain( + httpie_run: HttpieRunT, + store_set: StoreSetT, + tmp_path: pathlib.Path, +) -> None: """The plugin retrieves secrets from keychain for HTTP header auth.""" secrettxt = tmp_path.joinpath("secret.txt") secrettxt.write_text("value-can-be-anything", encoding="UTF-8") - set_credentials( + store_set( [ { "url": "http://example.com", @@ -376,10 +396,13 @@ def test_store_auth_header_keychain(httpie_run, set_credentials, tmp_path): @responses.activate -def test_store_auth_3rd_party_plugin(httpie_run, set_credentials): +def test_store_auth_3rd_party_plugin( + httpie_run: HttpieRunT, + store_set: StoreSetT, +) -> None: """The plugin works for third-party auth plugin.""" - set_credentials( + store_set( [ { "url": "http://example.com", @@ -403,13 +426,17 @@ def test_store_auth_3rd_party_plugin(httpie_run, set_credentials): @responses.activate -def test_store_auth_3rd_party_plugin_keychain(httpie_run, set_credentials, tmp_path): +def test_store_auth_3rd_party_plugin_keychain( + httpie_run: HttpieRunT, + store_set: StoreSetT, + tmp_path: pathlib.Path, +) -> None: """The plugin retrieves secrets from keychain for third-party auth plugins.""" secrettxt = tmp_path.joinpath("secret.txt") secrettxt.write_text("secret:rice", encoding="UTF-8") - set_credentials( + store_set( [ { "url": "http://example.com", @@ -436,10 +463,13 @@ def test_store_auth_3rd_party_plugin_keychain(httpie_run, set_credentials, tmp_p @responses.activate -def test_store_auth_multiple_token_header(httpie_run, set_credentials): +def test_store_auth_multiple_token_header( + httpie_run: HttpieRunT, + store_set: StoreSetT, +) -> None: """The plugin works for multiple auths.""" - set_credentials( + store_set( [ { "url": "http://example.com", @@ -472,10 +502,13 @@ def test_store_auth_multiple_token_header(httpie_run, set_credentials): @responses.activate -def test_store_auth_multiple_header_header(httpie_run, set_credentials): +def test_store_auth_multiple_header_header( + httpie_run: HttpieRunT, + store_set: StoreSetT, +) -> None: """The plugin supports usage of the same auth provider twice.""" - set_credentials( + store_set( [ { "url": "http://example.com", @@ -508,14 +541,18 @@ def test_store_auth_multiple_header_header(httpie_run, set_credentials): @responses.activate -def test_store_auth_multiple_token_header_keychain(httpie_run, set_credentials, tmp_path): +def test_store_auth_multiple_token_header_keychain( + httpie_run: HttpieRunT, + store_set: StoreSetT, + tmp_path: pathlib.Path, +) -> None: """The plugin retrieves secrets from keychains for combination of auths.""" tokentxt, secrettxt = tmp_path.joinpath("token.txt"), tmp_path.joinpath("secret.txt") tokentxt.write_text("token-can-be-anything", encoding="UTF-8") secrettxt.write_text("secret-can-be-anything", encoding="UTF-8") - set_credentials( + store_set( [ { "url": "http://example.com", @@ -625,10 +662,16 @@ def test_store_auth_multiple_token_header_keychain(httpie_run, set_credentials, ), ], ) -def test_store_auth_missing(httpie_run, set_credentials, httpie_stderr, auth, error_message): +def test_store_auth_missing( + httpie_run: HttpieRunT, + store_set: StoreSetT, + httpie_stderr: io.StringIO, + auth: typing.Mapping[str, str], + error_message: str, +) -> None: """The plugin raises error on wrong parameters.""" - set_credentials([{"url": "http://example.com", "auth": auth}]) + store_set([{"url": "http://example.com", "auth": auth}]) httpie_run(["-A", "store", "http://example.com"]) if _is_windows: @@ -695,10 +738,16 @@ def test_store_auth_missing(httpie_run, set_credentials, httpie_stderr, auth, er ), ], ) -def test_store_lookup_regexp(httpie_run, set_credentials, regexp, url, normalized_url): +def test_store_lookup_regexp( + httpie_run: HttpieRunT, + store_set: StoreSetT, + regexp: str, + url: str, + normalized_url: str, +) -> None: """The plugin uses pattern matching to find credentials.""" - set_credentials( + store_set( [ { "url": regexp, @@ -719,10 +768,10 @@ def test_store_lookup_regexp(httpie_run, set_credentials, regexp, url, normalize @responses.activate -def test_store_lookup_1st_matched_wins(httpie_run, set_credentials): +def test_store_lookup_1st_matched_wins(httpie_run: HttpieRunT, store_set: StoreSetT) -> None: """The plugin uses auth of first matched credential entry.""" - set_credentials( + store_set( [ { "url": "yoda.ua", @@ -751,13 +800,13 @@ def test_store_lookup_1st_matched_wins(httpie_run, set_credentials): @responses.activate -def test_store_lookup_many_credentials(httpie_run, set_credentials): +def test_store_lookup_many_credentials(httpie_run: HttpieRunT, store_set: StoreSetT) -> None: """The plugin works with many URLs and credentials.""" responses.add(responses.GET, "https://yoda.ua/about/", status=200) responses.add(responses.GET, "http://skywalker.com", status=200) - set_credentials( + store_set( [ { "url": "yoda.ua", @@ -804,10 +853,16 @@ def test_store_lookup_many_credentials(httpie_run, set_credentials): ), ], ) -def test_store_lookup_error(httpie_run, set_credentials, regexp, url, httpie_stderr): +def test_store_lookup_error( + httpie_run: HttpieRunT, + store_set: StoreSetT, + regexp: str, + url: str, + httpie_stderr: io.StringIO, +) -> None: """The plugin raises error if no credentials found.""" - set_credentials( + store_set( [ { "url": regexp, @@ -827,10 +882,10 @@ def test_store_lookup_error(httpie_run, set_credentials, regexp, url, httpie_std @responses.activate -def test_store_lookup_by_id(httpie_run, set_credentials): +def test_store_lookup_by_id(httpie_run: HttpieRunT, store_set: StoreSetT) -> None: """The plugin uses a given credential ID as a hint for 2+ matches.""" - set_credentials( + store_set( [ { "url": "yoda.ua", @@ -857,10 +912,14 @@ def test_store_lookup_by_id(httpie_run, set_credentials): @responses.activate -def test_store_lookup_by_id_error(httpie_run, set_credentials, httpie_stderr): +def test_store_lookup_by_id_error( + httpie_run: HttpieRunT, + store_set: StoreSetT, + httpie_stderr: io.StringIO, +) -> None: """The plugin raises error if no credentials found.""" - set_credentials( + store_set( [ { "id": "yoda", @@ -894,10 +953,14 @@ def test_store_lookup_by_id_error(httpie_run, set_credentials, httpie_stderr): pytest.param(0o400, id="0400"), ], ) -def test_store_permissions_safe(httpie_run, set_credentials, mode): +def test_store_permissions_safe( + httpie_run: HttpieRunT, + store_set: StoreSetT, + mode: int, +) -> None: """The plugin doesn't complain if credentials file has safe permissions.""" - set_credentials( + store_set( [ { "url": "http://example.com", @@ -941,15 +1004,15 @@ def test_store_permissions_safe(httpie_run, set_credentials, mode): ], ) def test_store_permissions_unsafe( - httpie_run, - set_credentials, - mode, - httpie_stderr, - credentials_file, -): + httpie_run: HttpieRunT, + store_set: StoreSetT, + mode: int, + httpie_stderr: io.StringIO, + credentials_file: pathlib.Path, +) -> None: """The plugin complains if credentials file has unsafe permissions.""" - set_credentials([{"url": "http://example.com", "auth": {}}], mode=mode) + store_set([{"url": "http://example.com", "auth": {}}], mode=mode) httpie_run(["-A", "store", "http://example.com"]) assert httpie_stderr.getvalue().strip() == ( @@ -970,15 +1033,15 @@ def test_store_permissions_unsafe( ], ) def test_store_permissions_not_enough( - httpie_run, - set_credentials, - mode, - httpie_stderr, - credentials_file, -): + httpie_run: HttpieRunT, + store_set: StoreSetT, + mode: int, + httpie_stderr: io.StringIO, + credentials_file: pathlib.Path, +) -> None: """The plugin complains if credentials file has unsafe permissions.""" - set_credentials([{"url": "http://example.com", "auth": {}}], mode=mode) + store_set([{"url": "http://example.com", "auth": {}}], mode=mode) httpie_run(["-A", "store", "http://example.com"]) assert httpie_stderr.getvalue().strip() == ( @@ -989,7 +1052,11 @@ def test_store_permissions_not_enough( @responses.activate -def test_store_auth_no_database(httpie_run, credentials_file, httpie_stderr): +def test_store_auth_no_database( + httpie_run: HttpieRunT, + credentials_file: pathlib.Path, + httpie_stderr: io.StringIO, +) -> None: """The plugin raises error if credentials file does not exist.""" httpie_run(["-A", "store", "http://example.com"]) @@ -1029,9 +1096,13 @@ def test_store_auth_no_database(httpie_run, credentials_file, httpie_stderr): ], ) def test_store_auth_header_value_illegal_characters( - httpie_run, set_credentials, httpie_stderr, auth, error -): - set_credentials([{"url": "http://example.com", "auth": auth}]) + httpie_run: HttpieRunT, + store_set: StoreSetT, + httpie_stderr: io.StringIO, + auth: typing.Mapping[str, str], + error: str, +) -> None: + store_set([{"url": "http://example.com", "auth": auth}]) httpie_run(["-A", "store", "http://example.com"]) assert len(responses.calls) == 0 @@ -1059,9 +1130,13 @@ def test_store_auth_header_value_illegal_characters( ], ) def test_store_auth_header_name_illegal_characters( - httpie_run, set_credentials, httpie_stderr, auth, error -): - set_credentials([{"url": "http://example.com", "auth": auth}]) + httpie_run: HttpieRunT, + store_set: StoreSetT, + httpie_stderr: io.StringIO, + auth: typing.Mapping[str, str], + error: str, +) -> None: + store_set([{"url": "http://example.com", "auth": auth}]) httpie_run(["-A", "store", "http://example.com"]) assert len(responses.calls) == 0 @@ -1070,10 +1145,14 @@ def test_store_auth_header_name_illegal_characters( @responses.activate @pytest.mark.parametrize("auth_type", ["store", "credential-store", "creds"]) -def test_auth_type_aliases(httpie_run, set_credentials, auth_type): +def test_auth_type_aliases( + httpie_run: HttpieRunT, + store_set: StoreSetT, + auth_type: str, +) -> None: """The plugin can be invoked via 'creds' alias.""" - set_credentials( + store_set( [ { "url": "http://example.com",