From 4627d786bc094a132cf6c4d49bcc9de14fca2819 Mon Sep 17 00:00:00 2001 From: Lasse Yledahl Date: Thu, 3 Oct 2024 13:45:16 +0000 Subject: [PATCH] use less of Any as type --- ruff.toml | 8 +++- .../clients/amapi_client/amapi_client.py | 3 +- src/eduid/common/decorators.py | 4 +- src/eduid/common/logging.py | 2 +- src/eduid/common/misc/encoders.py | 2 +- src/eduid/common/models/generic.py | 4 +- src/eduid/common/models/scim_base.py | 2 +- src/eduid/common/rpc/lookup_mobile_relay.py | 2 +- src/eduid/common/testing_base.py | 11 +++-- src/eduid/queue/decorators.py | 4 +- src/eduid/satosa/scimapi/stepup.py | 2 +- src/eduid/scimapi/testing.py | 2 +- src/eduid/scimapi/utils.py | 2 +- src/eduid/userdb/admin/__init__.py | 12 ++--- src/eduid/userdb/credentials/external.py | 2 +- src/eduid/userdb/credentials/password.py | 4 +- src/eduid/userdb/db/async_db.py | 2 +- src/eduid/userdb/db/sync_db.py | 2 +- src/eduid/userdb/element.py | 2 +- src/eduid/userdb/exceptions.py | 4 +- src/eduid/userdb/mail.py | 2 +- src/eduid/userdb/proofing/element.py | 2 +- src/eduid/userdb/proofing/state.py | 2 +- src/eduid/userdb/support/models.py | 2 +- src/eduid/userdb/testing/__init__.py | 20 ++++++-- src/eduid/userdb/testing/temp_instance.py | 2 +- src/eduid/userdb/tests/test_db.py | 9 ++-- .../userdb/tests/test_group_management.py | 6 +-- src/eduid/userdb/tests/test_resetpw.py | 6 +-- src/eduid/userdb/tests/test_userdb.py | 36 ++++++++------ src/eduid/userdb/tou.py | 2 +- src/eduid/userdb/user.py | 2 +- src/eduid/userdb/userdb.py | 2 +- src/eduid/vccs/server/hasher.py | 11 +++-- src/eduid/webapp/authn/tests/test_authn.py | 22 ++++----- src/eduid/webapp/bankid/tests/test_app.py | 10 ++-- src/eduid/webapp/common/api/middleware.py | 4 +- src/eduid/webapp/common/api/request.py | 15 +++--- src/eduid/webapp/common/api/schemas/csrf.py | 6 +-- src/eduid/webapp/common/api/schemas/email.py | 5 +- src/eduid/webapp/common/api/testing.py | 29 +++++------- .../webapp/common/api/tests/test_backdoor.py | 11 ++--- .../webapp/common/api/tests/test_inputs.py | 2 +- .../common/api/tests/test_nin_helpers.py | 5 +- src/eduid/webapp/common/authn/cache.py | 2 +- .../common/authn/tests/test_fido_tokens.py | 5 +- .../webapp/common/authn/tests/test_vccs.py | 9 ++-- .../webapp/common/session/eduid_session.py | 13 +++-- .../webapp/common/session/redis_session.py | 15 +++--- .../session/tests/test_eduid_session.py | 9 ++-- .../common/session/tests/test_namespaces.py | 15 +++--- .../session/tests/test_redis_session.py | 2 +- src/eduid/webapp/eidas/tests/test_app.py | 10 ++-- src/eduid/webapp/email/tests/test_app.py | 28 ++++++----- src/eduid/webapp/freja_eid/helpers.py | 6 +-- src/eduid/webapp/freja_eid/tests/test_app.py | 8 +++- .../webapp/group_management/tests/test_app.py | 14 ++++-- src/eduid/webapp/idp/decorators.py | 6 ++- src/eduid/webapp/idp/schemas.py | 2 +- src/eduid/webapp/idp/service.py | 5 +- src/eduid/webapp/idp/settings/common.py | 5 +- src/eduid/webapp/idp/sso_cache.py | 8 ++-- src/eduid/webapp/idp/tests/test_SSOSession.py | 5 +- src/eduid/webapp/idp/tests/test_api.py | 9 ++-- src/eduid/webapp/jsconfig/tests/test_app.py | 8 +++- src/eduid/webapp/ladok/tests/test_app.py | 15 ++++-- src/eduid/webapp/letter_proofing/ekopost.py | 3 +- .../webapp/letter_proofing/tests/test_app.py | 18 ++++--- .../lookup_mobile_proofing/tests/test_app.py | 8 +++- .../webapp/oidc_proofing/tests/test_app.py | 8 +++- src/eduid/webapp/orcid/tests/test_app.py | 5 +- .../webapp/personal_data/tests/test_app.py | 20 +++++--- src/eduid/webapp/phone/tests/test_app.py | 30 +++++++----- .../webapp/reset_password/tests/test_app.py | 47 ++++++++++--------- src/eduid/webapp/security/tests/test_app.py | 22 ++++----- .../security/tests/test_change_password.py | 12 +++-- .../webapp/security/tests/test_webauthn.py | 11 +++-- src/eduid/webapp/signup/tests/test_app.py | 12 +++-- src/eduid/webapp/support/app.py | 2 +- src/eduid/webapp/support/tests/test_app.py | 5 +- src/eduid/webapp/svipe_id/helpers.py | 10 ++-- src/eduid/webapp/svipe_id/tests/test_app.py | 8 +++- src/eduid/workers/am/testing.py | 20 ++++---- src/eduid/workers/am/tests/test_am.py | 8 +++- src/eduid/workers/am/tests/test_index.py | 5 +- src/eduid/workers/am/tests/test_signup.py | 9 +++- src/eduid/workers/am/tests/test_tasks.py | 12 +++-- src/eduid/workers/amapi/context_request.py | 8 ++-- src/eduid/workers/amapi/testing.py | 5 +- .../workers/amapi/tests/test_middleware.py | 3 +- src/eduid/workers/amapi/tests/test_status.py | 5 +- src/eduid/workers/amapi/tests/test_user.py | 9 +++- src/eduid/workers/job_runner/config.py | 2 +- .../client/mobile_lookup_client.py | 5 +- src/eduid/workers/lookup_mobile/decorators.py | 4 +- .../development/development_search_result.py | 3 +- .../lookup_mobile/test/test_decorators.py | 20 ++++---- src/eduid/workers/lookup_mobile/testing.py | 12 ++--- src/eduid/workers/msg/decorators.py | 4 +- src/eduid/workers/msg/testing.py | 12 ++--- .../workers/msg/tests/test_decorators.py | 10 ++-- src/eduid/workers/msg/tests/test_mongo.py | 5 +- src/eduid/workers/msg/tests/test_tasks.py | 5 +- 103 files changed, 501 insertions(+), 374 deletions(-) diff --git a/ruff.toml b/ruff.toml index 47b030f2c..7dba2fc3f 100644 --- a/ruff.toml +++ b/ruff.toml @@ -3,6 +3,10 @@ line-length = 120 target-version = "py310" [lint] -select = ["E", "F", "W", "I", "ASYNC", "UP", "FLY", "PERF", "FURB", "ERA", "ANN0", "ANN2"] +select = ["E", "F", "W", "I", "ASYNC", "UP", "FLY", "PERF", "FURB", "ERA", "ANN"] -ignore = ["E501"] +# ANN101 and ANN102 are depracated +ignore = ["E501", "ANN1"] + +[lint.flake8-annotations] +allow-star-arg-any = true diff --git a/src/eduid/common/clients/amapi_client/amapi_client.py b/src/eduid/common/clients/amapi_client/amapi_client.py index 479a9adaf..e6d93db71 100644 --- a/src/eduid/common/clients/amapi_client/amapi_client.py +++ b/src/eduid/common/clients/amapi_client/amapi_client.py @@ -9,6 +9,7 @@ __author__ = "masv" from eduid.common.models.amapi_user import ( + UserBaseRequest, UserUpdateEmailRequest, UserUpdateLanguageRequest, UserUpdateMetaCleanedRequest, @@ -27,7 +28,7 @@ def __init__(self, amapi_url: str, auth_data: GNAPClientAuthData, verify_tls: bo def _users_base_url(self) -> str: return urlappend(self.amapi_url, "users") - def _put(self, base_path: str, user: str, endpoint: str, body: Any) -> httpx.Response: + def _put(self, base_path: str, user: str, endpoint: str, body: UserBaseRequest) -> httpx.Response: return self.put(url=urlappend(base_path, f"{user}/{endpoint}"), content=body.json()) def update_user_email(self, user: str, body: UserUpdateEmailRequest) -> UserUpdateResponse: diff --git a/src/eduid/common/decorators.py b/src/eduid/common/decorators.py index 92eb76fd6..a2c135bce 100644 --- a/src/eduid/common/decorators.py +++ b/src/eduid/common/decorators.py @@ -29,7 +29,7 @@ def decorator(func1: Callable) -> Callable: fmt1 = "Call to deprecated function {name} ({reason})." @wraps(func1) - def new_func1(*args: Any, **kwargs: Any) -> Any: + def new_func1(*args: Any, **kwargs: Any) -> Any: # noqa: ANN401 warnings.simplefilter("always", DeprecationWarning) warnings.warn( fmt1.format(name=func1.__name__, reason=reason), category=DeprecationWarning, stacklevel=2 @@ -58,7 +58,7 @@ def new_func1(*args: Any, **kwargs: Any) -> Any: fmt2 = "Call to deprecated function {name}." @wraps(func2) - def new_func2(*args: Any, **kwargs: Any) -> Any: + def new_func2(*args: Any, **kwargs: Any) -> Any: # noqa: ANN401 warnings.simplefilter("always", DeprecationWarning) warnings.warn(fmt2.format(name=func2.__name__), category=DeprecationWarning, stacklevel=2) warnings.simplefilter("default", DeprecationWarning) diff --git a/src/eduid/common/logging.py b/src/eduid/common/logging.py index fad6563db..923f46ef2 100644 --- a/src/eduid/common/logging.py +++ b/src/eduid/common/logging.py @@ -139,7 +139,7 @@ def filter(self, record: logging.LogRecord) -> bool: def merge_config(base_config: dict[str, Any], new_config: dict[str, Any]) -> dict[str, Any]: """Recursively merge two dictConfig dicts.""" - def merge(node: dict[str, Any], key: str, value: Any) -> None: + def merge(node: dict[str, Any], key: str, value: object) -> None: if isinstance(value, dict): for item in value: if key in node: diff --git a/src/eduid/common/misc/encoders.py b/src/eduid/common/misc/encoders.py index 3a366eba6..318124f19 100644 --- a/src/eduid/common/misc/encoders.py +++ b/src/eduid/common/misc/encoders.py @@ -11,7 +11,7 @@ class EduidJSONEncoder(json.JSONEncoder): # TODO: This enables us to serialise NameIDs into the stored sessions, # but we don't seem to de-serialise them on load - def default(self, o: Any) -> str | Any: + def default(self, o: Any) -> str | Any: # noqa: ANN401 if isinstance(o, datetime): return o.isoformat() if isinstance(o, timedelta): diff --git a/src/eduid/common/models/generic.py b/src/eduid/common/models/generic.py index 431e79803..18d8a5d5b 100644 --- a/src/eduid/common/models/generic.py +++ b/src/eduid/common/models/generic.py @@ -16,7 +16,7 @@ # https://docs.pydantic.dev/2.6/concepts/types/#handling-third-party-types class ObjectIdPydanticAnnotation: @classmethod - def __get_pydantic_core_schema__(cls, _source_type: Any, _handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: + def __get_pydantic_core_schema__(cls, _source_type: Any, _handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: # noqa: ANN401 """ We return a pydantic_core.CoreSchema that behaves in the following ways: @@ -58,7 +58,7 @@ def __get_pydantic_json_schema__( class JWKPydanticAnnotation: @classmethod - def __get_pydantic_core_schema__(cls, _source_type: Any, _handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: + def __get_pydantic_core_schema__(cls, _source_type: Any, _handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: # noqa: ANN401 """ We return a pydantic_core.CoreSchema that behaves in the following ways: diff --git a/src/eduid/common/models/scim_base.py b/src/eduid/common/models/scim_base.py index 0182e6111..c3951038d 100644 --- a/src/eduid/common/models/scim_base.py +++ b/src/eduid/common/models/scim_base.py @@ -115,7 +115,7 @@ def is_group(self) -> bool: return self.ref is not None and "/Groups/" in self.ref @classmethod - def from_mapping(cls: type[TSubResource], data: Any) -> TSubResource: + def from_mapping(cls: type[TSubResource], data: object) -> TSubResource: return cls.model_validate(data) diff --git a/src/eduid/common/rpc/lookup_mobile_relay.py b/src/eduid/common/rpc/lookup_mobile_relay.py index 8b1be36d7..b9f25eeee 100644 --- a/src/eduid/common/rpc/lookup_mobile_relay.py +++ b/src/eduid/common/rpc/lookup_mobile_relay.py @@ -28,7 +28,7 @@ def find_nin_by_mobile(self, mobile_number: str) -> str | None: raise LookupMobileTaskFailed(f"find_nin_by_mobile task failed: {e}") @deprecated("This task seems unused") - def find_mobiles_by_nin(self, nin: str) -> Any: + def find_mobiles_by_nin(self, nin: str) -> Any: # noqa: ANN401 try: result = self._find_mobiles_by_NIN.delay(nin) result = result.get(timeout=10) # Lower timeout than standard gunicorn worker timeout (25) diff --git a/src/eduid/common/testing_base.py b/src/eduid/common/testing_base.py index b3c816599..562a49ec5 100644 --- a/src/eduid/common/testing_base.py +++ b/src/eduid/common/testing_base.py @@ -4,13 +4,14 @@ import logging.config import os import uuid +from collections.abc import Iterable from datetime import datetime, timedelta, timezone from enum import Enum from typing import Any, TypeVar from bson import ObjectId -from eduid.userdb.testing import MongoTestCase +from eduid.userdb.testing import MongoTestCase, SetupConfig logger = logging.getLogger(__name__) @@ -18,14 +19,14 @@ class CommonTestCase(MongoTestCase): """Base Test case for eduID webapps and workers""" - def setUp(self, *args: Any, **kwargs: Any) -> None: + def setUp(self, config: SetupConfig | None = None) -> None: """ set up tests """ if "EDUID_CONFIG_YAML" not in os.environ: os.environ["EDUID_CONFIG_YAML"] = "YAML_CONFIG_NOT_USED" - super().setUp(*args, **kwargs) + super().setUp(config=config) SomeData = TypeVar("SomeData") @@ -37,7 +38,7 @@ def normalised_data( """Utility function for normalising data before comparisons in test cases.""" class NormaliseEncoder(json.JSONEncoder): - def default(self, o: Any) -> str | Any: + def default(self, o: object) -> Iterable: if isinstance(o, datetime): if replace_datetime is not None: return replace_datetime @@ -66,7 +67,7 @@ class NormaliseDecoder(json.JSONDecoder): def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(object_hook=self.object_hook, *args, **kwargs) - def object_hook(self, o: Any) -> dict[str, Any]: + def object_hook(self, o: dict) -> dict[str, Any]: """ Decode any keys ending in _ts to datetime objects. diff --git a/src/eduid/queue/decorators.py b/src/eduid/queue/decorators.py index 2a639b4a7..d6acaf9c1 100644 --- a/src/eduid/queue/decorators.py +++ b/src/eduid/queue/decorators.py @@ -25,7 +25,7 @@ def __call__(self, f: Callable[..., Any]) -> Callable[..., Any]: if not self.enabled: return f - def audit(*args: Any, **kwargs: Any) -> Any: + def audit(*args: Any, **kwargs: Any) -> Any: # noqa: ANN401 ret = f(*args, **kwargs) if not isclass(ret) and self.collection: # we can't save class objects in mongodb date = utc_now() @@ -52,7 +52,7 @@ def disable(cls) -> None: cls.enabled = False @staticmethod - def _filter(func: str, data: Any, *args: Any, **kwargs: Any) -> Any: + def _filter(func: str, data: object, *args: Any, **kwargs: Any) -> Any: # noqa: ANN401 if data is False: return data if func == "_get_navet_data": diff --git a/src/eduid/satosa/scimapi/stepup.py b/src/eduid/satosa/scimapi/stepup.py index 434be5e98..b41238e90 100644 --- a/src/eduid/satosa/scimapi/stepup.py +++ b/src/eduid/satosa/scimapi/stepup.py @@ -469,7 +469,7 @@ def _handle_authn_response(self, context: satosa.context.Context, binding: SAMLB raise RuntimeError("Unexpected response type") return res - def _metadata_endpoint(self, context: satosa.context.Context, extra: Any) -> CallbackReturnType: + def _metadata_endpoint(self, context: satosa.context.Context, extra: object) -> CallbackReturnType: metadata_string = create_metadata_string(None, self.sp.config, 4, None, None, None, None, None).decode("utf-8") return Response(metadata_string, content="text/xml") diff --git a/src/eduid/scimapi/testing.py b/src/eduid/scimapi/testing.py index cba0e22ab..ea0592f1c 100644 --- a/src/eduid/scimapi/testing.py +++ b/src/eduid/scimapi/testing.py @@ -194,7 +194,7 @@ def _assertScimError( schemas: list[str] | None = None, status: int = 400, scim_type: str | None = None, - detail: Any | None = None, + detail: object | None = None, exclude_keys: list[str] | None = None, ) -> None: if schemas is None: diff --git a/src/eduid/scimapi/utils.py b/src/eduid/scimapi/utils.py index c2d433a49..2eec1ef40 100644 --- a/src/eduid/scimapi/utils.py +++ b/src/eduid/scimapi/utils.py @@ -63,7 +63,7 @@ def load_jwks(config: ScimApiConfig) -> jwk.JWKSet: def retryable_db_write(func: Callable) -> Callable: @functools.wraps(func) - def wrapper_run_func(*args: Any, **kwargs: Any) -> Any: + def wrapper_run_func(*args: Any, **kwargs: Any) -> Any: # noqa: ANN401 max_retries = 10 retry = 0 while True: diff --git a/src/eduid/userdb/admin/__init__.py b/src/eduid/userdb/admin/__init__.py index 07acbd5be..9e3886c02 100644 --- a/src/eduid/userdb/admin/__init__.py +++ b/src/eduid/userdb/admin/__init__.py @@ -9,7 +9,7 @@ import time from collections.abc import Generator from copy import deepcopy -from typing import Any +from typing import Any # noqa: F401 import bson import bson.json_util @@ -51,7 +51,7 @@ def __init__(self, myname: str | None = None, backupbase: str = "/root/raw_db_ch self._backupbase: str = backupbase self._file_num: int = 0 - def find(self, db: str, collection: str, search_filter: Any) -> Generator[RawData, None, None]: + def find(self, db: str, collection: str, search_filter: object) -> Generator[RawData, None, None]: """ Look for documents matching search_filter in the specified database and collection. @@ -72,7 +72,7 @@ def find(self, db: str, collection: str, search_filter: Any) -> Generator[RawDat ) sys.exit(1) - def save_with_backup(self, raw: RawData, dry_run: bool = True) -> Any: + def save_with_backup(self, raw: RawData, dry_run: bool = True) -> str | None: """ Save a mongodb document while trying to carefully make a backup of the document before, after and what changed. @@ -113,7 +113,7 @@ def save_with_backup(self, raw: RawData, dry_run: bool = True) -> Any: if raw.before == raw.doc: sys.stderr.write(f"Document in {db_coll} with id {_id} not changed, aborting save_with_backup\n") - return + return None self._file_num = 0 backup_dir = self._make_backupdir(db_coll, _id) @@ -132,13 +132,13 @@ def save_with_backup(self, raw: RawData, dry_run: bool = True) -> Any: # Write changes.txt after saving, so it will also indicate a successful save return self._write_changes(raw, backup_dir, res) - def _write_changes(self, raw: RawData, backup_dir: str, res: Any) -> Any: + def _write_changes(self, raw: RawData, backup_dir: str, res: str) -> str: """ Write a file with one line per change between the before-doc and current doc. The format is intended to be easy to grep through. """ - def safe_encode(k2: Any, v2: Any) -> str: + def safe_encode(k2: object, v2: object) -> str: try: return bson.json_util.dumps({k2: v2}, json_options=PYTHON_UUID_LEGACY_JSON_OPTIONS) except: diff --git a/src/eduid/userdb/credentials/external.py b/src/eduid/userdb/credentials/external.py index 89eab3996..b39042ba2 100644 --- a/src/eduid/userdb/credentials/external.py +++ b/src/eduid/userdb/credentials/external.py @@ -26,7 +26,7 @@ class ExternalCredential(Credential): @field_validator("credential_id", mode="before") @classmethod - def credential_id_objectid(cls, v: Any) -> str: + def credential_id_objectid(cls, v: object) -> str: """Turn ObjectId into string""" if isinstance(v, ObjectId): v = str(v) diff --git a/src/eduid/userdb/credentials/password.py b/src/eduid/userdb/credentials/password.py index d69daa032..7a9edad4d 100644 --- a/src/eduid/userdb/credentials/password.py +++ b/src/eduid/userdb/credentials/password.py @@ -1,7 +1,5 @@ from __future__ import annotations -from typing import Any - from bson import ObjectId from pydantic import Field, field_validator @@ -19,7 +17,7 @@ class Password(Credential): @field_validator("credential_id", mode="before") @classmethod - def credential_id_objectid(cls, v: Any) -> str: + def credential_id_objectid(cls, v: object) -> str: """Turn ObjectId into string""" if isinstance(v, ObjectId): v = str(v) diff --git a/src/eduid/userdb/db/async_db.py b/src/eduid/userdb/db/async_db.py index e76aa403f..292f82e1e 100644 --- a/src/eduid/userdb/db/async_db.py +++ b/src/eduid/userdb/db/async_db.py @@ -157,7 +157,7 @@ async def _drop_whole_collection(self) -> None: logger.warning(f"{self!s} Dropping collection {self._coll_name!r}") return await self._coll.drop() - async def _get_document_by_attr(self, attr: str, value: Any) -> Mapping[str, Any] | None: + async def _get_document_by_attr(self, attr: str, value: object) -> Mapping[str, Any] | None: """ Return the document in the MongoDB matching field=value diff --git a/src/eduid/userdb/db/sync_db.py b/src/eduid/userdb/db/sync_db.py index a6afbf0f1..f151334e5 100644 --- a/src/eduid/userdb/db/sync_db.py +++ b/src/eduid/userdb/db/sync_db.py @@ -182,7 +182,7 @@ def _get_all_docs(self) -> pymongo.cursor.Cursor[TUserDbDocument]: """ return self._coll.find({}) - def _get_document_by_attr(self, attr: str, value: Any) -> TUserDbDocument | None: + def _get_document_by_attr(self, attr: str, value: Any) -> TUserDbDocument | None: # noqa: ANN401 """ Return the document in the MongoDB matching field=value diff --git a/src/eduid/userdb/element.py b/src/eduid/userdb/element.py index 7f1a09bf8..7a4eb3530 100644 --- a/src/eduid/userdb/element.py +++ b/src/eduid/userdb/element.py @@ -245,7 +245,7 @@ class PrimaryElement(VerifiedElement, ABC): is_primary: bool = Field(default=False, alias="primary") # primary is the old name - def __setattr__(self, key: str, value: Any) -> None: + def __setattr__(self, key: str, value: object) -> None: """ raise PrimaryElementViolation when trying to set a primary element as unverified """ diff --git a/src/eduid/userdb/exceptions.py b/src/eduid/userdb/exceptions.py index 1aa2739dc..0cf281fc9 100644 --- a/src/eduid/userdb/exceptions.py +++ b/src/eduid/userdb/exceptions.py @@ -2,8 +2,6 @@ Exceptions thrown by the eduid.userdb database lookup functions. """ -from typing import Any - class EduIDDBError(Exception): """ @@ -13,7 +11,7 @@ class EduIDDBError(Exception): :type reason: object """ - def __init__(self, reason: Any) -> None: + def __init__(self, reason: object) -> None: Exception.__init__(self) self.reason = reason diff --git a/src/eduid/userdb/mail.py b/src/eduid/userdb/mail.py index 1aed56693..886bdddba 100644 --- a/src/eduid/userdb/mail.py +++ b/src/eduid/userdb/mail.py @@ -14,7 +14,7 @@ class MailAddress(PrimaryElement): @field_validator("email", mode="before") @classmethod - def validate_email(cls, v: Any) -> str: + def validate_email(cls, v: object) -> str: if not isinstance(v, str): raise ValueError("must be a string") return v.lower() diff --git a/src/eduid/userdb/proofing/element.py b/src/eduid/userdb/proofing/element.py index 3b61c6e28..a0e66a525 100644 --- a/src/eduid/userdb/proofing/element.py +++ b/src/eduid/userdb/proofing/element.py @@ -86,7 +86,7 @@ class EmailProofingElement(ProofingElement): @field_validator("email", mode="before") @classmethod - def validate_email(cls, v: Any) -> str: + def validate_email(cls, v: object) -> str: if not isinstance(v, str): raise ValueError("must be a string") return v.lower() diff --git a/src/eduid/userdb/proofing/state.py b/src/eduid/userdb/proofing/state.py index f31da57fb..c459d08c0 100644 --- a/src/eduid/userdb/proofing/state.py +++ b/src/eduid/userdb/proofing/state.py @@ -62,7 +62,7 @@ def _default_from_dict(cls: type[TProofingState], data: Mapping[str, Any], field return cls(**_data) @classmethod - def from_dict(cls, data: Mapping[str, Any]) -> Any: + def from_dict(cls, data: Mapping[str, Any]) -> Any: # noqa: ANN401 raise NotImplementedError(f"from_dict not implemented for class {cls.__name__}") def to_dict(self) -> TUserDbDocument: diff --git a/src/eduid/userdb/support/models.py b/src/eduid/userdb/support/models.py index 39d6982f0..5022a9327 100644 --- a/src/eduid/userdb/support/models.py +++ b/src/eduid/userdb/support/models.py @@ -11,7 +11,7 @@ class GenericFilterDict(dict): add_keys: list[str] | None = None remove_keys: list[str] | None = None - def __init__(self, data: Any | None) -> None: + def __init__(self, data: dict[str, Any] | None) -> None: """ Create a filtered dict with white- or blacklisting of keys diff --git a/src/eduid/userdb/testing/__init__.py b/src/eduid/userdb/testing/__init__.py index 86f67e969..1fcf76aec 100644 --- a/src/eduid/userdb/testing/__init__.py +++ b/src/eduid/userdb/testing/__init__.py @@ -8,6 +8,7 @@ import logging.config import unittest from collections.abc import Sequence +from dataclasses import dataclass from typing import Any, cast import pymongo @@ -72,6 +73,17 @@ def get_instance(cls: type[MongoTemporaryInstance], max_retry_seconds: int = 20) return cast(MongoTemporaryInstance, super().get_instance(max_retry_seconds=max_retry_seconds)) +@dataclass +class SetupConfig: + am_users: list[User] | None = None + am_settings: dict[str, Any] | None = None + want_mongo_uri: bool = True + users: list[str] | None = None + copy_user_to_private: bool = False + init_msg: bool = True + init_lookup_mobile: bool = True + + class MongoTestCase(unittest.TestCase): """TestCase with an embedded MongoDB temporary instance. @@ -82,7 +94,7 @@ class MongoTestCase(unittest.TestCase): A test can access the port using the attribute `port` """ - def setUp(self, am_users: list[User] | None = None) -> None: + def setUp(self, config: SetupConfig | None = None) -> None: """ Test case initialization. :return: @@ -110,9 +122,11 @@ def setUp(self, am_users: list[User] | None = None) -> None: else: self.settings.update(mongo_settings) - if am_users: + if config is None: + config = SetupConfig() + if config.am_users: # Set up test users in the MongoDB. - for user in am_users: + for user in config.am_users: logger.debug(f"Adding test user {user} to the database") self.amdb.save(user) diff --git a/src/eduid/userdb/testing/temp_instance.py b/src/eduid/userdb/testing/temp_instance.py index 4dbae760d..46661b33e 100644 --- a/src/eduid/userdb/testing/temp_instance.py +++ b/src/eduid/userdb/testing/temp_instance.py @@ -82,7 +82,7 @@ def setup_conn(self) -> bool: @property @abstractmethod - def conn(self) -> Any: + def conn(self) -> Any: # noqa: ANN401 """Return the initialised _conn instance. No default since it ought to be typed in the subclasses.""" raise NotImplementedError("All subclasses of EduidTemporaryInstance should implement the conn property") diff --git a/src/eduid/userdb/tests/test_db.py b/src/eduid/userdb/tests/test_db.py index 6ca516097..bcbeae231 100644 --- a/src/eduid/userdb/tests/test_db.py +++ b/src/eduid/userdb/tests/test_db.py @@ -5,7 +5,7 @@ import eduid.userdb.db as db from eduid.userdb.fixtures.users import UserFixtures from eduid.userdb.identity import IdentityType -from eduid.userdb.testing import MongoTestCase +from eduid.userdb.testing import MongoTestCase, SetupConfig class TestMongoDB(TestCase): @@ -61,10 +61,13 @@ def test_uri_with_options(self) -> None: class TestDB(MongoTestCase): - def setUp(self) -> None: # type: ignore[override] + def setUp(self, config: SetupConfig | None = None) -> None: _users = UserFixtures() self._am_users = [_users.new_unverified_user_example, _users.mocked_user_standard_2, _users.new_user_example] - super().setUp(am_users=self._am_users) + if config is None: + config = SetupConfig() + config.am_users = self._am_users + super().setUp(config=config) def test_db_count(self) -> None: self.assertEqual(len(self._am_users), self.amdb.db_count()) diff --git a/src/eduid/userdb/tests/test_group_management.py b/src/eduid/userdb/tests/test_group_management.py index b74abfdae..ac5050374 100644 --- a/src/eduid/userdb/tests/test_group_management.py +++ b/src/eduid/userdb/tests/test_group_management.py @@ -4,7 +4,7 @@ from eduid.userdb.fixtures.users import UserFixtures from eduid.userdb.group_management import GroupInviteState, GroupManagementInviteStateDB, GroupRole -from eduid.userdb.testing import MongoTestCase +from eduid.userdb.testing import MongoTestCase, SetupConfig from eduid.userdb.user import User __author__ = "lundberg" @@ -13,8 +13,8 @@ class TestResetGroupInviteStateDB(MongoTestCase): user: User - def setUp(self) -> None: # type: ignore[override] - super().setUp() + def setUp(self, config: SetupConfig | None = None) -> None: + super().setUp(config=config) self.user = UserFixtures().mocked_user_standard self.invite_state_db = GroupManagementInviteStateDB(self.tmp_db.uri) diff --git a/src/eduid/userdb/tests/test_resetpw.py b/src/eduid/userdb/tests/test_resetpw.py index 6062110cf..635b63efa 100644 --- a/src/eduid/userdb/tests/test_resetpw.py +++ b/src/eduid/userdb/tests/test_resetpw.py @@ -2,12 +2,12 @@ from eduid.userdb.reset_password import ResetPasswordEmailAndPhoneState, ResetPasswordEmailState, ResetPasswordStateDB from eduid.userdb.reset_password.element import CodeElement -from eduid.userdb.testing import MongoTestCase +from eduid.userdb.testing import MongoTestCase, SetupConfig class TestResetPasswordStateDB(MongoTestCase): - def setUp(self) -> None: # type: ignore[override] - super().setUp() + def setUp(self, config: SetupConfig | None = None) -> None: + super().setUp(config=config) self.resetpw_db = ResetPasswordStateDB(self.tmp_db.uri, "eduid_reset_password") def test_email_state(self) -> None: diff --git a/src/eduid/userdb/tests/test_userdb.py b/src/eduid/userdb/tests/test_userdb.py index 72f593206..fd7ca4665 100644 --- a/src/eduid/userdb/tests/test_userdb.py +++ b/src/eduid/userdb/tests/test_userdb.py @@ -1,5 +1,4 @@ import logging -from typing import Any import bson import pytest @@ -10,16 +9,19 @@ from eduid.userdb.exceptions import UserDoesNotExist, UserOutOfSync from eduid.userdb.fixtures.passwords import signup_password from eduid.userdb.fixtures.users import UserFixtures -from eduid.userdb.testing import MongoTestCase +from eduid.userdb.testing import MongoTestCase, SetupConfig from eduid.userdb.util import format_dict_for_debug logger = logging.getLogger(__name__) class TestUserDB(MongoTestCase): - def setUp(self, *args: Any, **kwargs: Any) -> None: + def setUp(self, config: SetupConfig | None = None) -> None: self.user = UserFixtures().mocked_user_standard - super().setUp(am_users=[self.user], **kwargs) + if config is None: + config = SetupConfig() + config.am_users = [self.user] + super().setUp(config=config) def test_get_user_by_id(self) -> None: """Test get_user_by_id""" @@ -78,9 +80,12 @@ def test_get_user_by_eppn_not_found(self) -> None: class UserMissingMeta(MongoTestCase): user: User - def setUp(self) -> None: # type: ignore[override] + def setUp(self, config: SetupConfig | None = None) -> None: self.user = UserFixtures().mocked_user_standard - super().setUp(am_users=[self.user]) + if config is None: + config = SetupConfig() + config.am_users = [self.user] + super().setUp(config=config) self._remove_meta_from_user_in_db(self.user) @@ -111,10 +116,13 @@ def test_update_user_old(self) -> None: class UpdateUser(MongoTestCase): - def setUp(self, *args: Any, **kwargs: Any) -> None: + def setUp(self, config: SetupConfig | None = None) -> None: _users = UserFixtures() self.user = _users.mocked_user_standard - super().setUp(am_users=[self.user, _users.mocked_user_standard_2], **kwargs) + if config is None: + config = SetupConfig() + config.am_users = [self.user, _users.mocked_user_standard_2] + super().setUp(config=config) def test_stale_user_meta_version(self) -> None: test_user = self.amdb.get_user_by_eppn(self.user.eppn) @@ -143,8 +151,8 @@ def test_ok(self) -> None: class TestUserDB_mail(MongoTestCase): - def setUp(self, *args: Any, **kwargs: Any) -> None: - super().setUp(*args, **kwargs) + def setUp(self, config: SetupConfig | None = None) -> None: + super().setUp(config=config) data1: TUserDbDocument = TUserDbDocument( { "_id": bson.ObjectId(), @@ -196,8 +204,8 @@ def test_get_user_by_mail_multiple(self) -> None: class TestUserDB_phone(MongoTestCase): - def setUp(self, *args: Any, **kwargs: Any) -> None: - super().setUp(*args, **kwargs) + def setUp(self, config: SetupConfig | None = None) -> None: + super().setUp(config=config) data1: TUserDbDocument = TUserDbDocument( { "_id": bson.ObjectId(), @@ -262,8 +270,8 @@ def test_get_user_by_phone_multiple(self) -> None: class TestUserDB_nin(MongoTestCase): # TODO: Keep for a while to make sure the conversion to identities work as expected - def setUp(self, *args: Any, **kwargs: Any) -> None: - super().setUp(*args, **kwargs) + def setUp(self, config: SetupConfig | None = None) -> None: + super().setUp(config=config) data1: TUserDbDocument = TUserDbDocument( { "_id": bson.ObjectId(), diff --git a/src/eduid/userdb/tou.py b/src/eduid/userdb/tou.py index fd550173d..64d222f23 100644 --- a/src/eduid/userdb/tou.py +++ b/src/eduid/userdb/tou.py @@ -21,7 +21,7 @@ class ToUEvent(Event): @field_validator("version") @classmethod - def _validate_tou_version(cls, v: Any) -> str: + def _validate_tou_version(cls, v: object) -> str: if not v: raise ValueError("ToU must have a version") if not isinstance(v, str): diff --git a/src/eduid/userdb/user.py b/src/eduid/userdb/user.py index 7f12fbe96..aee68847d 100644 --- a/src/eduid/userdb/user.py +++ b/src/eduid/userdb/user.py @@ -126,7 +126,7 @@ def __str__(self) -> str: return f"" return f"" - def __eq__(self, other: Any) -> bool: + def __eq__(self, other: Any) -> bool: # noqa: ANN401 if self.__class__ is not other.__class__: raise TypeError(f"Trying to compare objects of different class {other.__class__} != {self.__class__}") return self.to_dict() == other.to_dict() diff --git a/src/eduid/userdb/userdb.py b/src/eduid/userdb/userdb.py index 8a97f3210..e53654d7c 100644 --- a/src/eduid/userdb/userdb.py +++ b/src/eduid/userdb/userdb.py @@ -234,7 +234,7 @@ def get_user_by_eppn(self, eppn: str | None) -> UserVar: raise UserDoesNotExist(f"No user with eppn {repr(eppn)}") return res - def _get_user_by_attr(self, attr: str, value: Any) -> UserVar | None: + def _get_user_by_attr(self, attr: str, value: Any) -> UserVar | None: # noqa: ANN401 """ Locate a user in the userdb using any attribute and value. diff --git a/src/eduid/vccs/server/hasher.py b/src/eduid/vccs/server/hasher.py index 50d9ea8e1..e998ed9de 100644 --- a/src/eduid/vccs/server/hasher.py +++ b/src/eduid/vccs/server/hasher.py @@ -6,7 +6,7 @@ from binascii import unhexlify from collections.abc import Mapping from hashlib import sha1 -from typing import Any, Literal +from typing import Literal import pyhsm import yaml @@ -34,7 +34,7 @@ def __init__(self, lock: Lock | NoOpLock) -> None: def unlock(self, password: str) -> None: raise NotImplementedError("Subclass should implement unlock") - def info(self) -> Any: + def info(self) -> str | bytes | None: raise NotImplementedError("Subclass should implement info") async def hmac_sha1(self, key_handle: int | None, data: bytes) -> bytes: @@ -65,8 +65,9 @@ def unlock(self, password: str) -> None: """Unlock YubiHSM on startup. The password is supposed to be hex encoded.""" self._yhsm.unlock(unhexlify(password)) - def info(self) -> Any: - return self._yhsm.info() + def info(self) -> str: + ret: bytes = self._yhsm.info() + return ret.decode() async def hmac_sha1(self, key_handle: int | None, data: bytes) -> bytes: """ @@ -122,7 +123,7 @@ def __init__(self, keys: Mapping[int, str], lock: Lock | NoOpLock, debug: bool = def unlock(self, password: str) -> None: return None - def info(self) -> Any: + def info(self) -> str: return f"key handles loaded: {list(self.keys.keys())}" async def hmac_sha1(self, key_handle: int | None, data: bytes) -> bytes: diff --git a/src/eduid/webapp/authn/tests/test_authn.py b/src/eduid/webapp/authn/tests/test_authn.py index e9d61331d..ba405b76a 100644 --- a/src/eduid/webapp/authn/tests/test_authn.py +++ b/src/eduid/webapp/authn/tests/test_authn.py @@ -16,6 +16,7 @@ from eduid.common.config.parsers import load_config from eduid.common.misc.timeutil import utc_now from eduid.common.models.saml2 import EduidAuthnContextClass +from eduid.userdb.testing import SetupConfig from eduid.webapp.authn.app import AuthnApp, authn_init_app from eduid.webapp.authn.settings.common import AuthnConfig from eduid.webapp.common.api.testing import EduidAPITestCase @@ -42,14 +43,8 @@ class AuthnAPITestBase(EduidAPITestCase): app: AuthnApp - def setUp( # type: ignore[override] - self, - *args: list[Any], - users: list[str] | None = None, - copy_user_to_private: bool = False, - **kwargs: dict[str, Any], - ) -> None: - super().setUp(*args, users=users, copy_user_to_private=copy_user_to_private, **kwargs) + def setUp(self, config: SetupConfig | None = None) -> None: + super().setUp(config=config) self.idp_url = "https://idp.example.com/simplesaml/saml2/idp/SSOService.php" def update_config(self, config: dict[str, Any]) -> dict[str, Any]: @@ -252,8 +247,11 @@ class AuthnAPITestCase(AuthnAPITestBase): app: AuthnApp - def setUp(self, **kwargs: Any) -> None: # type: ignore[override] - super().setUp(users=["hubba-bubba", "hubba-fooo"], **kwargs) + def setUp(self, config: SetupConfig | None = None) -> None: + if config is None: + config = SetupConfig() + config.users = ["hubba-bubba", "hubba-fooo"] + super().setUp(config=config) def test_login_authn(self) -> None: self.authn("/authenticate", FrontendAction.LOGIN) @@ -364,8 +362,8 @@ class NoAuthnAPITestCase(EduidAPITestCase): app: AuthnTestApp - def setUp(self) -> None: # type: ignore[override] - super().setUp() + def setUp(self, config: SetupConfig | None = None) -> None: + super().setUp(config=config) test_views = Blueprint("testing", __name__) @test_views.route("/test") diff --git a/src/eduid/webapp/bankid/tests/test_app.py b/src/eduid/webapp/bankid/tests/test_app.py index 5aed3da63..f59736aca 100644 --- a/src/eduid/webapp/bankid/tests/test_app.py +++ b/src/eduid/webapp/bankid/tests/test_app.py @@ -13,6 +13,7 @@ from eduid.userdb.credentials.external import BankIDCredential, SwedenConnectCredential from eduid.userdb.element import ElementKey from eduid.userdb.identity import IdentityProofingMethod +from eduid.userdb.testing import SetupConfig from eduid.webapp.bankid.app import BankIDApp, init_bankid_app from eduid.webapp.bankid.helpers import BankIDMsg from eduid.webapp.common.api.messages import AuthnStatusMsg, TranslatableMsg @@ -34,7 +35,7 @@ class BankIDTests(ProofingTests[BankIDApp]): """Base TestCase for those tests that need a full environment setup""" - def setUp(self, *args: Any, **kwargs: Any) -> None: + def setUp(self, config: SetupConfig | None = None) -> None: self.test_user_eppn = "hubba-bubba" self.test_unverified_user_eppn = "hubba-baar" self.test_user_nin = NinIdentity( @@ -161,7 +162,10 @@ def setUp(self, *args: Any, **kwargs: Any) -> None: """ # noqa: E501 - super().setUp(users=["hubba-bubba", "hubba-baar"]) + if config is None: + config = SetupConfig() + config.users = ["hubba-bubba", "hubba-baar"] + super().setUp(config=config) def load_app(self, config: Mapping[str, Any]) -> BankIDApp: """ @@ -805,7 +809,7 @@ def test_mfa_login_backdoor(self, mock_request_user_sync: MagicMock) -> None: @unittest.skip("No support for magic cookie yet") @patch("eduid.common.rpc.am_relay.AmRelay.request_user_sync") - def test_nin_verify_backdoor(self, mock_request_user_sync: Any) -> None: + def test_nin_verify_backdoor(self, mock_request_user_sync: MagicMock) -> None: mock_request_user_sync.side_effect = self.request_user_sync eppn = self.test_unverified_user_eppn diff --git a/src/eduid/webapp/common/api/middleware.py b/src/eduid/webapp/common/api/middleware.py index a3b67b34a..5ffd124c4 100644 --- a/src/eduid/webapp/common/api/middleware.py +++ b/src/eduid/webapp/common/api/middleware.py @@ -1,6 +1,6 @@ __author__ = "lundberg" -from collections.abc import Callable +from collections.abc import Callable, Iterable from typing import Any # TODO: in python >= 3.11 import from wsgiref.types @@ -18,7 +18,7 @@ def __init__(self, app: Callable[..., Any], prefix: str = "", server_name: str = self.prefix = prefix self.server_name = server_name - def __call__(self, environ: WSGIEnvironment, start_response: StartResponse) -> Any | list[bytes]: + def __call__(self, environ: WSGIEnvironment, start_response: StartResponse) -> Iterable[bytes]: # Handle localhost requests for health checks if environ.get("REMOTE_ADDR") == "127.0.0.1": environ["HTTP_HOST"] = self.server_name diff --git a/src/eduid/webapp/common/api/request.py b/src/eduid/webapp/common/api/request.py index 578eb6274..41964f6c4 100644 --- a/src/eduid/webapp/common/api/request.py +++ b/src/eduid/webapp/common/api/request.py @@ -16,7 +16,7 @@ import logging from collections.abc import Callable, Iterator -from typing import Any, AnyStr +from typing import Any, AnyStr, TypeVar from flask import abort from flask.wrappers import Request as FlaskRequest @@ -54,7 +54,7 @@ class SanitizedImmutableMultiDict(ImmutableMultiDict, SanitationMixin): sanitize the extracted data. """ - def __getitem__(self, key: Any) -> str: + def __getitem__(self, key: str) -> str: """ Return the first data value for this key; raises KeyError if not found. @@ -65,7 +65,7 @@ def __getitem__(self, key: Any) -> str: value = super().__getitem__(key) return self.sanitize_input(value) - def getlist(self, key: Any, type: Callable[[Any], Any] | None = None) -> list: + def getlist(self, key: str, type: Callable[[Any], Any] | None = None) -> list: """ Return the list of items for a given key. If that key is not in the `MultiDict`, the return value will be an empty list. Just as `get` @@ -145,6 +145,9 @@ def to_dict(self, flat: bool = True) -> dict | dict[Any, list[str]]: return dict(self.lists()) +T = TypeVar("T") + + class SanitizedTypeConversionDict(ImmutableTypeConversionDict, SanitationMixin): """ See `werkzeug.datastructures.TypeConversionDict`. @@ -152,14 +155,14 @@ class SanitizedTypeConversionDict(ImmutableTypeConversionDict, SanitationMixin): sanitize the extracted data. """ - def __getitem__(self, key: Any) -> str: + def __getitem__(self, key: str) -> str: """ Sanitized __getitem__ """ val = super(ImmutableTypeConversionDict, self).__getitem__(key) return self.sanitize_input(str(val)) - def get(self, key: str, default: str | None = None, type: type | None = None) -> Any | None: # type: ignore[override] + def get(self, key: str, default: str | None = None, type: Callable[[Any], T] | None = None) -> str | T | None: # type: ignore[override] """ Sanitized, type conversion get. The value identified by `key` is sanitized, and if `type` @@ -188,7 +191,7 @@ def values(self) -> list[str]: # type: ignore[override] """ return [self.sanitize_input(v) for v in super(ImmutableTypeConversionDict, self).values()] - def items(self) -> list[tuple[Any, str]]: # type: ignore[override] + def items(self) -> list[tuple[str, str]]: # type: ignore[override] """ Sanitized items """ diff --git a/src/eduid/webapp/common/api/schemas/csrf.py b/src/eduid/webapp/common/api/schemas/csrf.py index f32ece972..524abdfb0 100644 --- a/src/eduid/webapp/common/api/schemas/csrf.py +++ b/src/eduid/webapp/common/api/schemas/csrf.py @@ -26,13 +26,13 @@ def validate_csrf_token(self, value: str, **kwargs: Any) -> None: logger.debug(f"Validated CSRF token in session: {session.get_csrf_token()}") @post_load - def post_processing(self, in_data: Any, **kwargs: Any) -> Any: + def post_processing(self, in_data: dict[str, Any], **kwargs: Any) -> dict[str, Any]: # Remove token from data forwarded to views in_data = self.remove_csrf_token(in_data) return in_data @staticmethod - def remove_csrf_token(in_data: Any, **kwargs: Any) -> Any: + def remove_csrf_token(in_data: dict[str, Any], **kwargs: Any) -> dict[str, Any]: del in_data["csrf_token"] return in_data @@ -41,7 +41,7 @@ class CSRFResponseMixin(Schema): csrf_token = fields.String(required=True) @pre_dump - def get_csrf_token(self, out_data: Any, **kwargs: Any) -> Any: + def get_csrf_token(self, out_data: dict[str, Any], **kwargs: Any) -> dict[str, Any]: # Generate a new csrf token for every response out_data["csrf_token"] = session.new_csrf_token() logger.debug(f'Generated new CSRF token in CSRFResponseMixin: {out_data["csrf_token"]}') diff --git a/src/eduid/webapp/common/api/schemas/email.py b/src/eduid/webapp/common/api/schemas/email.py index 09211e9a8..ca5610b4b 100644 --- a/src/eduid/webapp/common/api/schemas/email.py +++ b/src/eduid/webapp/common/api/schemas/email.py @@ -1,3 +1,4 @@ +from collections.abc import Mapping from typing import Any from marshmallow.fields import Email @@ -10,12 +11,12 @@ class LowercaseEmail(Email): Email field that serializes and deserializes to a lower case string. """ - def _serialize(self, value: str | bytes, attr: Any, obj: Any, **kwargs: Any) -> str | None: + def _serialize(self, value: str | bytes, attr: str | None, obj: object, **kwargs: Any) -> str | None: _value = super()._serialize(value, attr, obj, **kwargs) if _value is None: return None return _value.lower() - def _deserialize(self, value: str | bytes, attr: Any, data: Any, **kwargs: Any) -> str: + def _deserialize(self, value: str | bytes, attr: str | None, data: Mapping[str, Any] | None, **kwargs: Any) -> str: _value: str = super()._deserialize(value, attr, data, **kwargs) return _value.lower() diff --git a/src/eduid/webapp/common/api/testing.py b/src/eduid/webapp/common/api/testing.py index c0be64630..95b62aede 100644 --- a/src/eduid/webapp/common/api/testing.py +++ b/src/eduid/webapp/common/api/testing.py @@ -26,7 +26,7 @@ from eduid.userdb.fixtures.users import UserFixtures from eduid.userdb.logs.db import ProofingLog from eduid.userdb.proofing.state import NinProofingState -from eduid.userdb.testing import MongoTemporaryInstance +from eduid.userdb.testing import MongoTemporaryInstance, SetupConfig from eduid.userdb.userdb import UserDB from eduid.webapp.common.api.app import EduIDBaseApp from eduid.webapp.common.api.messages import AuthnStatusMsg, TranslatableMsg @@ -97,16 +97,12 @@ class EduidAPITestCase(CommonTestCase, Generic[TTestAppVar]): app: TTestAppVar browser: CSRFTestClient - def setUp( # type: ignore[override] - self, - *args: list[Any], - users: list[str] | None = None, - copy_user_to_private: bool = False, - **kwargs: dict[str, Any], - ) -> None: + def setUp(self, config: SetupConfig | None = None) -> None: + if config is None: + config = SetupConfig() # test users - if users is None: - users = ["hubba-bubba"] + if config.users is None: + config.users = ["hubba-bubba"] _users = UserFixtures() _standard_test_users = { @@ -116,14 +112,15 @@ def setUp( # type: ignore[override] } # Make a list of User object to be saved to the new temporary mongodb instance - am_users = [_standard_test_users[x] for x in users] + am_users = [_standard_test_users[x] for x in config.users] - super().setUp(am_users=am_users, *args, **kwargs) + config.am_users = am_users + super().setUp(config=config) self.user: User | None = None # Load the user from the database so that it can be saved there again in tests - _test_user = self.amdb.get_user_by_eppn(users[0]) + _test_user = self.amdb.get_user_by_eppn(config.users[0]) # Initialize some convenience variables on self based on the first user in `users' self.test_user = _test_user self.test_user_data = self.test_user.to_dict() @@ -132,8 +129,8 @@ def setUp( # type: ignore[override] # Set up Redis for shared sessions self.redis_instance = RedisTemporaryInstance.get_instance() # settings - config = deepcopy(TEST_CONFIG) - self.settings: dict[str, Any] = self.update_config(config) + test_config = deepcopy(TEST_CONFIG) + self.settings: dict[str, Any] = self.update_config(test_config) self.settings["redis_config"] = RedisConfig(host="localhost", port=self.redis_instance.port) assert isinstance(self.tmp_db, MongoTemporaryInstance) # please mypy self.settings["mongo_uri"] = self.tmp_db.uri @@ -147,7 +144,7 @@ def setUp( # type: ignore[override] self.content_type_json = "application/json" self.test_domain = "test.localhost" - if copy_user_to_private: + if config.copy_user_to_private: data = self.test_user.to_dict() _private_userdb = getattr(self.app, "private_userdb") assert isinstance(_private_userdb, UserDB) diff --git a/src/eduid/webapp/common/api/tests/test_backdoor.py b/src/eduid/webapp/common/api/tests/test_backdoor.py index b1b18bf5f..92b35bcbe 100644 --- a/src/eduid/webapp/common/api/tests/test_backdoor.py +++ b/src/eduid/webapp/common/api/tests/test_backdoor.py @@ -5,6 +5,7 @@ from eduid.common.config.base import EduIDBaseAppConfig, EduidEnvironment, MagicCookieMixin from eduid.common.config.parsers import load_config +from eduid.userdb.testing import SetupConfig from eduid.webapp.common.api.app import EduIDBaseApp from eduid.webapp.common.api.helpers import check_magic_cookie from eduid.webapp.common.api.testing import EduidAPITestCase @@ -42,14 +43,8 @@ def __init__(self, config: BackdoorTestConfig) -> None: class BackdoorTests(EduidAPITestCase[BackdoorTestApp]): - def setUp( # type: ignore[override] - self, - *args: list[Any], - users: list[str] | None = None, - copy_user_to_private: bool = False, - **kwargs: dict[str, Any], - ) -> None: - super().setUp(*args, users=users, copy_user_to_private=copy_user_to_private, **kwargs) + def setUp(self, config: SetupConfig | None = None) -> None: + super().setUp(config=config) self.test_get_url = "/get-code?eppn=pepin-pepon" self.test_app_domain = "test.localhost" diff --git a/src/eduid/webapp/common/api/tests/test_inputs.py b/src/eduid/webapp/common/api/tests/test_inputs.py index 0d04b1c4a..b9eb4b7c3 100644 --- a/src/eduid/webapp/common/api/tests/test_inputs.py +++ b/src/eduid/webapp/common/api/tests/test_inputs.py @@ -21,7 +21,7 @@ __author__ = "lundberg" -def dont_validate(value: Any) -> NoReturn: +def dont_validate(value: str | bytes) -> NoReturn: raise ValidationError(f"Problem with {value!r}") diff --git a/src/eduid/webapp/common/api/tests/test_nin_helpers.py b/src/eduid/webapp/common/api/tests/test_nin_helpers.py index d8e3087ff..fc5ec984c 100644 --- a/src/eduid/webapp/common/api/tests/test_nin_helpers.py +++ b/src/eduid/webapp/common/api/tests/test_nin_helpers.py @@ -22,6 +22,7 @@ ) from eduid.userdb.proofing import LetterProofingStateDB, LetterProofingUserDB, NinProofingElement, ProofingUser from eduid.userdb.proofing.state import NinProofingState +from eduid.userdb.testing import SetupConfig from eduid.webapp.common.api.app import EduIDBaseApp from eduid.webapp.common.api.helpers import ( add_nin_to_user, @@ -59,8 +60,8 @@ def load_app(self, config: Mapping[str, Any]) -> HelpersTestApp: return app - def setUp(self, *args: Any, **kwargs: Any) -> None: - super().setUp(*args, **kwargs) + def setUp(self, config: SetupConfig | None = None) -> None: + super().setUp(config=config) self.test_user_nin = "200001023456" self.wrong_test_user_nin = "199909096789" self.test_userdata = self.test_user.to_dict() diff --git a/src/eduid/webapp/common/authn/cache.py b/src/eduid/webapp/common/authn/cache.py index 8a1411bf0..8db36a03e 100644 --- a/src/eduid/webapp/common/authn/cache.py +++ b/src/eduid/webapp/common/authn/cache.py @@ -27,7 +27,7 @@ def key(self) -> str: def data(self) -> dict[str, VT]: return self._backend[self._key] - def __contains__(self, key: Any) -> bool: + def __contains__(self, key: object) -> bool: return key in self.data def __delitem__(self, key: str) -> None: diff --git a/src/eduid/webapp/common/authn/tests/test_fido_tokens.py b/src/eduid/webapp/common/authn/tests/test_fido_tokens.py index 478871b33..00b71ff34 100644 --- a/src/eduid/webapp/common/authn/tests/test_fido_tokens.py +++ b/src/eduid/webapp/common/authn/tests/test_fido_tokens.py @@ -10,6 +10,7 @@ from eduid.common.config.base import EduIDBaseAppConfig, WebauthnConfigMixin2 from eduid.common.config.parsers import load_config from eduid.userdb.fixtures.fido_credentials import u2f_credential, webauthn_credential +from eduid.userdb.testing import SetupConfig from eduid.webapp.common.api.app import EduIDBaseApp from eduid.webapp.common.api.testing import EduidAPITestCase from eduid.webapp.common.authn.fido_tokens import VerificationProblem, start_token_verification, verify_webauthn @@ -91,8 +92,8 @@ def __init__(self, config: MockFidoConfig) -> None: class FidoTokensTestCase(EduidAPITestCase): app: MockFidoApp - def setUp(self) -> None: # type: ignore[override] - super().setUp() + def setUp(self, config: SetupConfig | None = None) -> None: + super().setUp(config=config) self.webauthn_credential = webauthn_credential self.u2f_credential = u2f_credential diff --git a/src/eduid/webapp/common/authn/tests/test_vccs.py b/src/eduid/webapp/common/authn/tests/test_vccs.py index 179ce51e8..e4de9c511 100644 --- a/src/eduid/webapp/common/authn/tests/test_vccs.py +++ b/src/eduid/webapp/common/authn/tests/test_vccs.py @@ -3,7 +3,7 @@ from eduid.userdb.credentials.password import Password from eduid.userdb.fixtures.users import UserFixtures -from eduid.userdb.testing import MongoTestCase +from eduid.userdb.testing import MongoTestCase, SetupConfig from eduid.userdb.user import User from eduid.vccs.client import VCCSClient, VCCSClientHTTPError from eduid.webapp.common.authn import vccs as vccs_module @@ -13,8 +13,11 @@ class VCCSTestCase(MongoTestCase): user: User - def setUp(self) -> None: # type: ignore[override] - super().setUp(am_users=[UserFixtures().new_user_example]) + def setUp(self, config: SetupConfig | None = None) -> None: + if config is None: + config = SetupConfig() + config.am_users = [UserFixtures().new_user_example] + super().setUp(config=config) self.vccs_client = cast(VCCSClient, MockVCCSClient()) _user = self.amdb.get_user_by_mail("johnsmith@example.com") assert _user is not None diff --git a/src/eduid/webapp/common/session/eduid_session.py b/src/eduid/webapp/common/session/eduid_session.py index b03405c1d..153c100a0 100644 --- a/src/eduid/webapp/common/session/eduid_session.py +++ b/src/eduid/webapp/common/session/eduid_session.py @@ -6,7 +6,7 @@ import pprint from collections.abc import Iterator, MutableMapping from datetime import datetime -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, TypeVar from flask import Request as FlaskRequest from flask import Response as FlaskResponse @@ -64,7 +64,10 @@ class EduidNamespaces(BaseModel): freja_eid: FrejaEIDNamespace | None = None -class EduidSession(SessionMixin, MutableMapping[str, Any]): +VT = TypeVar("VT") + + +class EduidSession(SessionMixin, MutableMapping[str, VT]): """ Session implementing the flask.sessions.SessionMixin interface. @@ -129,10 +132,10 @@ def __str__(self) -> str: f"modified={self.modified}, cookie={self.short_id}>" ) - def __getitem__(self, key: str) -> Any: + def __getitem__(self, key: str) -> VT | str | None: return self._session.__getitem__(key) - def __setitem__(self, key: str, value: Any) -> None: + def __setitem__(self, key: str, value: VT | str | None) -> None: if key not in self._session or self._session[key] != value: self._session[key] = value logger.debug(f"SET {self}[{key}] = {value}") @@ -431,7 +434,7 @@ def open_session( # type: ignore[override] base_session = self.manager.get_session(meta=_meta, new=True) new = True - sess = EduidSession(app, _meta, base_session, new=new) + sess: EduidSession = EduidSession(app, _meta, base_session, new=new) logger.debug(f"Created/loaded session {sess} with base_session {base_session}") if app.debug or _conf.testing: _loaded_data = json.dumps(sess._session.to_dict(), indent=4, sort_keys=True) diff --git a/src/eduid/webapp/common/session/redis_session.py b/src/eduid/webapp/common/session/redis_session.py index 23ad4f188..5bdb04579 100644 --- a/src/eduid/webapp/common/session/redis_session.py +++ b/src/eduid/webapp/common/session/redis_session.py @@ -49,7 +49,7 @@ import logging import typing from collections.abc import Iterator, Mapping -from typing import Any +from typing import Any, TypeVar import nacl.encoding import nacl.secret @@ -114,7 +114,7 @@ def get_session(self, meta: SessionMeta, new: bool) -> RedisEncryptedSession: """ conn = self._get_connection() - res = RedisEncryptedSession( + res: RedisEncryptedSession = RedisEncryptedSession( conn, db_key=meta.session_id, encryption_key=meta.derive_key(self.secret, "nacl", nacl.secret.SecretBox.KEY_SIZE), @@ -155,7 +155,10 @@ class SessionOutOfSync(Exception): pass -class RedisEncryptedSession(typing.MutableMapping): +VT = TypeVar("VT") + + +class RedisEncryptedSession(typing.MutableMapping[str, VT]): """ Session objects that keep their data in a redis db. """ @@ -200,18 +203,18 @@ def __init__( self.secret_box = nacl.secret.SecretBox(encryption_key) - self._data: dict[str, Any] = {} + self._data: dict[str, VT] = {} def __str__(self) -> str: # Include hex(id(self)) for now to troubleshoot clobbered sessions return f"<{self.__class__.__name__} at {hex(id(self))}: db_key={self.short_id}>" - def __getitem__(self, key: str) -> Any: + def __getitem__(self, key: str) -> VT: if key in self._data: return self._data[key] raise KeyError(f"Key {repr(key)} not present in session") - def __setitem__(self, key: str, value: Any) -> None: + def __setitem__(self, key: str, value: VT) -> None: if self.whitelist and key not in self.whitelist: if self.raise_on_unknown: raise ValueError(f"Key {repr(key)} not allowed in session") diff --git a/src/eduid/webapp/common/session/tests/test_eduid_session.py b/src/eduid/webapp/common/session/tests/test_eduid_session.py index 989ef18ea..f7113122b 100644 --- a/src/eduid/webapp/common/session/tests/test_eduid_session.py +++ b/src/eduid/webapp/common/session/tests/test_eduid_session.py @@ -3,6 +3,7 @@ from eduid.common.config.base import EduIDBaseAppConfig from eduid.common.config.parsers import load_config +from eduid.userdb.testing import SetupConfig from eduid.webapp.common.api.testing import EduidAPITestCase from eduid.webapp.common.authn.middleware import AuthnBaseApp from eduid.webapp.common.authn.utils import no_authn_views @@ -41,7 +42,9 @@ def unauthenticated() -> str: @app.route("/return-session-key-test") def return_session_key_test() -> str: - return session["test"] + ret = session["test"] + assert isinstance(ret, str) + return ret @app.route("/common") def common() -> str: @@ -79,9 +82,9 @@ def logout() -> str: class EduidSessionTests(EduidAPITestCase): app: SessionTestApp - def setUp(self, **kwargs: Any) -> None: # type: ignore[override] + def setUp(self, config: SetupConfig | None = None) -> None: self.test_user_eppn = "hubba-bubba" - super().setUp(**kwargs) + super().setUp(config=config) def load_app(self, config: Mapping[str, Any]) -> SessionTestApp: """ diff --git a/src/eduid/webapp/common/session/tests/test_namespaces.py b/src/eduid/webapp/common/session/tests/test_namespaces.py index d652399cf..d949ac5d4 100644 --- a/src/eduid/webapp/common/session/tests/test_namespaces.py +++ b/src/eduid/webapp/common/session/tests/test_namespaces.py @@ -36,7 +36,7 @@ def test_to_dict_from_dict(self) -> None: _meta = SessionMeta.new(app_secret="secret") assert isinstance(self.app.session_interface, SessionFactory) base_session = self.app.session_interface.manager.get_session(meta=_meta, new=True) - session = EduidSession(app=self.app, meta=_meta, base_session=base_session, new=True) + session: EduidSession = EduidSession(app=self.app, meta=_meta, base_session=base_session, new=True) assert session.idp.sso_cookie_val is None @@ -76,7 +76,7 @@ def test_to_dict_from_dict_with_timestamp(self) -> None: _meta = SessionMeta.new(app_secret="secret") assert isinstance(self.app.session_interface, SessionFactory) base_session = self.app.session_interface.manager.get_session(meta=_meta, new=True) - first = EduidSession(app=self.app, meta=_meta, base_session=base_session, new=True) + first: EduidSession = EduidSession(app=self.app, meta=_meta, base_session=base_session, new=True) assert first.idp.sso_cookie_val is None @@ -94,10 +94,11 @@ def test_to_dict_from_dict_with_timestamp(self) -> None: # Validate that the session can be loaded again base_session = self.app.session_interface.manager.get_session(meta=_meta, new=False) - second = EduidSession(self.app, _meta, base_session, new=False) + second: EduidSession = EduidSession(self.app, _meta, base_session, new=False) # loaded_session is raw data from the storage backend, it won't have timestamps deserialised into datetimes # (done by pydantic when loading the data into the EduidSession), so in order to expect the same serialisation # again we need to do that here + assert isinstance(second["idp"], dict) second["idp"]["ts"] = datetime.fromisoformat(second["idp"]["ts"]) # ...and that it serialises to the same data that was persisted assert second._session.to_dict() == out @@ -109,13 +110,13 @@ def test_clear_namespace(self) -> None: _meta = SessionMeta.new(app_secret="secret") assert isinstance(self.app.session_interface, SessionFactory) base_session = self.app.session_interface.manager.get_session(meta=_meta, new=True) - first = EduidSession(app=self.app, meta=_meta, base_session=base_session, new=True) + first: EduidSession = EduidSession(app=self.app, meta=_meta, base_session=base_session, new=True) first.signup.email.address = "test@example.com" first.signup.email.verification_code = "123456" first.persist() # load session again and clear it base_session = self.app.session_interface.manager.get_session(meta=_meta, new=False) - second = EduidSession(self.app, _meta, base_session, new=False) + second: EduidSession = EduidSession(self.app, _meta, base_session, new=False) assert second.signup.email.address == "test@example.com" assert second.signup.email.verification_code == "123456" second.signup.clear() @@ -123,7 +124,7 @@ def test_clear_namespace(self) -> None: second.persist() # load session one more time and make sure verification_code is empty base_session = self.app.session_interface.manager.get_session(meta=_meta, new=False) - third = EduidSession(self.app, _meta, base_session, new=False) + third: EduidSession = EduidSession(self.app, _meta, base_session, new=False) assert third.signup.email.address == "test@example.com" assert third.signup.email.verification_code is None @@ -147,7 +148,7 @@ def test_sp_authns_cleanup(self) -> None: _meta = SessionMeta.new(app_secret="secret") assert isinstance(self.app.session_interface, SessionFactory) base_session = self.app.session_interface.manager.get_session(meta=_meta, new=True) - sess = EduidSession(app=self.app, meta=_meta, base_session=base_session, new=True) + sess: EduidSession = EduidSession(app=self.app, meta=_meta, base_session=base_session, new=True) for i in range(15): sess.authn.sp.authns[AuthnRequestRef(str(i))] = SP_AuthnRequest( frontend_action=FrontendAction.LOGIN, finish_url="some_url" diff --git a/src/eduid/webapp/common/session/tests/test_redis_session.py b/src/eduid/webapp/common/session/tests/test_redis_session.py index ae91734c9..5c0e95b16 100644 --- a/src/eduid/webapp/common/session/tests/test_redis_session.py +++ b/src/eduid/webapp/common/session/tests/test_redis_session.py @@ -70,7 +70,7 @@ def test_decrypt_session(self) -> None: ) assert meta.session_id == "36d4b3272d57b997be7f312ba0b80331747820ce51471566dd0bc3de0bc07a46" - session = RedisEncryptedSession( + session: RedisEncryptedSession = RedisEncryptedSession( conn=self.redis_instance.conn, db_key=meta.session_id, encryption_key=meta.derive_key(app_secret, "nacl", nacl.secret.SecretBox.KEY_SIZE), diff --git a/src/eduid/webapp/eidas/tests/test_app.py b/src/eduid/webapp/eidas/tests/test_app.py index b744b5328..efac7eda6 100644 --- a/src/eduid/webapp/eidas/tests/test_app.py +++ b/src/eduid/webapp/eidas/tests/test_app.py @@ -13,6 +13,7 @@ from eduid.userdb.credentials.external import EidasCredential, ExternalCredential, SwedenConnectCredential from eduid.userdb.element import ElementKey from eduid.userdb.identity import EIDASIdentity, EIDASLoa, IdentityProofingMethod, PridPersistence +from eduid.userdb.testing import SetupConfig from eduid.webapp.common.api.messages import AuthnStatusMsg, CommonMsg, TranslatableMsg, redirect_with_msg from eduid.webapp.common.api.testing import CSRFTestClient from eduid.webapp.common.authn.acs_enums import AuthnAcsAction @@ -34,7 +35,7 @@ class EidasTests(ProofingTests[EidasApp]): """Base TestCase for those tests that need a full environment setup""" - def setUp(self, *args: Any, **kwargs: Any) -> None: + def setUp(self, config: SetupConfig | None = None) -> None: self.test_user_eppn = "hubba-bubba" self.test_unverified_user_eppn = "hubba-baar" self.test_user_nin = NinIdentity( @@ -184,7 +185,10 @@ def setUp(self, *args: Any, **kwargs: Any) -> None: """ # noqa: E501 - super().setUp(users=["hubba-bubba", "hubba-baar"]) + if config is None: + config = SetupConfig() + config.users = ["hubba-bubba", "hubba-baar"] + super().setUp(config=config) def load_app(self, config: Mapping[str, Any]) -> EidasApp: """ @@ -1097,7 +1101,7 @@ def test_mfa_login_backdoor(self, mock_request_user_sync: MagicMock) -> None: self._verify_user_parameters(eppn, num_mfa_tokens=0, identity_verified=True, num_proofings=0) @patch("eduid.common.rpc.am_relay.AmRelay.request_user_sync") - def test_nin_verify_backdoor(self, mock_request_user_sync: Any) -> None: + def test_nin_verify_backdoor(self, mock_request_user_sync: MagicMock) -> None: mock_request_user_sync.side_effect = self.request_user_sync eppn = self.test_unverified_user_eppn diff --git a/src/eduid/webapp/email/tests/test_app.py b/src/eduid/webapp/email/tests/test_app.py index 370d8054e..f0346aaf0 100644 --- a/src/eduid/webapp/email/tests/test_app.py +++ b/src/eduid/webapp/email/tests/test_app.py @@ -2,7 +2,7 @@ from collections.abc import Mapping from datetime import datetime, timedelta from typing import Any -from unittest.mock import patch +from unittest.mock import MagicMock, patch from werkzeug.test import TestResponse @@ -10,13 +10,17 @@ from eduid.userdb import User from eduid.userdb.mail import MailAddress from eduid.userdb.proofing import EmailProofingElement, EmailProofingState +from eduid.userdb.testing import SetupConfig from eduid.webapp.common.api.testing import EduidAPITestCase from eduid.webapp.email.app import EmailApp, email_init_app class EmailTests(EduidAPITestCase[EmailApp]): - def setUp(self, *args: Any, **kwargs: Any) -> None: - super().setUp(*args, **kwargs, copy_user_to_private=True) + def setUp(self, config: SetupConfig | None = None) -> None: + if config is None: + config = SetupConfig() + config.copy_user_to_private = True + super().setUp(config=config) def load_app(self, config: Mapping[str, Any]) -> EmailApp: """ @@ -76,8 +80,8 @@ def _get_all_emails(self) -> dict: @patch("eduid.webapp.email.verifications.get_short_hash") def _post_email( self, - mock_code_verification: Any, - mock_request_user_sync: Any, + mock_code_verification: MagicMock, + mock_request_user_sync: MagicMock, data1: dict[str, Any] | None = None, send_data: bool = True, ) -> TestResponse: @@ -112,7 +116,7 @@ def _post_email( return client.post("/new") @patch("eduid.common.rpc.am_relay.AmRelay.request_user_sync") - def _post_primary(self, mock_request_user_sync: Any, data1: dict[str, Any] | None = None) -> TestResponse: + def _post_primary(self, mock_request_user_sync: MagicMock, data1: dict[str, Any] | None = None) -> TestResponse: """ Choose an email of the test user as primary @@ -137,7 +141,7 @@ def _post_primary(self, mock_request_user_sync: Any, data1: dict[str, Any] | Non return client.post("/primary", data=json.dumps(data), content_type=self.content_type_json) @patch("eduid.common.rpc.am_relay.AmRelay.request_user_sync") - def _remove(self, mock_request_user_sync: Any, data1: dict[str, Any] | None = None) -> TestResponse: + def _remove(self, mock_request_user_sync: MagicMock, data1: dict[str, Any] | None = None) -> TestResponse: """ POST to remove an email address form the test user @@ -160,7 +164,7 @@ def _remove(self, mock_request_user_sync: Any, data1: dict[str, Any] | None = No return client.post("/remove", data=json.dumps(data), content_type=self.content_type_json) @patch("eduid.common.rpc.am_relay.AmRelay.request_user_sync") - def _resend_code(self, mock_request_user_sync: Any, data1: dict[str, Any] | None = None) -> TestResponse: + def _resend_code(self, mock_request_user_sync: MagicMock, data1: dict[str, Any] | None = None) -> TestResponse: """ Trigger resending a new verification code to the email being verified @@ -183,8 +187,8 @@ def _resend_code(self, mock_request_user_sync: Any, data1: dict[str, Any] | None @patch("eduid.webapp.email.verifications.get_short_hash") def _verify( self, - mock_code_verification: Any, - mock_request_user_sync: Any, + mock_code_verification: MagicMock, + mock_request_user_sync: MagicMock, data1: dict[str, Any] | None = None, data2: dict[str, Any] | None = None, ) -> TestResponse: @@ -231,8 +235,8 @@ def _verify( @patch("eduid.webapp.email.verifications.get_short_hash") def _get_code_backdoor( self, - mock_code_verification: Any, - mock_request_user_sync: Any, + mock_code_verification: MagicMock, + mock_request_user_sync: MagicMock, data1: dict[str, Any] | None = None, email: str = "johnsmith3@example.com", code: str = "123456", diff --git a/src/eduid/webapp/freja_eid/helpers.py b/src/eduid/webapp/freja_eid/helpers.py index 6ce1d0e7a..125828051 100644 --- a/src/eduid/webapp/freja_eid/helpers.py +++ b/src/eduid/webapp/freja_eid/helpers.py @@ -1,7 +1,6 @@ import logging from datetime import date from enum import Enum, unique -from typing import Any from pydantic import BaseModel, ConfigDict, Field @@ -36,13 +35,14 @@ class FrejaEIDMsg(TranslatableMsg): class SessionOAuthCache: + # Used to store json-encoded data (OAuth->BaseOAuth->FrameworkIntegration) @staticmethod - def get(key: str) -> Any: + def get(key: str) -> str | None: logger.debug(f"Getting {key} from session.freja_eid.oauth_cache") return session.freja_eid.rp.authlib_cache.get(key) @staticmethod - def set(key: str, value: Any, expires: int | None = None) -> None: + def set(key: str, value: str, expires: int | None = None) -> None: session.freja_eid.rp.authlib_cache[key] = value logger.debug(f"Set {key}={value} (expires={expires}) in session.freja_eid.oauth_cache") diff --git a/src/eduid/webapp/freja_eid/tests/test_app.py b/src/eduid/webapp/freja_eid/tests/test_app.py index 066930922..2bd76e1d9 100644 --- a/src/eduid/webapp/freja_eid/tests/test_app.py +++ b/src/eduid/webapp/freja_eid/tests/test_app.py @@ -11,6 +11,7 @@ from eduid.common.config.base import FrontendAction from eduid.common.misc.timeutil import utc_now from eduid.userdb.identity import FrejaIdentity, FrejaRegistrationLevel, IdentityProofingMethod +from eduid.userdb.testing import SetupConfig from eduid.webapp.common.api.messages import CommonMsg from eduid.webapp.common.proofing.messages import ProofingMsg from eduid.webapp.common.proofing.testing import ProofingTests @@ -29,8 +30,11 @@ class FrejaEIDTests(ProofingTests[FrejaEIDApp]): """Base TestCase for those tests that need a full environment setup""" - def setUp(self, *args: Any, **kwargs: Any) -> None: - super().setUp(*args, **kwargs, users=["hubba-bubba", "hubba-baar"]) + def setUp(self, config: SetupConfig | None = None) -> None: + if config is None: + config = SetupConfig() + config.users = ["hubba-bubba", "hubba-baar"] + super().setUp(config=config) self.unverified_test_user = self.app.central_userdb.get_user_by_eppn("hubba-baar") self._user_setup() diff --git a/src/eduid/webapp/group_management/tests/test_app.py b/src/eduid/webapp/group_management/tests/test_app.py index 0f6c0bf73..3815f70a6 100644 --- a/src/eduid/webapp/group_management/tests/test_app.py +++ b/src/eduid/webapp/group_management/tests/test_app.py @@ -1,7 +1,7 @@ import json from collections.abc import Mapping from typing import Any -from unittest.mock import patch +from unittest.mock import MagicMock, patch from uuid import UUID from werkzeug.test import TestResponse @@ -13,6 +13,7 @@ from eduid.userdb.element import ElementKey from eduid.userdb.scimapi import GroupExtensions, ScimApiGroup from eduid.userdb.scimapi.userdb import ScimApiUser +from eduid.userdb.testing import SetupConfig from eduid.webapp.common.api.testing import EduidAPITestCase from eduid.webapp.group_management.app import GroupManagementApp, init_group_management_app from eduid.webapp.group_management.helpers import GroupManagementMsg @@ -36,9 +37,12 @@ def setUpClass(cls) -> None: ) super().setUpClass() - def setUp(self, *args: Any, **kwargs: Any) -> None: + def setUp(self, config: SetupConfig | None = None) -> None: users = ["hubba-bubba", "hubba-baar", "hubba-fooo"] - super().setUp(users=users, **kwargs) + if config is None: + config = SetupConfig() + config.users = users + super().setUp(config=config) self.test_user2 = self.app.central_userdb.get_user_by_eppn("hubba-baar") self.test_user3 = self.app.central_userdb.get_user_by_eppn("hubba-fooo") # Temporarily fix email address locally until test user fixtures are merged @@ -107,7 +111,7 @@ def _add_scim_group( @patch("eduid.common.rpc.mail_relay.MailRelay.sendmail") def _invite( - self, mock_sendmail: Any, group_scim_id: str, inviter: User, invite_address: str, role: str + self, mock_sendmail: MagicMock, group_scim_id: str, inviter: User, invite_address: str, role: str ) -> TestResponse: mock_sendmail.return_value = True with self.session_cookie(self.browser, inviter.eppn) as client: @@ -153,7 +157,7 @@ def _decline_invite(self, group_scim_id: str, invitee: User, invite_address: str @patch("eduid.common.rpc.mail_relay.MailRelay.sendmail") def _delete_invite( - self, mock_sendmail: Any, group_scim_id: str, inviter: User, invite_address: str, role: str + self, mock_sendmail: MagicMock, group_scim_id: str, inviter: User, invite_address: str, role: str ) -> TestResponse: mock_sendmail.return_value = True with self.session_cookie(self.browser, inviter.eppn) as client: diff --git a/src/eduid/webapp/idp/decorators.py b/src/eduid/webapp/idp/decorators.py index 437328b63..aa5e382df 100644 --- a/src/eduid/webapp/idp/decorators.py +++ b/src/eduid/webapp/idp/decorators.py @@ -4,7 +4,9 @@ from typing import Any from flask import Response, jsonify, request +from werkzeug.wrappers import Response as WerkzeugResponse +from eduid.webapp.common.api.messages import FluxData from eduid.webapp.common.api.schemas.models import FluxFailResponse from eduid.webapp.common.session import session from eduid.webapp.idp.app import current_idp_app as current_app @@ -19,7 +21,7 @@ def require_ticket(f: Callable) -> Callable: @wraps(f) - def require_ticket_decorator(*args: Any, **kwargs: Any) -> Response | Any: + def require_ticket_decorator(*args: Any, **kwargs: Any) -> Response | WerkzeugResponse: """Decorator to turn the 'ref' parameter sent by the frontend into a ticket (LoginContext)""" if "ref" not in kwargs: logger.debug("Login ref not supplied") @@ -58,7 +60,7 @@ def require_ticket_decorator(*args: Any, **kwargs: Any) -> Response | Any: def uses_sso_session(f: Callable) -> Callable: @wraps(f) - def uses_sso_session_decorator(*args: Any, **kwargs: Any) -> Any: + def uses_sso_session_decorator(*args: Any, **kwargs: Any) -> FluxData | WerkzeugResponse: """Decorator to supply the current SSO session, if one is found and still valid""" kwargs["sso_session"] = get_sso_session() diff --git a/src/eduid/webapp/idp/schemas.py b/src/eduid/webapp/idp/schemas.py index 8d2bb2080..48a01be2f 100644 --- a/src/eduid/webapp/idp/schemas.py +++ b/src/eduid/webapp/idp/schemas.py @@ -78,7 +78,7 @@ class MfaAuthResponsePayload(EduidSchema, CSRFResponseMixin): class ToUVersions(fields.Field): """Handle list of ToU versions available in the frontend both as comma-separated string (bug) and as list""" - def _deserialize(self, value: Any, attr: str | None, data: Any, **kwargs: Any) -> list[str] | None: + def _deserialize(self, value: object, attr: str | None, data: object, **kwargs: Any) -> list[str] | None: if value is None: return None if isinstance(value, str): diff --git a/src/eduid/webapp/idp/service.py b/src/eduid/webapp/idp/service.py index d38786975..575776135 100644 --- a/src/eduid/webapp/idp/service.py +++ b/src/eduid/webapp/idp/service.py @@ -60,7 +60,6 @@ """ from abc import ABC -from typing import Any from flask import request from pydantic import BaseModel, ConfigDict, Field, field_validator @@ -80,7 +79,7 @@ class SAMLQueryParams(BaseModel): @field_validator("SAMLRequest", "RelayState") @classmethod - def validate_query_params(cls, v: Any) -> str: + def validate_query_params(cls, v: object) -> str: if not isinstance(v, str) or not v: raise ValueError("must be a non-empty string") # TODO: perform extra sanitation? @@ -88,7 +87,7 @@ def validate_query_params(cls, v: Any) -> str: @field_validator("request_ref") @classmethod - def validate_request_ref(cls, v: Any) -> str | None: + def validate_request_ref(cls, v: object) -> str | None: if v is None: return None if not isinstance(v, str): diff --git a/src/eduid/webapp/idp/settings/common.py b/src/eduid/webapp/idp/settings/common.py index 0463d2918..fb678d7c0 100644 --- a/src/eduid/webapp/idp/settings/common.py +++ b/src/eduid/webapp/idp/settings/common.py @@ -3,7 +3,6 @@ """ from datetime import timedelta -from typing import Any from pydantic import Field, HttpUrl, field_validator from pydantic_core.core_schema import ValidationInfo @@ -158,7 +157,7 @@ class IdPConfig(EduIDBaseAppConfig, TouConfigMixin, WebauthnConfigMixin2, AmConf @field_validator("sso_cookie") @classmethod - def make_sso_cookie(cls, v: Any, info: ValidationInfo) -> CookieConfig: + def make_sso_cookie(cls, v: object, info: ValidationInfo) -> CookieConfig: # Convert sso_cookie from dict to the proper dataclass if isinstance(v, dict): return CookieConfig(**v) @@ -171,7 +170,7 @@ def make_sso_cookie(cls, v: Any, info: ValidationInfo) -> CookieConfig: @field_validator("sso_session_lifetime", mode="before") @classmethod - def validate_sso_session_lifetime(cls, v: Any) -> int | str | timedelta: + def validate_sso_session_lifetime(cls, v: object) -> int | str | timedelta: if isinstance(v, int): # legacy format for this was number of minutes v = v * 60 diff --git a/src/eduid/webapp/idp/sso_cache.py b/src/eduid/webapp/idp/sso_cache.py index 78ce1723a..6c0564f6a 100644 --- a/src/eduid/webapp/idp/sso_cache.py +++ b/src/eduid/webapp/idp/sso_cache.py @@ -63,6 +63,7 @@ from threading import Lock from typing import Any, cast +from eduid.common.decorators import deprecated from eduid.userdb.db import BaseDB from eduid.userdb.exceptions import EduIDDBError from eduid.webapp.idp.sso_session import SSOSession, SSOSessionId @@ -94,6 +95,7 @@ def release(self) -> None: pass +@deprecated("This class seems unused") class ExpiringCacheMem: """ Simplistic implementation of a cache that removes entrys as they become too old. @@ -126,7 +128,7 @@ def __init__(self, name: str, logger: logging.Logger | None, ttl: int, lock: "Lo if self.logger is not None: warnings.warn("Object logger deprecated, using module_logger", DeprecationWarning) - def add(self, key: SSOSessionId, info: Any, now: int | None = None) -> None: + def add(self, key: SSOSessionId, info: object, now: int | None = None) -> None: """ Add entry to the cache. @@ -180,7 +182,7 @@ def get(self, key: SSOSessionId) -> Mapping[str, Any] | None: """ return self._data.get(key) - def update(self, key: SSOSessionId, info: Any) -> None: + def update(self, key: SSOSessionId, info: object) -> None: """ Update an entry in the cache. @@ -204,7 +206,7 @@ def delete(self, key: SSOSessionId) -> bool: logger.debug(f"Failed deleting key {key!r} from {self.name!s} cache (entry did not exist)") return False - def items(self) -> Any: + def items(self) -> dict[SSOSessionId, Any]: """ Return all items from cache. """ diff --git a/src/eduid/webapp/idp/tests/test_SSOSession.py b/src/eduid/webapp/idp/tests/test_SSOSession.py index eb33b50fd..3ab439435 100644 --- a/src/eduid/webapp/idp/tests/test_SSOSession.py +++ b/src/eduid/webapp/idp/tests/test_SSOSession.py @@ -3,14 +3,15 @@ from bson import ObjectId from eduid.userdb.element import ElementKey +from eduid.userdb.testing import SetupConfig from eduid.webapp.idp.idp_authn import AuthnData from eduid.webapp.idp.sso_session import SSOSession from eduid.webapp.idp.tests.test_api import IdPAPITests class test_SSOSession(IdPAPITests): - def setUp(self) -> None: # type: ignore[override] - super().setUp() + def setUp(self, config: SetupConfig | None = None) -> None: + super().setUp(config=config) # This is real data extracted from MongoDB before a lot of refactoring self.data = { "_id": ObjectId("5fcde44d56cf512b51f1ac4e"), diff --git a/src/eduid/webapp/idp/tests/test_api.py b/src/eduid/webapp/idp/tests/test_api.py index 06967d27f..33f78474a 100644 --- a/src/eduid/webapp/idp/tests/test_api.py +++ b/src/eduid/webapp/idp/tests/test_api.py @@ -21,6 +21,7 @@ from eduid.userdb.credentials.external import TrustFramework, external_credential_from_dict from eduid.userdb.idp import IdPUser from eduid.userdb.mail import MailAddress +from eduid.userdb.testing import SetupConfig from eduid.userdb.user import User from eduid.webapp.common.api.testing import EduidAPITestCase from eduid.webapp.common.authn.cache import IdentityCache, OutstandingQueriesCache, StateCache @@ -94,12 +95,8 @@ class IdPAPITests(EduidAPITestCase[IdPApp]): default_user: TestUser - def setUp( - self, - *args: Any, - **kwargs: Any, - ) -> None: - super().setUp(*args, **kwargs) + def setUp(self, config: SetupConfig | None = None) -> None: + super().setUp(config=config) self.idp_entity_id = "https://unittest-idp.example.edu/idp.xml" self.relay_state = AuthnRequestRef("test-fest") self.sp_config = get_saml2_config(self.app.conf.pysaml2_config, name="SP_CONFIG") diff --git a/src/eduid/webapp/jsconfig/tests/test_app.py b/src/eduid/webapp/jsconfig/tests/test_app.py index b49018186..f03370f3a 100644 --- a/src/eduid/webapp/jsconfig/tests/test_app.py +++ b/src/eduid/webapp/jsconfig/tests/test_app.py @@ -6,15 +6,19 @@ from eduid.common.config.parsers import load_config from eduid.common.testing_base import normalised_data +from eduid.userdb.testing import SetupConfig from eduid.webapp.common.api.testing import CSRFTestClient, EduidAPITestCase from eduid.webapp.jsconfig.app import JSConfigApp, jsconfig_init_app from eduid.webapp.jsconfig.settings.common import JSConfigConfig class JSConfigTests(EduidAPITestCase[JSConfigApp]): - def setUp(self) -> None: # type: ignore[override] + def setUp(self, config: SetupConfig | None = None) -> None: self.data_dir = str(PurePath(__file__).with_name("data")) - super().setUp(copy_user_to_private=False) + if config is None: + config = SetupConfig() + config.copy_user_to_private = False + super().setUp(config=config) def load_app(self, config: Mapping[str, Any]) -> JSConfigApp: """ diff --git a/src/eduid/webapp/ladok/tests/test_app.py b/src/eduid/webapp/ladok/tests/test_app.py index 8c0de8c25..7678dbc25 100644 --- a/src/eduid/webapp/ladok/tests/test_app.py +++ b/src/eduid/webapp/ladok/tests/test_app.py @@ -8,6 +8,7 @@ from eduid.common.config.base import EduidEnvironment from eduid.userdb.ladok import Ladok, University, UniversityName +from eduid.userdb.testing import SetupConfig from eduid.webapp.common.api.testing import EduidAPITestCase __author__ = "lundberg" @@ -28,7 +29,7 @@ def json(self) -> Mapping[str, Any]: class LadokTests(EduidAPITestCase[LadokApp]): - def setUp(self, *args: Any, **kwargs: Any) -> None: + def setUp(self, config: SetupConfig | None = None) -> None: self.test_user_eppn = "hubba-bubba" self.test_unverified_user_eppn = "hubba-baar" self.ladok_user_external_id = uuid4() @@ -45,7 +46,10 @@ def setUp(self, *args: Any, **kwargs: Any) -> None: self.universities_response = MockResponse(200, self.university_data) - super().setUp(users=["hubba-bubba", "hubba-baar"]) + if config is None: + config = SetupConfig() + config.users = ["hubba-bubba", "hubba-baar"] + super().setUp(config=config) # remove Ladok data from test user user = self.app.central_userdb.get_user_by_eppn(eppn=self.test_user_eppn) @@ -199,12 +203,15 @@ def test_unlink_user_no_op(self) -> None: class LadokDevTests(EduidAPITestCase[LadokApp]): - def setUp(self, *args: Any, **kwargs: Any) -> None: + def setUp(self, config: SetupConfig | None = None) -> None: self.test_user_eppn = "hubba-bubba" self.test_unverified_user_eppn = "hubba-baar" self.ladok_user_external_id = uuid4() - super().setUp(users=["hubba-bubba", "hubba-baar"]) + if config is None: + config = SetupConfig() + config.users = ["hubba-bubba", "hubba-baar"] + super().setUp(config=config) def load_app(self, config: Mapping[str, Any]) -> LadokApp: """ diff --git a/src/eduid/webapp/letter_proofing/ekopost.py b/src/eduid/webapp/letter_proofing/ekopost.py index 816e46ffb..69cc9d45e 100644 --- a/src/eduid/webapp/letter_proofing/ekopost.py +++ b/src/eduid/webapp/letter_proofing/ekopost.py @@ -2,7 +2,6 @@ import json from datetime import datetime from io import BytesIO -from typing import Any from hammock import Hammock @@ -25,7 +24,7 @@ def __init__(self, config: LetterProofingConfig) -> None: self.ekopost_api = Hammock(config.ekopost_api_uri, auth=auth, verify=config.ekopost_api_verify_ssl) - def send(self, eppn: str, document: BytesIO) -> Any: + def send(self, eppn: str, document: BytesIO) -> str: """ Send a letter containing a PDF-document to the recipient specified in the document. diff --git a/src/eduid/webapp/letter_proofing/tests/test_app.py b/src/eduid/webapp/letter_proofing/tests/test_app.py index 7bc106407..38d489710 100644 --- a/src/eduid/webapp/letter_proofing/tests/test_app.py +++ b/src/eduid/webapp/letter_proofing/tests/test_app.py @@ -10,6 +10,7 @@ from eduid.userdb import NinIdentity from eduid.userdb.element import ElementKey from eduid.userdb.identity import IdentityType +from eduid.userdb.testing import SetupConfig from eduid.webapp.common.api.testing import EduidAPITestCase from eduid.webapp.letter_proofing.app import LetterProofingApp, init_letter_proofing_app from eduid.webapp.letter_proofing.helpers import LetterMsg @@ -20,11 +21,14 @@ class LetterProofingTests(EduidAPITestCase[LetterProofingApp]): """Base TestCase for those tests that need a full environment setup""" - def setUp(self) -> None: # type: ignore[override] + def setUp(self, config: SetupConfig | None = None) -> None: self.test_user_eppn = "hubba-baar" self.test_user_nin = "200001023456" self.test_user_wrong_nin = "190001021234" - super().setUp(users=["hubba-baar"]) + if config is None: + config = SetupConfig() + config.users = ["hubba-baar"] + super().setUp(config=config) @staticmethod def mock_response( @@ -32,7 +36,7 @@ def mock_response( content: AnyStr | None = None, json_data: Mapping[str, Any] | None = None, headers: Mapping[str, Any] | None = None, - raise_for_status: Any = None, + raise_for_status: Exception | None = None, ) -> Mock: """ since we typically test a bunch of different @@ -77,7 +81,7 @@ def update_config(self, config: dict[str, Any]) -> dict[str, Any]: return config # Helper methods - def get_state(self) -> Any: + def get_state(self) -> dict[str, Any]: with self.session_cookie(self.browser, self.test_user_eppn) as client: response = client.get("/proofing") self.assertEqual(response.status_code, 200) @@ -154,9 +158,9 @@ def _verify_code2( @patch("eduid.common.rpc.msg_relay.MsgRelay.get_postal_address") def get_code_backdoor( self, - mock_get_postal_address: Any, - mock_request_user_sync: Any, - mock_hammock: Any, + mock_get_postal_address: MagicMock, + mock_request_user_sync: MagicMock, + mock_hammock: MagicMock, cookie_name: str | None = None, cookie_value: str | None = None, add_cookie: bool = True, diff --git a/src/eduid/webapp/lookup_mobile_proofing/tests/test_app.py b/src/eduid/webapp/lookup_mobile_proofing/tests/test_app.py index 9fff3a560..a4138405c 100644 --- a/src/eduid/webapp/lookup_mobile_proofing/tests/test_app.py +++ b/src/eduid/webapp/lookup_mobile_proofing/tests/test_app.py @@ -7,6 +7,7 @@ from eduid.common.config.base import EduidEnvironment from eduid.common.rpc.exceptions import LookupMobileTaskFailed from eduid.userdb import User +from eduid.userdb.testing import SetupConfig from eduid.webapp.common.api.testing import EduidAPITestCase from eduid.webapp.lookup_mobile_proofing.app import MobileProofingApp, init_lookup_mobile_proofing_app from eduid.webapp.lookup_mobile_proofing.helpers import MobileMsg @@ -17,13 +18,16 @@ class LookupMobileProofingTests(EduidAPITestCase[MobileProofingApp]): """Base TestCase for those tests that need a full environment setup""" - def setUp(self) -> None: # type: ignore[override] + def setUp(self, config: SetupConfig | None = None) -> None: self.test_user_eppn = "hubba-baar" self.test_user_nin = "199001023456" fifteen_years_ago = datetime.now() - timedelta(days=15 * 365) self.test_user_nin_underage = f"{fifteen_years_ago.year}01023456" - super().setUp(users=["hubba-baar"]) + if config is None: + config = SetupConfig() + config.users = ["hubba-baar"] + super().setUp(config=config) def load_app(self, config: Mapping[str, Any]) -> MobileProofingApp: """ diff --git a/src/eduid/webapp/oidc_proofing/tests/test_app.py b/src/eduid/webapp/oidc_proofing/tests/test_app.py index 2929588dc..8511a63d1 100644 --- a/src/eduid/webapp/oidc_proofing/tests/test_app.py +++ b/src/eduid/webapp/oidc_proofing/tests/test_app.py @@ -10,6 +10,7 @@ from eduid.userdb import NinIdentity from eduid.userdb.proofing.state import OidcProofingState +from eduid.userdb.testing import SetupConfig from eduid.webapp.common.api.testing import EduidAPITestCase from eduid.webapp.oidc_proofing.app import OIDCProofingApp, init_oidc_proofing_app from eduid.webapp.oidc_proofing.helpers import create_proofing_state, handle_freja_eid_userinfo @@ -22,7 +23,7 @@ class OidcProofingTests(EduidAPITestCase): app: OIDCProofingApp - def setUp(self, *args: Any, **kwargs: Any) -> None: + def setUp(self, config: SetupConfig | None = None) -> None: self.test_user_eppn = "hubba-baar" self.test_user_nin = "200001023456" self.test_user_wrong_nin = "190001021234" @@ -63,7 +64,10 @@ def __init__(self, status_code: int, text: str) -> None: self.oidc_provider_config_response = MockResponse(200, json.dumps(self.oidc_provider_config)) - super().setUp(users=["hubba-baar"], *args, **kwargs) + if config is None: + config = SetupConfig() + config.users = ["hubba-baar"] + super().setUp(config=config) def load_app(self, config: dict[str, Any]) -> OIDCProofingApp: """ diff --git a/src/eduid/webapp/orcid/tests/test_app.py b/src/eduid/webapp/orcid/tests/test_app.py index a558bd98c..98b84b71a 100644 --- a/src/eduid/webapp/orcid/tests/test_app.py +++ b/src/eduid/webapp/orcid/tests/test_app.py @@ -8,6 +8,7 @@ from eduid.userdb.orcid import OidcAuthorization, OidcIdToken, Orcid from eduid.userdb.proofing import ProofingUser from eduid.userdb.proofing.state import OrcidProofingState +from eduid.userdb.testing import SetupConfig from eduid.webapp.common.api.testing import EduidAPITestCase from eduid.webapp.orcid.app import OrcidApp, init_orcid_app @@ -17,7 +18,7 @@ class OrcidTests(EduidAPITestCase[OrcidApp]): """Base TestCase for those tests that need a full environment setup""" - def setUp(self, *args: Any, **kwargs: Any) -> None: + def setUp(self, config: SetupConfig | None = None) -> None: self.test_user_eppn = "hubba-bubba" self.oidc_provider_config = { "token_endpoint_auth_signing_alg_values_supported": ["RS256"], @@ -64,7 +65,7 @@ def __init__(self, status_code: int, text: str) -> None: created_by="orcid", ) - super().setUp(*args, **kwargs) + super().setUp(config=config) def load_app(self, config: Mapping[str, Any]) -> OrcidApp: """ diff --git a/src/eduid/webapp/personal_data/tests/test_app.py b/src/eduid/webapp/personal_data/tests/test_app.py index cb58588ee..c7862a28b 100644 --- a/src/eduid/webapp/personal_data/tests/test_app.py +++ b/src/eduid/webapp/personal_data/tests/test_app.py @@ -2,12 +2,13 @@ from collections.abc import Mapping from datetime import timedelta from typing import Any -from unittest.mock import patch +from unittest.mock import MagicMock, patch from werkzeug.test import TestResponse from eduid.common.config.base import FrontendAction from eduid.userdb.element import ElementKey +from eduid.userdb.testing import SetupConfig from eduid.webapp.common.api.exceptions import ApiException from eduid.webapp.common.api.schemas.authn_status import AuthnActionStatus from eduid.webapp.common.api.testing import EduidAPITestCase @@ -16,8 +17,11 @@ class PersonalDataTests(EduidAPITestCase[PersonalDataApp]): - def setUp(self, *args: Any, **kwargs: Any) -> None: - super().setUp(*args, copy_user_to_private=True, **kwargs) + def setUp(self, config: SetupConfig | None = None) -> None: + if config is None: + config = SetupConfig() + config.copy_user_to_private = True + super().setUp(config=config) def load_app(self, config: Mapping[str, Any]) -> PersonalDataApp: """ @@ -65,7 +69,7 @@ def _get_user_all_data(self, eppn: str) -> TestResponse: @patch("eduid.common.rpc.am_relay.AmRelay.request_user_sync") def _post_user( - self, mock_request_user_sync: Any, mod_data: dict[str, Any] | None = None, verified_user: bool = True + self, mock_request_user_sync: MagicMock, mod_data: dict[str, Any] | None = None, verified_user: bool = True ) -> TestResponse: """ POST personal data for the test user @@ -95,7 +99,7 @@ def _post_user( @patch("eduid.common.rpc.am_relay.AmRelay.request_user_sync") def _post_user_name( - self, mock_request_user_sync: Any, mod_data: dict[str, Any] | None = None, verified_user: bool = True + self, mock_request_user_sync: MagicMock, mod_data: dict[str, Any] | None = None, verified_user: bool = True ) -> TestResponse: """ POST user name for the test user @@ -124,7 +128,7 @@ def _post_user_name( @patch("eduid.common.rpc.am_relay.AmRelay.request_user_sync") def _post_user_language( - self, mock_request_user_sync: Any, mod_data: dict[str, Any] | None = None, verified_user: bool = True + self, mock_request_user_sync: MagicMock, mod_data: dict[str, Any] | None = None, verified_user: bool = True ) -> TestResponse: """ POST user language for the test user @@ -159,7 +163,9 @@ def _get_preferences(self, eppn: str | None = None) -> TestResponse: return response2 @patch("eduid.common.rpc.am_relay.AmRelay.request_user_sync") - def _post_preferences(self, mock_request_user_sync: Any, mod_data: dict[str, Any] | None = None) -> TestResponse: + def _post_preferences( + self, mock_request_user_sync: MagicMock, mod_data: dict[str, Any] | None = None + ) -> TestResponse: """ POST preferences for the test user """ diff --git a/src/eduid/webapp/phone/tests/test_app.py b/src/eduid/webapp/phone/tests/test_app.py index bf74aff08..b1f8b8887 100644 --- a/src/eduid/webapp/phone/tests/test_app.py +++ b/src/eduid/webapp/phone/tests/test_app.py @@ -8,14 +8,18 @@ from werkzeug.test import TestResponse from eduid.common.config.base import EduidEnvironment +from eduid.userdb.testing import SetupConfig from eduid.webapp.common.api.testing import EduidAPITestCase from eduid.webapp.phone.app import PhoneApp, phone_init_app from eduid.webapp.phone.helpers import PhoneMsg class PhoneTests(EduidAPITestCase[PhoneApp]): - def setUp(self, *args: Any, **kwargs: Any) -> None: - super().setUp(*args, copy_user_to_private=True, **kwargs) + def setUp(self, config: SetupConfig | None = None) -> None: + if config is None: + config = SetupConfig() + config.copy_user_to_private = True + super().setUp(config=config) self.test_number = "+34609609609" @@ -59,9 +63,9 @@ def _get_all_phone(self, eppn: str | None = None) -> dict: @patch("eduid.common.rpc.msg_relay.MsgRelay.sendsms") def _post_phone( self, - mock_phone_validator: Any, - mock_code_verification: Any, - mock_request_user_sync: Any, + mock_phone_validator: MagicMock, + mock_code_verification: MagicMock, + mock_request_user_sync: MagicMock, mod_data: dict[str, Any] | None = None, send_data: bool = True, ) -> TestResponse: @@ -95,7 +99,7 @@ def _post_phone( return client.post("/new") @patch("eduid.common.rpc.am_relay.AmRelay.request_user_sync") - def _post_primary(self, mock_request_user_sync: Any, mod_data: dict[str, Any] | None = None) -> TestResponse: + def _post_primary(self, mock_request_user_sync: MagicMock, mod_data: dict[str, Any] | None = None) -> TestResponse: """ Set phone number as the primary number for the test user @@ -118,7 +122,7 @@ def _post_primary(self, mock_request_user_sync: Any, mod_data: dict[str, Any] | return client.post("/primary", data=json.dumps(data), content_type=self.content_type_json) @patch("eduid.common.rpc.am_relay.AmRelay.request_user_sync") - def _remove(self, mock_request_user_sync: Any, mod_data: dict[str, Any] | None = None) -> TestResponse: + def _remove(self, mock_request_user_sync: MagicMock, mod_data: dict[str, Any] | None = None) -> TestResponse: """ Remove phone number from the test user @@ -145,9 +149,9 @@ def _remove(self, mock_request_user_sync: Any, mod_data: dict[str, Any] | None = @patch("eduid.common.rpc.msg_relay.MsgRelay.sendsms") def _send_code( self, - mock_phone_validator: Any, - mock_request_user_sync: Any, - mock_verification_code: Any, + mock_phone_validator: MagicMock, + mock_request_user_sync: MagicMock, + mock_verification_code: MagicMock, mod_data: dict[str, Any] | None = None, captcha_completed: bool = True, ) -> TestResponse: @@ -178,9 +182,9 @@ def _send_code( @patch("eduid.common.rpc.msg_relay.MsgRelay.sendsms") def _get_code_backdoor( self, - mock_phone_validator: Any, - mock_code_verification: Any, - mock_request_user_sync: Any, + mock_phone_validator: MagicMock, + mock_code_verification: MagicMock, + mock_request_user_sync: MagicMock, mod_data: dict[str, Any] | None = None, phone: str = "+34670123456", code: str = "5250f9a4", diff --git a/src/eduid/webapp/reset_password/tests/test_app.py b/src/eduid/webapp/reset_password/tests/test_app.py index cdfad0d6d..79b917c83 100644 --- a/src/eduid/webapp/reset_password/tests/test_app.py +++ b/src/eduid/webapp/reset_password/tests/test_app.py @@ -1,9 +1,9 @@ import datetime import json -from collections.abc import Mapping +from collections.abc import Callable, Iterable, Mapping from datetime import timedelta from typing import Any -from unittest.mock import Mock, patch +from unittest.mock import MagicMock, Mock, patch from urllib.parse import quote_plus from flask import url_for @@ -17,6 +17,7 @@ from eduid.userdb.fixtures.fido_credentials import webauthn_credential as sample_credential from eduid.userdb.fixtures.users import UserFixtures from eduid.userdb.reset_password import ResetPasswordEmailAndPhoneState, ResetPasswordEmailState +from eduid.userdb.testing import SetupConfig from eduid.webapp.common.api.testing import EduidAPITestCase from eduid.webapp.common.api.utils import get_zxcvbn_terms, hash_password from eduid.webapp.common.authn.testing import MockVCCSClient @@ -40,8 +41,8 @@ class ResetPasswordTests(EduidAPITestCase[ResetPasswordApp]): """Base TestCase for those tests that need a full environment setup""" - def setUp(self, *args: Any, **kwargs: Any) -> None: - super().setUp(*args, **kwargs) + def setUp(self, config: SetupConfig | None = None) -> None: + super().setUp(config=config) self.other_test_user = UserFixtures().mocked_user_standard_2 def load_app(self, config: Mapping[str, Any] | None) -> ResetPasswordApp: @@ -124,8 +125,8 @@ def _post_reset_code( @patch("eduid.common.rpc.am_relay.AmRelay.request_user_sync") def _post_reset_password( self, - mock_request_user_sync: Any, - mock_get_vccs_client: Any, + mock_request_user_sync: MagicMock, + mock_get_vccs_client: MagicMock, data1: dict[str, Any] | None = None, data2: dict[str, Any] | None = None, ) -> TestResponse: @@ -177,10 +178,10 @@ def _post_reset_password( @patch("eduid.common.rpc.msg_relay.MsgRelay.sendsms") def _post_choose_extra_sec( self, - mock_sendsms: Any, - mock_request_user_sync: Any, - mock_get_vccs_client: Any, - sendsms_side_effect: Any = None, + mock_sendsms: MagicMock, + mock_request_user_sync: MagicMock, + mock_get_vccs_client: MagicMock, + sendsms_side_effect: Callable | Exception | Iterable | None = None, data1: dict[str, Any] | None = None, data2: dict[str, Any] | None = None, data3: dict[str, Any] | None = None, @@ -241,9 +242,9 @@ def _post_choose_extra_sec( @patch("eduid.common.rpc.msg_relay.MsgRelay.sendsms") def _post_reset_password_secure_phone( self, - mock_sendsms: Any, - mock_request_user_sync: Any, - mock_get_vccs_client: Any, + mock_sendsms: MagicMock, + mock_request_user_sync: MagicMock, + mock_get_vccs_client: MagicMock, data1: dict[str, Any] | None = None, data2: dict[str, Any] | None = None, ) -> TestResponse: @@ -299,9 +300,9 @@ def _post_reset_password_secure_phone( @patch("fido2.cose.ES256.verify") def _post_reset_password_secure_token( self, - mock_verify: Any, - mock_request_user_sync: Any, - mock_get_vccs_client: Any, + mock_verify: MagicMock, + mock_request_user_sync: MagicMock, + mock_get_vccs_client: MagicMock, data1: dict[str, Any] | None = None, credential_data: dict[str, Any] | None = None, data2: dict[str, Any] | None = None, @@ -369,8 +370,8 @@ def _post_reset_password_secure_token( @patch("eduid.common.rpc.am_relay.AmRelay.request_user_sync") def _post_reset_password_secure_external_mfa( self, - mock_request_user_sync: Any, - mock_get_vccs_client: Any, + mock_request_user_sync: MagicMock, + mock_get_vccs_client: MagicMock, data1: dict[str, Any] | None = None, data2: dict[str, Any] | None = None, external_mfa_state: dict[str, Any] | None = None, @@ -449,10 +450,10 @@ def _get_email_code_backdoor( @patch("eduid.common.rpc.msg_relay.MsgRelay.sendsms") def _get_phone_code_backdoor( self, - mock_sendsms: Any, - mock_request_user_sync: Any, - mock_get_vccs_client: Any, - sendsms_side_effect: Any = None, + mock_sendsms: MagicMock, + mock_request_user_sync: MagicMock, + mock_get_vccs_client: MagicMock, + sendsms_side_effect: Callable | Exception | Iterable | None = None, magic_cookie_name: str | None = None, ) -> TestResponse: """ @@ -862,7 +863,7 @@ def test_post_reset_password_secure_phone(self) -> None: self.assertEqual(len(verified_identities), 3) @patch("eduid.webapp.reset_password.views.reset_password.verify_phone_number") - def test_post_reset_password_secure_phone_verify_fail(self, mock_verify: Any) -> None: + def test_post_reset_password_secure_phone_verify_fail(self, mock_verify: MagicMock) -> None: mock_verify.return_value = False response = self._post_reset_password_secure_phone() self._check_error_response( diff --git a/src/eduid/webapp/security/tests/test_app.py b/src/eduid/webapp/security/tests/test_app.py index e881c2e15..3a53bd27e 100644 --- a/src/eduid/webapp/security/tests/test_app.py +++ b/src/eduid/webapp/security/tests/test_app.py @@ -7,7 +7,7 @@ from werkzeug.test import TestResponse from eduid.common.config.base import FrontendAction -from eduid.common.rpc.msg_relay import DeregisteredCauseCode, DeregistrationInformation, OfficialAddress +from eduid.common.rpc.msg_relay import DeregisteredCauseCode, DeregistrationInformation, NavetData, OfficialAddress from eduid.userdb import User from eduid.userdb.element import ElementKey from eduid.userdb.identity import IdentityType @@ -54,8 +54,8 @@ def update_config(self, config: dict[str, Any]) -> dict[str, Any]: @patch("eduid.webapp.security.views.security.revoke_all_credentials") def _delete_account( self, - mock_revoke: Any, - mock_sync: Any, + mock_revoke: MagicMock, + mock_sync: MagicMock, data1: dict[str, Any] | None = None, ) -> TestResponse: """ @@ -78,7 +78,7 @@ def _delete_account( @patch("eduid.common.rpc.am_relay.AmRelay.request_user_sync") def _remove_nin( - self, mock_request_user_sync: Any, data1: dict[str, Any] | None = None, unverify: bool = False + self, mock_request_user_sync: MagicMock, data1: dict[str, Any] | None = None, unverify: bool = False ) -> TestResponse: """ Send a POST request to remove a NIN from the test user, possibly @@ -108,7 +108,7 @@ def _remove_nin( return client.post("/remove-nin", data=json.dumps(data), content_type=self.content_type_json) @patch("eduid.common.rpc.am_relay.AmRelay.request_user_sync") - def _remove_identity(self, mock_request_user_sync: Any, data1: dict[str, Any] | None = None) -> TestResponse: + def _remove_identity(self, mock_request_user_sync: MagicMock, data1: dict[str, Any] | None = None) -> TestResponse: """ Send a POST request to remove all identities from the test user @@ -132,7 +132,7 @@ def _remove_identity(self, mock_request_user_sync: Any, data1: dict[str, Any] | @patch("eduid.common.rpc.am_relay.AmRelay.request_user_sync") def _add_nin( self, - mock_request_user_sync: Any, + mock_request_user_sync: MagicMock, data1: dict[str, Any] | None = None, remove: bool = True, unverify: bool = False, @@ -172,10 +172,10 @@ def _add_nin( @patch("eduid.common.rpc.am_relay.AmRelay.request_user_sync") def _refresh_user_data( self, - mock_request_user_sync: Any, - mock_get_all_navet_data: Any, + mock_request_user_sync: MagicMock, + mock_get_all_navet_data: MagicMock, user: User, - navet_return_value: Any | None = None, + navet_return_value: NavetData | None = None, ) -> TestResponse: mock_request_user_sync.side_effect = self.request_user_sync if navet_return_value is None: @@ -307,7 +307,7 @@ def test_remove_not_existing_nin(self) -> None: assert user.identities.nin.is_verified is True @patch("eduid.webapp.security.views.security.remove_nin_from_user") - def test_remove_nin_am_fail(self, mock_remove: Any) -> None: + def test_remove_nin_am_fail(self, mock_remove: MagicMock) -> None: from eduid.common.rpc.exceptions import AmTaskFailed mock_remove.side_effect = AmTaskFailed() @@ -431,7 +431,7 @@ def test_remove_identity(self) -> None: assert user.identities.nin is None @patch("eduid.webapp.security.views.security.remove_identity_from_user") - def test_remove_identity_am_fail(self, mock_remove: Any) -> None: + def test_remove_identity_am_fail(self, mock_remove: MagicMock) -> None: from eduid.common.rpc.exceptions import AmTaskFailed mock_remove.side_effect = AmTaskFailed() diff --git a/src/eduid/webapp/security/tests/test_change_password.py b/src/eduid/webapp/security/tests/test_change_password.py index 395da41a3..5a453a906 100644 --- a/src/eduid/webapp/security/tests/test_change_password.py +++ b/src/eduid/webapp/security/tests/test_change_password.py @@ -7,6 +7,7 @@ from eduid.common.config.base import FrontendAction from eduid.userdb.credentials import Password +from eduid.userdb.testing import SetupConfig from eduid.webapp.common.api.schemas.authn_status import AuthnActionStatus from eduid.webapp.common.api.testing import EduidAPITestCase from eduid.webapp.common.api.utils import hash_password @@ -17,11 +18,14 @@ class ChangePasswordTests(EduidAPITestCase[SecurityApp]): """Base TestCase for those tests that need a full environment setup""" - def setUp(self, *args: Any, **kwargs: Any) -> None: + def setUp(self, config: SetupConfig | None = None) -> None: self.test_user_eppn = "hubba-bubba" self.test_user_email = "johnsmith@example.com" self.test_user_nin = "197801011235" - super().setUp(*args, **kwargs, copy_user_to_private=True) + if config is None: + config = SetupConfig() + config.copy_user_to_private = True + super().setUp(config=config) def load_app(self, config: Mapping[str, Any]) -> SecurityApp: """ @@ -59,7 +63,7 @@ def _get_suggested(self, reauthn: int | None = 60) -> TestResponse: @patch("eduid.common.rpc.am_relay.AmRelay.request_user_sync") def _change_password( self, - mock_request_user_sync: Any, + mock_request_user_sync: MagicMock, data1: dict[str, Any] | None = None, ) -> TestResponse: """ @@ -84,7 +88,7 @@ def _change_password( @patch("eduid.common.rpc.am_relay.AmRelay.request_user_sync") def _get_suggested_and_change( self, - mock_request_user_sync: Any, + mock_request_user_sync: MagicMock, data1: dict[str, Any] | None = None, correct_old_password: bool = True, ) -> TestResponse: diff --git a/src/eduid/webapp/security/tests/test_webauthn.py b/src/eduid/webapp/security/tests/test_webauthn.py index e09854714..48d31d95c 100644 --- a/src/eduid/webapp/security/tests/test_webauthn.py +++ b/src/eduid/webapp/security/tests/test_webauthn.py @@ -2,7 +2,7 @@ import json from collections.abc import Mapping from typing import Any -from unittest.mock import patch +from unittest.mock import MagicMock, patch from fido2.webauthn import AttestationObject, AuthenticatorAttachment, CollectedClientData from fido_mds import FidoMetadataStore @@ -10,6 +10,7 @@ from eduid.common.config.base import EduidEnvironment, FrontendAction from eduid.userdb.credentials import U2F, FidoCredential, Webauthn +from eduid.userdb.testing import SetupConfig from eduid.webapp.common.api.testing import CSRFTestClient, EduidAPITestCase from eduid.webapp.common.session import EduidSession from eduid.webapp.common.session.namespaces import WebauthnRegistration, WebauthnState @@ -98,8 +99,8 @@ class SecurityWebauthnTests(EduidAPITestCase): app: SecurityApp - def setUp(self) -> None: # type: ignore[override] - super().setUp() + def setUp(self, config: SetupConfig | None = None) -> None: + super().setUp(config=config) # remove all FidoCredentials from the test user user = self.app.central_userdb.get_user_by_eppn(self.test_user_eppn) assert user is not None @@ -249,7 +250,7 @@ def _begin_register_key( @patch("eduid.common.rpc.am_relay.AmRelay.request_user_sync") def _finish_register_key( self, - mock_request_user_sync: Any, + mock_request_user_sync: MagicMock, client_data: bytes, attestation: bytes, state: dict, @@ -310,7 +311,7 @@ def _finish_register_key( @patch("eduid.common.rpc.am_relay.AmRelay.request_user_sync") def _remove( self, - mock_request_user_sync: Any, + mock_request_user_sync: MagicMock, client_data: bytes, attestation: bytes, state: dict, diff --git a/src/eduid/webapp/signup/tests/test_app.py b/src/eduid/webapp/signup/tests/test_app.py index 56905ed12..224b54900 100644 --- a/src/eduid/webapp/signup/tests/test_app.py +++ b/src/eduid/webapp/signup/tests/test_app.py @@ -20,6 +20,7 @@ from eduid.userdb.exceptions import UserOutOfSync from eduid.userdb.signup import Invite, InviteMailAddress, InviteType from eduid.userdb.signup.invite import InvitePhoneNumber, SCIMReference +from eduid.userdb.testing import SetupConfig from eduid.webapp.common.api.exceptions import ProofingLogFailure from eduid.webapp.common.api.messages import CommonMsg, TranslatableMsg from eduid.webapp.common.api.testing import EduidAPITestCase @@ -51,8 +52,11 @@ class SignupResult: class SignupTests(EduidAPITestCase[SignupApp], MockedScimAPIMixin): - def setUp(self, *args: Any, **kwargs: Any) -> None: - super().setUp(*args, **kwargs, copy_user_to_private=True) + def setUp(self, config: SetupConfig | None = None) -> None: + if config is None: + config = SetupConfig() + config.copy_user_to_private = True + super().setUp(config=config) def load_app(self, config: Mapping[str, Any]) -> SignupApp: """ @@ -517,8 +521,8 @@ def _prepare_for_create_user( @patch("eduid.vccs.client.VCCSClient.add_credentials") def _create_user( self, - mock_add_credentials: Any, - mock_request_user_sync: Any, + mock_add_credentials: MagicMock, + mock_request_user_sync: MagicMock, data: dict[str, Any] | None = None, custom_password: str | None = None, expect_success: bool = True, diff --git a/src/eduid/webapp/support/app.py b/src/eduid/webapp/support/app.py index fc8a159b7..30e73b8ce 100644 --- a/src/eduid/webapp/support/app.py +++ b/src/eduid/webapp/support/app.py @@ -46,7 +46,7 @@ def dateformat(value: datetime | None, format: str = "%Y-%m-%d") -> str: return value.strftime(format) @app.template_filter("multisort") - def sort_multi(items: list, *operators: str, **kwargs: bool) -> list: + def sort_multi(items: list, *operators: str, **kwargs: Any) -> list: # Don't try to sort on missing keys keys = list(operators) # operators is immutable for key in operators: diff --git a/src/eduid/webapp/support/tests/test_app.py b/src/eduid/webapp/support/tests/test_app.py index 43996e0d9..9b7c5bda3 100644 --- a/src/eduid/webapp/support/tests/test_app.py +++ b/src/eduid/webapp/support/tests/test_app.py @@ -1,6 +1,7 @@ from collections.abc import Mapping from typing import Any +from eduid.userdb.testing import SetupConfig from eduid.webapp.common.api.testing import CSRFTestClient, EduidAPITestCase from eduid.webapp.support.app import SupportApp, support_init_app @@ -12,8 +13,8 @@ class SupportAppTests(EduidAPITestCase): app: SupportApp - def setUp(self) -> None: # type: ignore[override] - super().setUp() + def setUp(self, config: SetupConfig | None = None) -> None: + super().setUp(config=config) self.test_user_eppn = "hubba-bubba" self.client = self.app.test_client() diff --git a/src/eduid/webapp/svipe_id/helpers.py b/src/eduid/webapp/svipe_id/helpers.py index 58420ae26..4af378441 100644 --- a/src/eduid/webapp/svipe_id/helpers.py +++ b/src/eduid/webapp/svipe_id/helpers.py @@ -1,7 +1,6 @@ import logging from datetime import date from enum import unique -from typing import Any from iso3166 import countries from pydantic import BaseModel, ConfigDict, Field, field_validator @@ -34,13 +33,14 @@ class SvipeIDMsg(TranslatableMsg): class SessionOAuthCache: + # Used to store json-encoded data (OAuth->BaseOAuth->FrameworkIntegration) @staticmethod - def get(key: str) -> Any: + def get(key: str) -> str | None: logger.debug(f"Getting {key} from session.svipe_id.oauth_cache") return session.svipe_id.rp.authlib_cache.get(key) @staticmethod - def set(key: str, value: Any, expires: int | None = None) -> None: + def set(key: str, value: str, expires: int | None = None) -> None: session.svipe_id.rp.authlib_cache[key] = value logger.debug(f"Set {key}={value} (expires={expires}) in session.svipe_id.oauth_cache") @@ -83,8 +83,8 @@ class SvipeDocumentUserInfo(UserInfoBase): @field_validator("document_nationality") @classmethod - def iso_3166_1_alpha_3_to_alpha2(cls, v: Any) -> str: - # translate ISO 3166-1 alpha-3 to alpha-2 to match the format used in eduid-userdb + def iso_3166_1_alpha_3_to_alpha2(cls, v: str | int) -> str: + # translate ISO 3166-1 alpha-3 to alpha-2 to match the format used §in eduid-userdb try: country = countries.get(v) except KeyError: diff --git a/src/eduid/webapp/svipe_id/tests/test_app.py b/src/eduid/webapp/svipe_id/tests/test_app.py index 6812109e8..3f18ee9a2 100644 --- a/src/eduid/webapp/svipe_id/tests/test_app.py +++ b/src/eduid/webapp/svipe_id/tests/test_app.py @@ -12,6 +12,7 @@ from eduid.common.misc.timeutil import utc_now from eduid.userdb import SvipeIdentity from eduid.userdb.identity import IdentityProofingMethod +from eduid.userdb.testing import SetupConfig from eduid.webapp.common.api.messages import CommonMsg from eduid.webapp.common.proofing.messages import ProofingMsg from eduid.webapp.common.proofing.testing import ProofingTests @@ -25,8 +26,11 @@ class SvipeIdTests(ProofingTests[SvipeIdApp]): """Base TestCase for those tests that need a full environment setup""" - def setUp(self, *args: Any, **kwargs: Any) -> None: - super().setUp(*args, **kwargs, users=["hubba-bubba", "hubba-baar"]) + def setUp(self, config: SetupConfig | None = None) -> None: + if config is None: + config = SetupConfig() + config.users = ["hubba-bubba", "hubba-baar"] + super().setUp(config=config) self.unverified_test_user = self.app.central_userdb.get_user_by_eppn("hubba-baar") self._user_setup() diff --git a/src/eduid/workers/am/testing.py b/src/eduid/workers/am/testing.py index b51466391..8a7b8a2a4 100644 --- a/src/eduid/workers/am/testing.py +++ b/src/eduid/workers/am/testing.py @@ -22,7 +22,7 @@ from eduid.userdb.exceptions import UserDoesNotExist from eduid.userdb.identity import IdentityType from eduid.userdb.proofing import ProofingUser -from eduid.userdb.testing import MongoTemporaryInstance +from eduid.userdb.testing import MongoTemporaryInstance, SetupConfig from eduid.userdb.userdb import UserDB from eduid.workers.am.ams import AttributeFetcher from eduid.workers.am.common import AmCelerySingleton @@ -106,13 +106,11 @@ class WorkerTestCase(CommonTestCase): Base Test case for eduID celery workers """ - def setUp( - self, *args: Any, am_settings: dict[str, Any] | None = None, want_mongo_uri: bool = True, **kwargs: Any - ) -> None: + def setUp(self, config: SetupConfig | None = None) -> None: """ set up tests """ - super().setUp(*args, **kwargs) + super().setUp(config=config) settings: dict[str, Any] = { "app_name": "testing", @@ -129,9 +127,11 @@ def setUp( "mongo_uri": None, } - if am_settings: - settings.update(am_settings) - if want_mongo_uri: + if config is None: + config = SetupConfig() + if config.am_settings: + settings.update(config.am_settings) + if config.want_mongo_uri: assert isinstance(self.tmp_db, MongoTemporaryInstance) # please mypy settings["mongo_uri"] = self.tmp_db.uri @@ -155,8 +155,8 @@ class ProofingTestCase(AMTestCase): fetcher_name: str | None = None fetcher: AttributeFetcher | None = None - def setUp(self, *args: Any, **kwargs: Any) -> None: - super().setUp(*args, **kwargs) + def setUp(self, config: SetupConfig | None = None) -> None: + super().setUp(config=config) if self.fetcher_name: self.fetcher = AmCelerySingleton.af_registry.get_fetcher(self.fetcher_name) diff --git a/src/eduid/workers/am/tests/test_am.py b/src/eduid/workers/am/tests/test_am.py index 9cecb3e5c..74cc713bd 100644 --- a/src/eduid/workers/am/tests/test_am.py +++ b/src/eduid/workers/am/tests/test_am.py @@ -6,6 +6,7 @@ from eduid.common.config.workers import AmConfig from eduid.userdb.db import TUserDbDocument from eduid.userdb.exceptions import UserDoesNotExist +from eduid.userdb.testing import SetupConfig from eduid.userdb.user import User from eduid.workers.am.ams.common import AttributeFetcher from eduid.workers.am.common import AmCelerySingleton @@ -84,8 +85,11 @@ class MessageTest(AMTestCase): transforms 'uid' to its urn:oid representation. """ - def setUp(self, *args: Any, **kwargs: Any) -> None: - super().setUp(*args, want_mongo_uri=True, **kwargs) + def setUp(self, config: SetupConfig | None = None) -> None: + if config is None: + config = SetupConfig() + config.want_mongo_uri = True + super().setUp(config=config) self.private_db = AmTestUserDb(db_uri=self.tmp_db.uri, db_name="eduid_am_test") # register fake AMP plugin named 'test' AmConfig(app_name="message_test", mongo_uri=self.tmp_db.uri) diff --git a/src/eduid/workers/am/tests/test_index.py b/src/eduid/workers/am/tests/test_index.py index acdab9231..ebd0ed936 100644 --- a/src/eduid/workers/am/tests/test_index.py +++ b/src/eduid/workers/am/tests/test_index.py @@ -3,6 +3,7 @@ import unittest from eduid.userdb import UserDB +from eduid.userdb.testing import SetupConfig from eduid.workers.am.testing import AMTestCase # TODO: tbd: fix or remove as it is not working yet @@ -10,8 +11,8 @@ @unittest.skip("Not working yet") class TestIndexes(AMTestCase): - def setUp(self) -> None: # type: ignore[override] - super().setUp() + def setUp(self, config: SetupConfig | None = None) -> None: + super().setUp(config=config) def test_index_setup(self) -> None: indexes = { diff --git a/src/eduid/workers/am/tests/test_signup.py b/src/eduid/workers/am/tests/test_signup.py index a21cdabc3..c5347cf2d 100644 --- a/src/eduid/workers/am/tests/test_signup.py +++ b/src/eduid/workers/am/tests/test_signup.py @@ -7,6 +7,7 @@ from eduid.userdb.exceptions import UserDoesNotExist from eduid.userdb.fixtures.users import UserFixtures from eduid.userdb.signup import SignupUser +from eduid.userdb.testing import SetupConfig from eduid.userdb.user import User from eduid.workers.am.common import AmCelerySingleton from eduid.workers.am.testing import USER_DATA, AMTestCase @@ -15,10 +16,14 @@ class AttributeFetcherTests(AMTestCase): user: User - def setUp(self) -> None: # type: ignore[override] + def setUp(self, config: SetupConfig | None = None) -> None: am_settings = {"new_user_date": "2001-01-01"} self.user = UserFixtures().mocked_user_standard - super().setUp(am_settings=am_settings, am_users=[self.user]) + if config is None: + config = SetupConfig() + config.am_users = [self.user] + config.am_settings = am_settings + super().setUp(config=config) self.fetcher = AmCelerySingleton.af_registry.get_fetcher("eduid_signup") diff --git a/src/eduid/workers/am/tests/test_tasks.py b/src/eduid/workers/am/tests/test_tasks.py index 797145f6b..b889caa38 100644 --- a/src/eduid/workers/am/tests/test_tasks.py +++ b/src/eduid/workers/am/tests/test_tasks.py @@ -1,5 +1,3 @@ -from typing import Any - from bson import ObjectId import eduid.userdb @@ -7,6 +5,7 @@ from eduid.userdb.exceptions import EduIDUserDBError, MultipleUsersReturned from eduid.userdb.fixtures.users import UserFixtures from eduid.userdb.identity import IdentityType +from eduid.userdb.testing import SetupConfig from eduid.userdb.user import User from eduid.workers.am.consistency_checks import check_locked_identity, unverify_duplicates from eduid.workers.am.testing import AMTestCase @@ -15,10 +14,15 @@ class TestTasks(AMTestCase): user: User - def setUp(self, *args: Any, **kwargs: Any) -> None: + def setUp(self, config: SetupConfig | None = None) -> None: _users = UserFixtures() self.user = _users.mocked_user_standard - super().setUp(want_mongo_uri=True, am_users=[self.user, _users.mocked_user_standard_2], **kwargs) + _am_users = [self.user, _users.mocked_user_standard_2] + if config is None: + config = SetupConfig() + config.want_mongo_uri = True + config.am_users = _am_users + super().setUp(config=config) def test_get_user_by_id(self) -> None: user = self.amdb.get_user_by_id(self.user.user_id) diff --git a/src/eduid/workers/amapi/context_request.py b/src/eduid/workers/amapi/context_request.py index 0426d62bc..ebd8d7dd1 100644 --- a/src/eduid/workers/amapi/context_request.py +++ b/src/eduid/workers/amapi/context_request.py @@ -4,8 +4,10 @@ from dataclasses import asdict, dataclass from typing import Any -from fastapi import Request, Response from fastapi.routing import APIRoute +from starlette.requests import Request, empty_receive, empty_send +from starlette.responses import Response +from starlette.types import Receive, Scope, Send @dataclass @@ -15,8 +17,8 @@ def to_dict(self) -> dict[str, Any]: class ContextRequest(Request): - def __init__(self, *args: Any, **kwargs: Any) -> None: - super().__init__(*args, **kwargs) + def __init__(self, scope: Scope, receive: Receive = empty_receive, send: Send = empty_send) -> None: + super().__init__(scope=scope, receive=receive, send=send) @property def context(self) -> Context: diff --git a/src/eduid/workers/amapi/testing.py b/src/eduid/workers/amapi/testing.py index de5c65653..b98191123 100644 --- a/src/eduid/workers/amapi/testing.py +++ b/src/eduid/workers/amapi/testing.py @@ -6,13 +6,14 @@ from eduid.common.models.amapi_user import Reason, Source from eduid.common.testing_base import CommonTestCase +from eduid.userdb.testing import SetupConfig from eduid.workers.amapi.app import init_api from eduid.workers.amapi.config import EndpointRestriction class TestAMBase(CommonTestCase): - def setUp(self, *args: Any, **kwargs: Any) -> None: - super().setUp(*args, **kwargs) + def setUp(self, config: SetupConfig | None = None) -> None: + super().setUp(config=config) self.path = pkg_resources.resource_filename(__name__, "tests/data") self.test_config = self._get_config() diff --git a/src/eduid/workers/amapi/tests/test_middleware.py b/src/eduid/workers/amapi/tests/test_middleware.py index 1f90954ca..3f741c3f3 100644 --- a/src/eduid/workers/amapi/tests/test_middleware.py +++ b/src/eduid/workers/amapi/tests/test_middleware.py @@ -1,13 +1,12 @@ import fnmatch import unittest -from typing import Any from eduid.workers.amapi.config import EndpointRestriction, SupportedMethod from eduid.workers.amapi.middleware import AuthenticationMiddleware class TestMiddleware(unittest.TestCase): - def setUp(self, *args: Any, **kwargs: Any) -> None: + def setUp(self) -> None: super().setUp() self.middleware = AuthenticationMiddleware diff --git a/src/eduid/workers/amapi/tests/test_status.py b/src/eduid/workers/amapi/tests/test_status.py index 2e76c3848..9057e49c0 100644 --- a/src/eduid/workers/amapi/tests/test_status.py +++ b/src/eduid/workers/amapi/tests/test_status.py @@ -1,9 +1,10 @@ +from eduid.userdb.testing import SetupConfig from eduid.workers.amapi.testing import TestAMBase class TestStatus(TestAMBase): - def setUp(self) -> None: # type: ignore[override] - super().setUp() + def setUp(self, config: SetupConfig | None = None) -> None: + super().setUp(config=config) def test_status_healthy_ok(self) -> None: response = self.client.get(url="/status/healthy") diff --git a/src/eduid/workers/amapi/tests/test_user.py b/src/eduid/workers/amapi/tests/test_user.py index 6f9c38c00..36cdb821f 100644 --- a/src/eduid/workers/amapi/tests/test_user.py +++ b/src/eduid/workers/amapi/tests/test_user.py @@ -11,13 +11,18 @@ from eduid.common.clients.gnap_client.base import GNAPBearerTokenMixin from eduid.userdb.fixtures.users import UserFixtures from eduid.userdb.meta import CleanerType +from eduid.userdb.testing import SetupConfig from eduid.workers.amapi.testing import TestAMBase from eduid.workers.amapi.utils import AuthnBearerToken class TestUsers(TestAMBase, GNAPBearerTokenMixin): - def setUp(self, *args: Any, **kwargs: Any) -> None: - super().setUp(am_users=[UserFixtures().new_user_example]) + def setUp(self, config: SetupConfig | None = None) -> None: + _am_users = [UserFixtures().new_user_example] + if config is None: + config = SetupConfig() + config.am_users = _am_users + super().setUp(config=config) def _make_url(self, endpoint: str | None = None) -> str: if endpoint is None: diff --git a/src/eduid/workers/job_runner/config.py b/src/eduid/workers/job_runner/config.py index fd31142ea..2c99052f4 100644 --- a/src/eduid/workers/job_runner/config.py +++ b/src/eduid/workers/job_runner/config.py @@ -34,7 +34,7 @@ class JobCronConfig(BaseModel): @model_validator(mode="before") @classmethod - def at_least_one_datetime_value(cls, data: Any) -> Any: + def at_least_one_datetime_value(cls, data: dict[str, Any]) -> dict[str, Any]: if isinstance(data, dict): need_one_of = ["year", "month", "day", "week", "day_of_week", "hour", "minute", "second"] assert len(data.keys() & need_one_of), f"At least one of {need_one_of} must be set" diff --git a/src/eduid/workers/lookup_mobile/client/mobile_lookup_client.py b/src/eduid/workers/lookup_mobile/client/mobile_lookup_client.py index ceb2c0f56..39e10d61a 100644 --- a/src/eduid/workers/lookup_mobile/client/mobile_lookup_client.py +++ b/src/eduid/workers/lookup_mobile/client/mobile_lookup_client.py @@ -1,5 +1,4 @@ from logging import Logger -from typing import Any from suds.client import Client from suds.sudsobject import Object @@ -28,7 +27,7 @@ def client(self) -> Client: self._client = Client(self.conf.teleadress_client_url, port=self.conf.teleadress_client_port) return self._client - def _get_find_person(self) -> Any: + def _get_find_person(self) -> Object: find_person = self.client.factory.create("ns7:FindPersonClass") find_person.QueryParams = self.client.factory.create("ns7:QueryParamsClass") find_person.QueryColumns = self.client.factory.create("ns7:QueryColumnsClass") @@ -59,7 +58,7 @@ def find_NIN_by_mobile(self, mobile_number: str) -> str | None: return format_NIN(nin) - def _search(self, param: Any | Object) -> list | None: + def _search(self, param: Object) -> list | None: # Start the search # TODO: remove self.conf.devel_mode, use environment instead if self.conf.testing or self.conf.environment == EduidEnvironment.dev: diff --git a/src/eduid/workers/lookup_mobile/decorators.py b/src/eduid/workers/lookup_mobile/decorators.py index c5666fcca..cc9f06227 100644 --- a/src/eduid/workers/lookup_mobile/decorators.py +++ b/src/eduid/workers/lookup_mobile/decorators.py @@ -29,7 +29,7 @@ def __call__(self, f: Callable[..., Any]) -> Callable[..., Any]: if not self.enabled: return f - def audit(*args: Any, **kwargs: Any) -> Any: + def audit(*args: Any, **kwargs: Any) -> Any: # noqa: ANN401 ret = f(*args, **kwargs) # XXX Ugly hack # The class that uses the decorator needs to have self.conf['MONGO_URI'] and self.transaction_audit set @@ -63,7 +63,7 @@ def enable(cls) -> None: def disable(cls) -> None: cls.enabled = False - def _filter(self, func: str, data: Any, *args: Any, **kwargs: Any) -> Any: + def _filter(self, func: str, data: Any, *args: Any, **kwargs: Any) -> Any: # noqa: ANN401 if data is False: return data if func == "find_mobiles_by_NIN": diff --git a/src/eduid/workers/lookup_mobile/development/development_search_result.py b/src/eduid/workers/lookup_mobile/development/development_search_result.py index cb973d466..39eb3799d 100644 --- a/src/eduid/workers/lookup_mobile/development/development_search_result.py +++ b/src/eduid/workers/lookup_mobile/development/development_search_result.py @@ -1,5 +1,4 @@ __author__ = "mathiashedstrom" -from typing import Any from suds.sudsobject import Object @@ -27,7 +26,7 @@ def __init__(self) -> None: self._error_text = "" -def _get_devel_search_result(search_param: Any | Object) -> DevelopResult: +def _get_devel_search_result(search_param: Object) -> DevelopResult: nin = search_param.QueryParams.FindSSNo mobile = search_param.QueryParams.FindTelephone diff --git a/src/eduid/workers/lookup_mobile/test/test_decorators.py b/src/eduid/workers/lookup_mobile/test/test_decorators.py index 06f674395..2b89799d0 100644 --- a/src/eduid/workers/lookup_mobile/test/test_decorators.py +++ b/src/eduid/workers/lookup_mobile/test/test_decorators.py @@ -1,15 +1,15 @@ __author__ = "lundberg" -from typing import Any from eduid.common.config.workers import MsgConfig +from eduid.userdb.testing import SetupConfig from eduid.workers.lookup_mobile.decorators import TransactionAudit from eduid.workers.lookup_mobile.testing import LookupMobileMongoTestCase class TestTransactionAudit(LookupMobileMongoTestCase): - def setUp(self) -> None: # type: ignore[override] - super().setUp() + def setUp(self, config: SetupConfig | None = None) -> None: + super().setUp(config=config) # need to set self.mongo_uri and db for the TransactionAudit decorator self.conf = MsgConfig(app_name="testing", mongo_uri=self.tmp_db.uri) self.db = self.tmp_db.conn["eduid_lookup_mobile"] @@ -19,7 +19,7 @@ def setUp(self) -> None: # type: ignore[override] def test_successfull_transaction_audit(self) -> None: @TransactionAudit() def find_mobiles_by_NIN( - self: Any, national_identity_number: str, number_region: str | None = None + self: TestTransactionAudit, national_identity_number: str, number_region: str | None = None ) -> list[str]: return ["list", "of", "mobile_numbers"] @@ -33,7 +33,7 @@ def find_mobiles_by_NIN( c.delete_many({}) # Clear database @TransactionAudit() - def find_NIN_by_mobile(self: Any, mobile_number: str) -> str: + def find_NIN_by_mobile(self: TestTransactionAudit, mobile_number: str) -> str: return "200202025678" find_NIN_by_mobile(self, "+46701740699") @@ -47,7 +47,9 @@ def find_NIN_by_mobile(self: Any, mobile_number: str) -> str: def test_failed_transaction_audit(self) -> None: @TransactionAudit() - def find_mobiles_by_NIN(self: Any, national_identity_number: str, number_region: str | None = None) -> list: + def find_mobiles_by_NIN( + self: TestTransactionAudit, national_identity_number: str, number_region: str | None = None + ) -> list: return [] find_mobiles_by_NIN(self, "200202025678") @@ -58,7 +60,7 @@ def find_mobiles_by_NIN(self: Any, national_identity_number: str, number_region: c.delete_many({}) # Clear database @TransactionAudit() - def find_NIN_by_mobile(self: Any, mobile_number: str) -> None: + def find_NIN_by_mobile(self: TestTransactionAudit, mobile_number: str) -> None: return find_NIN_by_mobile(self, "+46701740699") @@ -74,7 +76,7 @@ def test_transaction_audit_toggle(self) -> None: TransactionAudit.disable() @TransactionAudit() - def no_name(self: Any) -> dict[str, str]: + def no_name(self: TestTransactionAudit) -> dict[str, str]: return {"baka": "kaka"} no_name(self) @@ -85,7 +87,7 @@ def no_name(self: Any) -> dict[str, str]: TransactionAudit.enable() @TransactionAudit() - def no_name2(self: Any) -> dict[str, str]: + def no_name2(self: TestTransactionAudit) -> dict[str, str]: return {"baka": "kaka"} no_name2(self) diff --git a/src/eduid/workers/lookup_mobile/testing.py b/src/eduid/workers/lookup_mobile/testing.py index fd266bebd..b6d31f348 100644 --- a/src/eduid/workers/lookup_mobile/testing.py +++ b/src/eduid/workers/lookup_mobile/testing.py @@ -1,11 +1,9 @@ import logging -from typing import Any from eduid.common.config.base import CeleryConfigMixin, EduIDBaseAppConfig from eduid.common.config.workers import MobConfig from eduid.common.rpc.lookup_mobile_relay import LookupMobileRelay -from eduid.userdb.testing import MongoTestCase -from eduid.userdb.user import User +from eduid.userdb.testing import MongoTestCase, SetupConfig from eduid.workers.lookup_mobile.common import MobCelerySingleton logger = logging.getLogger(__name__) @@ -16,9 +14,11 @@ class MobTestConfig(EduIDBaseAppConfig, CeleryConfigMixin): class LookupMobileMongoTestCase(MongoTestCase): - def setUp(self, am_users: list[User] | None = None, init_lookup_mobile: bool = True) -> Any: - super().setUp(am_users=am_users) - if init_lookup_mobile: + def setUp(self, config: SetupConfig | None = None) -> None: + super().setUp(config=config) + if config is None: + config = SetupConfig() + if config.init_lookup_mobile: settings = { "app_name": "testing", "celery": { diff --git a/src/eduid/workers/msg/decorators.py b/src/eduid/workers/msg/decorators.py index eb2a29c31..27c9b9e8b 100644 --- a/src/eduid/workers/msg/decorators.py +++ b/src/eduid/workers/msg/decorators.py @@ -23,7 +23,7 @@ def __call__(self, f: Callable[..., Any]) -> Callable[..., Any]: if not self.enabled: return f - def audit(*args: Any, **kwargs: Any) -> Any: + def audit(*args: Any, **kwargs: Any) -> Any: # noqa: ANN401 ret = f(*args, **kwargs) if not isclass(ret): # we can't save class objects in mongodb date = datetime.utcnow() @@ -57,7 +57,7 @@ def disable(cls) -> None: cls.enabled = False @staticmethod - def _filter(func: str, data: Any, *args: Any, **kwargs: Any) -> Any: + def _filter(func: str, data: Any, *args: Any, **kwargs: Any) -> Any: # noqa: ANN401 if data is False: return data if func == "_get_navet_data": diff --git a/src/eduid/workers/msg/testing.py b/src/eduid/workers/msg/testing.py index d798ec27a..1b8efc746 100644 --- a/src/eduid/workers/msg/testing.py +++ b/src/eduid/workers/msg/testing.py @@ -1,13 +1,11 @@ import logging from pathlib import PurePath -from typing import Any from eduid.common.config.base import EduIDBaseAppConfig, MailConfigMixin, MsgConfigMixin from eduid.common.config.workers import MsgConfig from eduid.common.rpc.mail_relay import MailRelay from eduid.common.rpc.msg_relay import MsgRelay -from eduid.userdb.testing import MongoTestCase -from eduid.userdb.user import User +from eduid.userdb.testing import MongoTestCase, SetupConfig from eduid.workers.msg.common import MsgCelerySingleton logger = logging.getLogger(__name__) @@ -22,10 +20,12 @@ class MailTestConfig(EduIDBaseAppConfig, MailConfigMixin): class MsgMongoTestCase(MongoTestCase): - def setUp(self, am_users: list[User] | None = None, init_msg: bool = True) -> Any: - super().setUp() + def setUp(self, config: SetupConfig | None = None) -> None: + super().setUp(config=config) data_path = PurePath(__file__).with_name("tests") / "data" - if init_msg: + if config is None: + config = SetupConfig() + if config.init_msg: settings = { "app_name": "testing", "celery": { diff --git a/src/eduid/workers/msg/tests/test_decorators.py b/src/eduid/workers/msg/tests/test_decorators.py index 19045117a..aab703ae9 100644 --- a/src/eduid/workers/msg/tests/test_decorators.py +++ b/src/eduid/workers/msg/tests/test_decorators.py @@ -1,13 +1,11 @@ -from typing import Any - -from eduid.userdb.user import User +from eduid.userdb.testing import SetupConfig from eduid.workers.msg.decorators import TransactionAudit from eduid.workers.msg.testing import MsgMongoTestCase class TestTransactionAudit(MsgMongoTestCase): - def setUp(self, am_users: list[User] | None = None, init_msg: bool = True) -> None: - super().setUp(init_msg=init_msg) + def setUp(self, config: SetupConfig | None = None) -> None: + super().setUp(config=config) assert self.msg_settings.mongo_uri TransactionAudit.enable(self.msg_settings.mongo_uri, db_name="test") @@ -38,7 +36,7 @@ def _get_navet_data(arg1: str, arg2: str) -> set[str]: @TransactionAudit() def send_message( - _self: Any, + _self: str, message_type: str, reference: str, message_dict: str, diff --git a/src/eduid/workers/msg/tests/test_mongo.py b/src/eduid/workers/msg/tests/test_mongo.py index e65291b69..165c2a579 100644 --- a/src/eduid/workers/msg/tests/test_mongo.py +++ b/src/eduid/workers/msg/tests/test_mongo.py @@ -1,10 +1,11 @@ from eduid.userdb.db.base import TUserDbDocument +from eduid.userdb.testing import SetupConfig from eduid.workers.msg.testing import MsgMongoTestCase class MessageTest(MsgMongoTestCase): - def setUp(self) -> None: # type: ignore[override] - super().setUp() + def setUp(self, config: SetupConfig | None = None) -> None: + super().setUp(config=config) def test_mongo(self) -> None: db = self.tmp_db.conn["test"] diff --git a/src/eduid/workers/msg/tests/test_tasks.py b/src/eduid/workers/msg/tests/test_tasks.py index 6f9368ebf..ac8db77f3 100644 --- a/src/eduid/workers/msg/tests/test_tasks.py +++ b/src/eduid/workers/msg/tests/test_tasks.py @@ -3,6 +3,7 @@ import pytest from celery.exceptions import Retry +from eduid.userdb.testing import SetupConfig from eduid.workers.msg.testing import MsgMongoTestCase @@ -11,8 +12,8 @@ class MockException(Exception): class TestTasks(MsgMongoTestCase): - def setUp(self, init_msg: bool = True) -> None: # type: ignore[override] - super().setUp(init_msg=init_msg) + def setUp(self, config: SetupConfig | None = None) -> None: + super().setUp(config=config) @patch("smscom.SMSClient.send") def test_send_message_sms(self, sms_mock: MagicMock) -> None: