Compare commits

..

1 Commits

Author SHA1 Message Date
Trenton H
f79a0ae899 Updates the baselines for the latest code changes 2026-03-03 10:28:12 -08:00
6 changed files with 3448 additions and 3471 deletions

View File

@@ -264,6 +264,7 @@ src/documents/management/commands/document_exporter.py:0: error: Function is mis
src/documents/management/commands/document_exporter.py:0: error: Incompatible types in assignment (expression has type "str", variable has type "Path") [assignment]
src/documents/management/commands/document_exporter.py:0: error: Invalid index type "str" for "str"; expected type "SupportsIndex | slice[Any, Any, Any]" [index]
src/documents/management/commands/document_exporter.py:0: error: Invalid index type "str" for "str"; expected type "SupportsIndex | slice[Any, Any, Any]" [index]
src/documents/management/commands/document_exporter.py:0: error: Library stubs not installed for "tqdm" [import-untyped]
src/documents/management/commands/document_exporter.py:0: error: Missing type parameters for generic type "QuerySet" [type-arg]
src/documents/management/commands/document_exporter.py:0: error: Missing type parameters for generic type "dict" [type-arg]
src/documents/management/commands/document_exporter.py:0: error: Missing type parameters for generic type "dict" [type-arg]
@@ -282,23 +283,18 @@ src/documents/management/commands/document_importer.py:0: error: Function is mis
src/documents/management/commands/document_importer.py:0: error: Incompatible types in assignment (expression has type "str | None", base class "CryptMixin" defined the type as "str") [assignment]
src/documents/management/commands/document_importer.py:0: error: Invalid index type "str" for "str"; expected type "SupportsIndex | slice[Any, Any, Any]" [index]
src/documents/management/commands/document_importer.py:0: error: Invalid index type "str" for "str"; expected type "SupportsIndex | slice[Any, Any, Any]" [index]
src/documents/management/commands/document_importer.py:0: error: Library stubs not installed for "tqdm" [import-untyped]
src/documents/management/commands/document_importer.py:0: error: Missing type parameters for generic type "Generator" [type-arg]
src/documents/management/commands/document_importer.py:0: error: Missing type parameters for generic type "dict" [type-arg]
src/documents/management/commands/document_importer.py:0: error: Need type annotation for "manifest_paths" (hint: "manifest_paths: list[<type>] = ...") [var-annotated]
src/documents/management/commands/document_importer.py:0: error: Skipping analyzing "auditlog.registry": module is installed, but missing library stubs or py.typed marker [import-untyped]
src/documents/management/commands/document_index.py:0: error: Function is missing a type annotation [no-untyped-def]
src/documents/management/commands/document_index.py:0: error: Function is missing a type annotation [no-untyped-def]
src/documents/management/commands/document_llmindex.py:0: error: Function is missing a type annotation [no-untyped-def]
src/documents/management/commands/document_llmindex.py:0: error: Function is missing a type annotation [no-untyped-def]
src/documents/management/commands/document_renamer.py:0: error: Function is missing a type annotation [no-untyped-def]
src/documents/management/commands/document_renamer.py:0: error: Function is missing a type annotation [no-untyped-def]
src/documents/management/commands/document_retagger.py:0: error: Function is missing a type annotation [no-untyped-def]
src/documents/management/commands/document_retagger.py:0: error: Function is missing a type annotation [no-untyped-def]
src/documents/management/commands/document_sanity_checker.py:0: error: Function is missing a type annotation [no-untyped-def]
src/documents/management/commands/document_sanity_checker.py:0: error: Function is missing a type annotation [no-untyped-def]
src/documents/management/commands/document_retagger.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/management/commands/document_retagger.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/management/commands/document_thumbnails.py:0: error: Function is missing a type annotation [no-untyped-def]
src/documents/management/commands/document_thumbnails.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/management/commands/document_thumbnails.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/management/commands/loaddata_stdin.py:0: error: Function is missing a type annotation [no-untyped-def]
src/documents/management/commands/loaddata_stdin.py:0: error: Function is missing a type annotation [no-untyped-def]
src/documents/management/commands/loaddata_stdin.py:0: error: Incompatible types in assignment (expression has type "tuple[Callable[[Any, Any], TextIO | Any], None]", target has type "tuple[Callable[[str, Literal['r', 'rb']], BufferedReader[_BufferedReaderStream]]]") [assignment]
@@ -308,11 +304,8 @@ src/documents/management/commands/mixins.py:0: error: Attribute "kdf_algorithm"
src/documents/management/commands/mixins.py:0: error: Attribute "key_iterations" already defined on line 0 [no-redef]
src/documents/management/commands/mixins.py:0: error: Attribute "key_size" already defined on line 0 [no-redef]
src/documents/management/commands/mixins.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/management/commands/mixins.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/management/commands/mixins.py:0: error: Incompatible types in assignment (expression has type "list[dict[str, Sequence[str]]]", variable has type "CryptFields") [assignment]
src/documents/management/commands/mixins.py:0: error: Missing type parameters for generic type "dict" [type-arg]
src/documents/management/commands/mixins.py:0: error: Unsupported operand types for // ("None" and "int") [operator]
src/documents/management/commands/prune_audit_logs.py:0: error: Function is missing a type annotation [no-untyped-def]
src/documents/management/commands/prune_audit_logs.py:0: error: Function is missing a type annotation [no-untyped-def]
src/documents/management/commands/prune_audit_logs.py:0: error: Skipping analyzing "auditlog.models": module is installed, but missing library stubs or py.typed marker [import-untyped]
src/documents/matching.py:0: error: Argument 1 to "existing_document_matches_workflow" has incompatible type "ConsumableDocument | Document"; expected "Document" [arg-type]
@@ -448,18 +441,7 @@ src/documents/plugins/helpers.py:0: error: Function is missing a type annotation
src/documents/plugins/helpers.py:0: error: Skipping analyzing "channels_redis.pubsub": module is installed, but missing library stubs or py.typed marker [import-untyped]
src/documents/regex.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/documents/regex.py:0: error: Library stubs not installed for "regex" [import-untyped]
src/documents/sanity_checker.py:0: error: Argument 1 to "Path" has incompatible type "Path | None"; expected "str | PathLike[str]" [arg-type]
src/documents/sanity_checker.py:0: error: Cannot use Final inside a loop [misc]
src/documents/sanity_checker.py:0: error: Cannot use Final inside a loop [misc]
src/documents/sanity_checker.py:0: error: Cannot use Final inside a loop [misc]
src/documents/sanity_checker.py:0: error: Function is missing a type annotation [no-untyped-def]
src/documents/sanity_checker.py:0: error: Function is missing a type annotation [no-untyped-def]
src/documents/sanity_checker.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/sanity_checker.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/sanity_checker.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/sanity_checker.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/sanity_checker.py:0: error: Incompatible type for "task_id" of "PaperlessTask" (got "UUID", expected "str | int | Combinable") [misc]
src/documents/sanity_checker.py:0: error: Missing type parameters for generic type "dict" [type-arg]
src/documents/schema.py:0: error: Function is missing a type annotation [no-untyped-def]
src/documents/schema.py:0: error: Function is missing a type annotation [no-untyped-def]
src/documents/schema.py:0: error: Function is missing a type annotation [no-untyped-def]
@@ -550,6 +532,8 @@ src/documents/serialisers.py:0: error: Function is missing a type annotation [n
src/documents/serialisers.py:0: error: Function is missing a type annotation [no-untyped-def]
src/documents/serialisers.py:0: error: Function is missing a type annotation [no-untyped-def]
src/documents/serialisers.py:0: error: Function is missing a type annotation [no-untyped-def]
src/documents/serialisers.py:0: error: Function is missing a type annotation [no-untyped-def]
src/documents/serialisers.py:0: error: Function is missing a type annotation [no-untyped-def]
src/documents/serialisers.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/serialisers.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/serialisers.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
@@ -640,6 +624,7 @@ src/documents/serialisers.py:0: error: Missing type parameters for generic type
src/documents/serialisers.py:0: error: Missing type parameters for generic type "Serializer" [type-arg]
src/documents/serialisers.py:0: error: Missing type parameters for generic type "Serializer" [type-arg]
src/documents/serialisers.py:0: error: Missing type parameters for generic type "Serializer" [type-arg]
src/documents/serialisers.py:0: error: Missing type parameters for generic type "Serializer" [type-arg]
src/documents/serialisers.py:0: error: Missing type parameters for generic type "dict" [type-arg]
src/documents/serialisers.py:0: error: Missing type parameters for generic type "dict" [type-arg]
src/documents/serialisers.py:0: error: Need type annotation for "document" [var-annotated]
@@ -664,9 +649,6 @@ src/documents/signals/handlers.py:0: error: Argument 2 to "match_storage_paths"
src/documents/signals/handlers.py:0: error: Argument 2 to "match_tags" has incompatible type "DocumentClassifier | None"; expected "DocumentClassifier" [arg-type]
src/documents/signals/handlers.py:0: error: Argument 2 to "validate_move" has incompatible type "Path | Any | None"; expected "Path" [arg-type]
src/documents/signals/handlers.py:0: error: Argument 3 to "validate_move" has incompatible type "Path | Any | None"; expected "Path" [arg-type]
src/documents/signals/handlers.py:0: error: Argument 5 to "_suggestion_printer" has incompatible type "Any | None"; expected "MatchingModel" [arg-type]
src/documents/signals/handlers.py:0: error: Argument 5 to "_suggestion_printer" has incompatible type "Any | None"; expected "MatchingModel" [arg-type]
src/documents/signals/handlers.py:0: error: Argument 5 to "_suggestion_printer" has incompatible type "Any | None"; expected "MatchingModel" [arg-type]
src/documents/signals/handlers.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/documents/signals/handlers.py:0: error: Function is missing a type annotation [no-untyped-def]
src/documents/signals/handlers.py:0: error: Function is missing a type annotation [no-untyped-def]
@@ -686,12 +668,6 @@ src/documents/signals/handlers.py:0: error: Function is missing a type annotatio
src/documents/signals/handlers.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/signals/handlers.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/signals/handlers.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/signals/handlers.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/signals/handlers.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/signals/handlers.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/signals/handlers.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/signals/handlers.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/signals/handlers.py:0: error: Incompatible types in assignment (expression has type "list[Tag]", variable has type "set[Tag]") [assignment]
src/documents/signals/handlers.py:0: error: Incompatible types in assignment (expression has type "tuple[Any, Any, Any]", variable has type "tuple[Any, Any]") [assignment]
src/documents/signals/handlers.py:0: error: Item "ConsumableDocument" of "Document | ConsumableDocument" has no attribute "save" [union-attr]
src/documents/signals/handlers.py:0: error: Item "ConsumableDocument" of "Document | ConsumableDocument" has no attribute "source_path" [union-attr]
@@ -719,7 +695,6 @@ src/documents/tasks.py:0: error: Function is missing a type annotation for one o
src/documents/tasks.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/tasks.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/tasks.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/tasks.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/tasks.py:0: error: Incompatible type for "task_id" of "PaperlessTask" (got "UUID", expected "str | int | Combinable") [misc]
src/documents/tasks.py:0: error: Incompatible type for "task_id" of "PaperlessTask" (got "UUID", expected "str | int | Combinable") [misc]
src/documents/tasks.py:0: error: Incompatible types in assignment (expression has type "Path", variable has type "str") [assignment]
@@ -746,8 +721,80 @@ src/documents/templating/utils.py:0: error: Function is missing a type annotatio
src/documents/templating/workflows.py:0: error: Incompatible return value type (got "None", expected "str") [return-value]
src/documents/tests/conftest.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/documents/tests/conftest.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/documents/tests/conftest.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/documents/tests/conftest.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/documents/tests/conftest.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/tests/conftest.py:0: error: Incompatible return value type (got "DocumentFactory", expected "Document") [return-value]
src/documents/tests/factories.py:0: error: Missing type parameters for generic type "DjangoModelFactory" [type-arg]
src/documents/tests/factories.py:0: error: Missing type parameters for generic type "DjangoModelFactory" [type-arg]
src/documents/tests/factories.py:0: error: Missing type parameters for generic type "DjangoModelFactory" [type-arg]
src/documents/tests/factories.py:0: error: Missing type parameters for generic type "DjangoModelFactory" [type-arg]
src/documents/tests/factories.py:0: error: Missing type parameters for generic type "DjangoModelFactory" [type-arg]
src/documents/tests/management/test_management_base_cmd.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/documents/tests/management/test_management_base_cmd.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/documents/tests/management/test_management_base_cmd.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/documents/tests/management/test_management_base_cmd.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/documents/tests/management/test_management_base_cmd.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/documents/tests/management/test_management_base_cmd.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/documents/tests/management/test_management_base_cmd.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/documents/tests/management/test_management_base_cmd.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/documents/tests/management/test_management_base_cmd.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/documents/tests/management/test_management_base_cmd.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/documents/tests/management/test_management_base_cmd.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/documents/tests/management/test_management_base_cmd.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/documents/tests/management/test_management_base_cmd.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/documents/tests/management/test_management_base_cmd.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/documents/tests/management/test_management_base_cmd.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/documents/tests/management/test_management_base_cmd.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/documents/tests/management/test_management_base_cmd.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/documents/tests/management/test_management_base_cmd.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/documents/tests/management/test_management_base_cmd.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/documents/tests/management/test_management_base_cmd.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/documents/tests/management/test_management_base_cmd.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/documents/tests/management/test_management_base_cmd.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/documents/tests/management/test_management_base_cmd.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/documents/tests/management/test_management_base_cmd.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/documents/tests/management/test_management_base_cmd.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/documents/tests/management/test_management_base_cmd.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/documents/tests/management/test_management_base_cmd.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/documents/tests/management/test_management_base_cmd.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/documents/tests/management/test_management_base_cmd.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/documents/tests/management/test_management_base_cmd.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/documents/tests/management/test_management_base_cmd.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/documents/tests/management/test_management_base_cmd.py:0: error: Function is missing a type annotation [no-untyped-def]
src/documents/tests/management/test_management_base_cmd.py:0: error: Function is missing a type annotation [no-untyped-def]
src/documents/tests/management/test_management_base_cmd.py:0: error: Function is missing a type annotation [no-untyped-def]
src/documents/tests/management/test_management_base_cmd.py:0: error: Function is missing a type annotation [no-untyped-def]
src/documents/tests/management/test_management_base_cmd.py:0: error: Function is missing a type annotation [no-untyped-def]
src/documents/tests/management/test_management_base_cmd.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/tests/management/test_management_base_cmd.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/tests/management/test_management_base_cmd.py:0: error: Incompatible types in assignment (expression has type "StringIO", variable has type "OutputWrapper") [assignment]
src/documents/tests/management/test_management_base_cmd.py:0: error: Incompatible types in assignment (expression has type "StringIO", variable has type "OutputWrapper") [assignment]
src/documents/tests/management/test_management_base_cmd.py:0: error: Incompatible types in assignment (expression has type "StringIO", variable has type "OutputWrapper") [assignment]
src/documents/tests/management/test_management_base_cmd.py:0: error: Incompatible types in assignment (expression has type "StringIO", variable has type "OutputWrapper") [assignment]
src/documents/tests/management/test_management_base_cmd.py:0: error: Incompatible types in assignment (expression has type "StringIO", variable has type "OutputWrapper") [assignment]
src/documents/tests/management/test_management_base_cmd.py:0: error: Incompatible types in assignment (expression has type "StringIO", variable has type "OutputWrapper") [assignment]
src/documents/tests/management/test_management_base_cmd.py:0: error: Incompatible types in assignment (expression has type "StringIO", variable has type "OutputWrapper") [assignment]
src/documents/tests/management/test_management_base_cmd.py:0: error: Incompatible types in assignment (expression has type "StringIO", variable has type "OutputWrapper") [assignment]
src/documents/tests/management/test_management_base_cmd.py:0: error: Incompatible types in assignment (expression has type "StringIO", variable has type "OutputWrapper") [assignment]
src/documents/tests/management/test_management_base_cmd.py:0: error: Incompatible types in assignment (expression has type "StringIO", variable has type "OutputWrapper") [assignment]
src/documents/tests/management/test_management_base_cmd.py:0: error: Incompatible types in assignment (expression has type "StringIO", variable has type "OutputWrapper") [assignment]
src/documents/tests/management/test_management_base_cmd.py:0: error: Incompatible types in assignment (expression has type "StringIO", variable has type "OutputWrapper") [assignment]
src/documents/tests/management/test_management_base_cmd.py:0: error: Incompatible types in assignment (expression has type "StringIO", variable has type "OutputWrapper") [assignment]
src/documents/tests/management/test_management_base_cmd.py:0: error: Incompatible types in assignment (expression has type "StringIO", variable has type "OutputWrapper") [assignment]
src/documents/tests/management/test_management_base_cmd.py:0: error: Missing type parameters for generic type "QuerySet" [type-arg]
src/documents/tests/management/test_management_base_cmd.py:0: error: Missing type parameters for generic type "dict" [type-arg]
src/documents/tests/management/test_management_base_cmd.py:0: error: Missing type parameters for generic type "dict" [type-arg]
src/documents/tests/management/test_management_base_cmd.py:0: error: Missing type parameters for generic type "dict" [type-arg]
src/documents/tests/management/test_management_base_cmd.py:0: error: Missing type parameters for generic type "dict" [type-arg]
src/documents/tests/management/test_management_base_cmd.py:0: error: Missing type parameters for generic type "dict" [type-arg]
src/documents/tests/management/test_management_base_cmd.py:0: error: Missing type parameters for generic type "dict" [type-arg]
src/documents/tests/management/test_management_base_cmd.py:0: error: Missing type parameters for generic type "list" [type-arg]
src/documents/tests/management/test_management_base_cmd.py:0: error: Need type annotation for "result" [var-annotated]
src/documents/tests/management/test_management_base_cmd.py:0: error: Need type annotation for "result" (hint: "result: list[<type>] = ...") [var-annotated]
src/documents/tests/management/test_management_base_cmd.py:0: error: Need type annotation for "results" [var-annotated]
src/documents/tests/management/test_management_sanity_checker.py:0: error: "DocumentFactory" has no attribute "source_path"; maybe "storage_path"? [attr-defined]
src/documents/tests/management/test_management_sanity_checker.py:0: error: "DocumentFactory" has no attribute "thumbnail_path" [attr-defined]
src/documents/tests/test_admin.py:0: error: "PaperlessUserForm" has no attribute "request" [attr-defined]
src/documents/tests/test_admin.py:0: error: "PaperlessUserForm" has no attribute "request" [attr-defined]
src/documents/tests/test_admin.py:0: error: Function is missing a type annotation [no-untyped-def]
@@ -775,6 +822,7 @@ src/documents/tests/test_api_app_config.py:0: error: Item "None" of "Application
src/documents/tests/test_api_bulk_download.py:0: error: Value of type variable "_StrPathT" of "copy" cannot be "Path | None" [type-var]
src/documents/tests/test_api_bulk_edit.py:0: error: "type[MatchingModel]" has no attribute "objects" [attr-defined]
src/documents/tests/test_api_bulk_edit.py:0: error: "type[MatchingModel]" has no attribute "objects" [attr-defined]
src/documents/tests/test_api_bulk_edit.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/documents/tests/test_api_bulk_edit.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/tests/test_api_bulk_edit.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/tests/test_api_bulk_edit.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
@@ -827,8 +875,10 @@ src/documents/tests/test_api_custom_fields.py:0: error: Value of type "Any | Non
src/documents/tests/test_api_documents.py:0: error: "None" object is not iterable [misc]
src/documents/tests/test_api_documents.py:0: error: "object" has no attribute "get" [attr-defined]
src/documents/tests/test_api_documents.py:0: error: Argument 1 to "Path" has incompatible type "Path | None"; expected "str | PathLike[str]" [arg-type]
src/documents/tests/test_api_documents.py:0: error: Argument 1 to "Path" has incompatible type "Path | None"; expected "str | PathLike[str]" [arg-type]
src/documents/tests/test_api_documents.py:0: error: Argument 1 to "assertCountEqual" of "TestCase" has incompatible type "list[int] | None"; expected "Iterable[Any]" [arg-type]
src/documents/tests/test_api_documents.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/documents/tests/test_api_documents.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/documents/tests/test_api_documents.py:0: error: Function is missing a type annotation [no-untyped-def]
src/documents/tests/test_api_documents.py:0: error: Function is missing a type annotation [no-untyped-def]
src/documents/tests/test_api_documents.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
@@ -1173,7 +1223,20 @@ src/documents/tests/test_management_exporter.py:0: error: Function is missing a
src/documents/tests/test_management_exporter.py:0: error: Item "None" of "MailAccount | None" has no attribute "password" [union-attr]
src/documents/tests/test_management_exporter.py:0: error: Skipping analyzing "allauth.socialaccount.models": module is installed, but missing library stubs or py.typed marker [import-untyped]
src/documents/tests/test_management_fuzzy.py:0: error: Function is missing a type annotation [no-untyped-def]
src/documents/tests/test_management_retagger.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/documents/tests/test_management_retagger.py:0: error: "DocumentFactory" has no attribute "refresh_from_db" [attr-defined]
src/documents/tests/test_management_retagger.py:0: error: "DocumentFactory" has no attribute "refresh_from_db" [attr-defined]
src/documents/tests/test_management_retagger.py:0: error: "DocumentFactory" has no attribute "refresh_from_db" [attr-defined]
src/documents/tests/test_management_retagger.py:0: error: "DocumentFactory" has no attribute "tags" [attr-defined]
src/documents/tests/test_management_retagger.py:0: error: "DocumentFactory" has no attribute "tags" [attr-defined]
src/documents/tests/test_management_retagger.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/tests/test_management_retagger.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/tests/test_management_retagger.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/tests/test_management_retagger.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/tests/test_management_retagger.py:0: error: Incompatible return value type (got "tuple[CorrespondentFactory, CorrespondentFactory]", expected "tuple[Correspondent, Correspondent]") [return-value]
src/documents/tests/test_management_retagger.py:0: error: Incompatible return value type (got "tuple[DocumentFactory, DocumentFactory, DocumentFactory, DocumentFactory]", expected "tuple[Document, Document, Document, Document]") [return-value]
src/documents/tests/test_management_retagger.py:0: error: Incompatible return value type (got "tuple[DocumentTypeFactory, DocumentTypeFactory]", expected "tuple[DocumentType, DocumentType]") [return-value]
src/documents/tests/test_management_retagger.py:0: error: Incompatible return value type (got "tuple[StoragePathFactory, StoragePathFactory, StoragePathFactory]", expected "tuple[StoragePath, StoragePath, StoragePath]") [return-value]
src/documents/tests/test_management_retagger.py:0: error: Incompatible return value type (got "tuple[TagFactory, TagFactory, TagFactory, TagFactory, TagFactory]", expected "tuple[Tag, Tag, Tag, Tag, Tag]") [return-value]
src/documents/tests/test_management_superuser.py:0: error: Function is missing a type annotation [no-untyped-def]
src/documents/tests/test_migration_share_link_bundle.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/tests/test_migration_share_link_bundle.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
@@ -1190,9 +1253,10 @@ src/documents/tests/test_parsers.py:0: error: Function is missing a type annotat
src/documents/tests/test_parsers.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/tests/test_parsers.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/tests/test_parsers.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/tests/test_sanity_check.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/documents/tests/test_sanity_check.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/tests/test_sanity_check.py:0: error: Invalid index type "None" for "dict[int, list[dict[Any, Any]]]"; expected type "int" [index]
src/documents/tests/test_sanity_check.py:0: error: Argument 1 to "Path" has incompatible type "Path | None"; expected "str | PathLike[str]" [arg-type]
src/documents/tests/test_sanity_check.py:0: error: Argument 1 to "Path" has incompatible type "Path | None"; expected "str | PathLike[str]" [arg-type]
src/documents/tests/test_sanity_check.py:0: error: Argument 1 to "Path" has incompatible type "Path | None"; expected "str | PathLike[str]" [arg-type]
src/documents/tests/test_sanity_check.py:0: error: Unsupported right operand type for in ("str | None") [operator]
src/documents/tests/test_share_link_bundles.py:0: error: "_MonkeyPatchedResponse" has no attribute "streaming_content" [attr-defined]
src/documents/tests/test_share_link_bundles.py:0: error: Argument 1 to "ZipFile" has incompatible type "Path | None"; expected "str | PathLike[str] | IO[bytes]" [arg-type]
src/documents/tests/test_share_link_bundles.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
@@ -1223,10 +1287,6 @@ src/documents/tests/test_tasks.py:0: error: Function is missing a type annotatio
src/documents/tests/test_tasks.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/tests/test_tasks.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/tests/test_tasks.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/tests/test_tasks.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/tests/test_tasks.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/tests/test_tasks.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/tests/test_tasks.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/tests/test_views.py:0: error: "_MonkeyPatchedWSGIResponse" has no attribute "render" [attr-defined]
src/documents/tests/test_views.py:0: error: "_MonkeyPatchedWSGIResponse" has no attribute "render" [attr-defined]
src/documents/tests/test_views.py:0: error: "_MonkeyPatchedWSGIResponse" has no attribute "url" [attr-defined]
@@ -1534,7 +1594,6 @@ src/documents/views.py:0: error: "get_serializer_context" undefined in superclas
src/documents/views.py:0: error: "object" not callable [operator]
src/documents/views.py:0: error: "type[Model]" has no attribute "objects" [attr-defined]
src/documents/views.py:0: error: Argument "path" to "EmailAttachment" has incompatible type "Path | None"; expected "Path" [arg-type]
src/documents/views.py:0: error: Argument 1 to "int" has incompatible type "str | None"; expected "str | Buffer | SupportsInt | SupportsIndex | SupportsTrunc" [arg-type]
src/documents/views.py:0: error: Argument 2 to "match_correspondents" has incompatible type "DocumentClassifier | None"; expected "DocumentClassifier" [arg-type]
src/documents/views.py:0: error: Argument 2 to "match_document_types" has incompatible type "DocumentClassifier | None"; expected "DocumentClassifier" [arg-type]
src/documents/views.py:0: error: Argument 2 to "match_storage_paths" has incompatible type "DocumentClassifier | None"; expected "DocumentClassifier" [arg-type]
@@ -1609,6 +1668,8 @@ src/documents/views.py:0: error: Function is missing a type annotation [no-unty
src/documents/views.py:0: error: Function is missing a type annotation [no-untyped-def]
src/documents/views.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/views.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/views.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/views.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/views.py:0: error: Incompatible type for lookup 'owner': (got "User | AnonymousUser", expected "User | int | None") [misc]
src/documents/views.py:0: error: Incompatible types in assignment (expression has type "Any | None", variable has type "dict[Any, Any]") [assignment]
src/documents/views.py:0: error: Incompatible types in assignment (expression has type "QuerySet[Any, Any]", variable has type "list[Any]") [assignment]
@@ -1864,39 +1925,54 @@ src/paperless/serialisers.py:0: error: Skipping analyzing "allauth.mfa.adapter":
src/paperless/serialisers.py:0: error: Skipping analyzing "allauth.mfa.models": module is installed, but missing library stubs or py.typed marker [import-untyped]
src/paperless/serialisers.py:0: error: Skipping analyzing "allauth.mfa.totp.internal.auth": module is installed, but missing library stubs or py.typed marker [import-untyped]
src/paperless/serialisers.py:0: error: Skipping analyzing "allauth.socialaccount.models": module is installed, but missing library stubs or py.typed marker [import-untyped]
src/paperless/settings.py:0: error: "Sequence[str]" has no attribute "append" [attr-defined]
src/paperless/settings.py:0: error: "Sequence[str]" has no attribute "insert" [attr-defined]
src/paperless/settings.py:0: error: "object" has no attribute "update" [attr-defined]
src/paperless/settings.py:0: error: "object" has no attribute "update" [attr-defined]
src/paperless/settings.py:0: error: "object" has no attribute "update" [attr-defined]
src/paperless/settings.py:0: error: "object" has no attribute "update" [attr-defined]
src/paperless/settings.py:0: error: Argument 1 to "_parse_ignore_dates" has incompatible type "str | None"; expected "str" [arg-type]
src/paperless/settings.py:0: error: Argument 1 to "append" of "list" has incompatible type "str | None"; expected "str" [arg-type]
src/paperless/settings.py:0: error: Argument 1 to "int" has incompatible type "str | None"; expected "str | Buffer | SupportsInt | SupportsIndex | SupportsTrunc" [arg-type]
src/paperless/settings.py:0: error: Argument 1 to "int" has incompatible type "str | None"; expected "str | Buffer | SupportsInt | SupportsIndex | SupportsTrunc" [arg-type]
src/paperless/settings.py:0: error: Argument 1 to "int" has incompatible type "str | None"; expected "str | Buffer | SupportsInt | SupportsIndex | SupportsTrunc" [arg-type]
src/paperless/settings.py:0: error: Argument 1 to "int" has incompatible type "str | None"; expected "str | Buffer | SupportsInt | SupportsIndex | SupportsTrunc" [arg-type]
src/paperless/settings.py:0: error: Argument 2 to "__get_path" has incompatible type "str | None"; expected "PathLike[Any] | str" [arg-type]
src/paperless/settings.py:0: error: Argument 2 to "getenv" has incompatible type "None"; expected "Collection[str]" [arg-type]
src/paperless/settings.py:0: error: Argument 2 to "getenv" has incompatible type "None"; expected "Collection[str]" [arg-type]
src/paperless/settings.py:0: error: Argument 2 to "getenv" has incompatible type "None"; expected "Collection[str]" [arg-type]
src/paperless/settings.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/paperless/settings.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/paperless/settings.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/paperless/settings.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/paperless/settings.py:0: error: Function is missing a type annotation [no-untyped-def]
src/paperless/settings.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/paperless/settings.py:0: error: Incompatible return value type (got "set[date]", expected "set[datetime]") [return-value]
src/paperless/settings.py:0: error: Incompatible return value type (got "tuple[str | None, str, str, str, str]", expected "tuple[str, str, str, str, str]") [return-value]
src/paperless/settings.py:0: error: Incompatible types in assignment (expression has type "bool", variable has type "str") [assignment]
src/paperless/settings.py:0: error: Incompatible types in assignment (expression has type "set[datetime]", variable has type "set[date]") [assignment]
src/paperless/settings.py:0: error: Missing type parameters for generic type "PathLike" [type-arg]
src/paperless/settings.py:0: error: Missing type parameters for generic type "dict" [type-arg]
src/paperless/settings.py:0: error: Missing type parameters for generic type "dict" [type-arg]
src/paperless/settings.py:0: error: No overload variant of "getenv" matches argument types "Collection[str]", "Collection[str]" [call-overload]
src/paperless/settings.py:0: error: Skipping analyzing "compression_middleware.middleware": module is installed, but missing library stubs or py.typed marker [import-untyped]
src/paperless/settings/__init__.py:0: error: "Sequence[str]" has no attribute "append" [attr-defined]
src/paperless/settings/__init__.py:0: error: "Sequence[str]" has no attribute "insert" [attr-defined]
src/paperless/settings/__init__.py:0: error: Argument 1 to "_parse_ignore_dates" has incompatible type "str | None"; expected "str" [arg-type]
src/paperless/settings/__init__.py:0: error: Argument 1 to "append" of "list" has incompatible type "str | None"; expected "str" [arg-type]
src/paperless/settings/__init__.py:0: error: Argument 2 to "__get_path" has incompatible type "str | None"; expected "PathLike[Any] | str" [arg-type]
src/paperless/settings/__init__.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/paperless/settings/__init__.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/paperless/settings/__init__.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/paperless/settings/__init__.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/paperless/settings/__init__.py:0: error: Function is missing a type annotation [no-untyped-def]
src/paperless/settings/__init__.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/paperless/settings/__init__.py:0: error: Incompatible return value type (got "set[date]", expected "set[datetime]") [return-value]
src/paperless/settings/__init__.py:0: error: Incompatible return value type (got "tuple[str | None, str, str, str, str]", expected "tuple[str, str, str, str, str]") [return-value]
src/paperless/settings/__init__.py:0: error: Incompatible types in assignment (expression has type "bool", variable has type "str") [assignment]
src/paperless/settings/__init__.py:0: error: Incompatible types in assignment (expression has type "set[datetime]", variable has type "set[date]") [assignment]
src/paperless/settings/__init__.py:0: error: Missing type parameters for generic type "PathLike" [type-arg]
src/paperless/settings/__init__.py:0: error: Missing type parameters for generic type "dict" [type-arg]
src/paperless/settings/__init__.py:0: error: No overload variant of "getenv" matches argument types "Collection[str]", "Collection[str]" [call-overload]
src/paperless/settings/__init__.py:0: error: Skipping analyzing "compression_middleware.middleware": module is installed, but missing library stubs or py.typed marker [import-untyped]
src/paperless/settings/parsers.py:0: error: Incompatible types in assignment (expression has type "Path", variable has type "bool") [assignment]
src/paperless/settings/parsers.py:0: error: Missing type parameters for generic type "dict" [type-arg]
src/paperless/settings/parsers.py:0: error: Missing type parameters for generic type "dict" [type-arg]
src/paperless/settings/parsers.py:0: error: Missing type parameters for generic type "dict" [type-arg]
src/paperless/signals.py:0: error: Function is missing a type annotation [no-untyped-def]
src/paperless/signals.py:0: error: Function is missing a type annotation [no-untyped-def]
src/paperless/tests/settings/test_custom_parsers.py:0: error: Missing type parameters for generic type "dict" [type-arg]
src/paperless/tests/settings/test_environment_parsers.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/paperless/tests/settings/test_environment_parsers.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/paperless/tests/settings/test_environment_parsers.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/paperless/tests/settings/test_environment_parsers.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/paperless/tests/settings/test_environment_parsers.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/paperless/tests/settings/test_environment_parsers.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/paperless/tests/settings/test_environment_parsers.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/paperless/tests/settings/test_environment_parsers.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/paperless/tests/settings/test_environment_parsers.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/paperless/tests/settings/test_environment_parsers.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/paperless/tests/settings/test_environment_parsers.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/paperless/tests/settings/test_environment_parsers.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/paperless/tests/settings/test_environment_parsers.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/paperless/tests/settings/test_environment_parsers.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/paperless/tests/settings/test_environment_parsers.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/paperless/tests/settings/test_environment_parsers.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/paperless/tests/settings/test_environment_parsers.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/paperless/tests/settings/test_environment_parsers.py:0: error: Function is missing a return type annotation [no-untyped-def]
src/paperless/tests/settings/test_environment_parsers.py:0: error: Function is missing a type annotation [no-untyped-def]
src/paperless/tests/settings/test_environment_parsers.py:0: error: Function is missing a type annotation [no-untyped-def]
src/paperless/tests/settings/test_environment_parsers.py:0: error: Function is missing a type annotation [no-untyped-def]
src/paperless/tests/settings/test_environment_parsers.py:0: error: Function is missing a type annotation [no-untyped-def]
src/paperless/tests/test_adapter.py:0: error: Cannot assign to a method [method-assign]
src/paperless/tests/test_adapter.py:0: error: Cannot assign to a method [method-assign]
src/paperless/tests/test_adapter.py:0: error: Cannot assign to a method [method-assign]
@@ -1904,6 +1980,12 @@ src/paperless/tests/test_adapter.py:0: error: Function is missing a type annotat
src/paperless/tests/test_adapter.py:0: error: Skipping analyzing "allauth.account.adapter": module is installed, but missing library stubs or py.typed marker [import-untyped]
src/paperless/tests/test_adapter.py:0: error: Skipping analyzing "allauth.core": module is installed, but missing library stubs or py.typed marker [import-untyped]
src/paperless/tests/test_adapter.py:0: error: Skipping analyzing "allauth.socialaccount.adapter": module is installed, but missing library stubs or py.typed marker [import-untyped]
src/paperless/tests/test_checks.py:0: error: Generator has incompatible item type "str | None"; expected "str" [misc]
src/paperless/tests/test_checks.py:0: error: Unsupported right operand type for in ("str | None") [operator]
src/paperless/tests/test_checks.py:0: error: Unsupported right operand type for in ("str | None") [operator]
src/paperless/tests/test_checks.py:0: error: Unsupported right operand type for in ("str | None") [operator]
src/paperless/tests/test_checks.py:0: error: Unsupported right operand type for in ("str | None") [operator]
src/paperless/tests/test_checks.py:0: error: Unsupported right operand type for in ("str | None") [operator]
src/paperless/tests/test_db_cache.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/paperless/tests/test_db_cache.py:0: error: Skipping analyzing "cachalot.settings": module is installed, but missing library stubs or py.typed marker [import-untyped]
src/paperless/tests/test_settings.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]

File diff suppressed because it is too large Load Diff

View File

@@ -1,100 +1,107 @@
import logging
from unittest import mock
import pytest
from allauth.account.adapter import get_adapter
from allauth.core import context
from allauth.socialaccount.adapter import get_adapter as get_social_adapter
from django.conf import settings
from django.contrib.auth.models import AnonymousUser
from django.contrib.auth.models import Group
from django.contrib.auth.models import User
from django.forms import ValidationError
from django.http import HttpRequest
from django.test import TestCase
from django.test import override_settings
from django.urls import reverse
from pytest_django.fixtures import SettingsWrapper
from pytest_mock import MockerFixture
from rest_framework.authtoken.models import Token
from paperless.adapter import DrfTokenStrategy
@pytest.mark.django_db
class TestCustomAccountAdapter:
def test_is_open_for_signup(self, settings: SettingsWrapper) -> None:
class TestCustomAccountAdapter(TestCase):
def test_is_open_for_signup(self) -> None:
adapter = get_adapter()
# With no accounts, signups should be allowed
assert adapter.is_open_for_signup(None)
self.assertTrue(adapter.is_open_for_signup(None))
User.objects.create_user("testuser")
# Test when ACCOUNT_ALLOW_SIGNUPS is True
settings.ACCOUNT_ALLOW_SIGNUPS = True
assert adapter.is_open_for_signup(None)
self.assertTrue(adapter.is_open_for_signup(None))
# Test when ACCOUNT_ALLOW_SIGNUPS is False
settings.ACCOUNT_ALLOW_SIGNUPS = False
assert not adapter.is_open_for_signup(None)
self.assertFalse(adapter.is_open_for_signup(None))
def test_is_safe_url(self, settings: SettingsWrapper) -> None:
def test_is_safe_url(self) -> None:
request = HttpRequest()
request.get_host = lambda: "example.com"
request.get_host = mock.Mock(return_value="example.com")
with context.request_context(request):
adapter = get_adapter()
with override_settings(ALLOWED_HOSTS=["*"]):
# True because request host is same
url = "https://example.com"
self.assertTrue(adapter.is_safe_url(url))
settings.ALLOWED_HOSTS = ["*"]
# True because request host is same
assert adapter.is_safe_url("https://example.com")
url = "https://evil.com"
# False despite wildcard because request host is different
assert not adapter.is_safe_url("https://evil.com")
self.assertFalse(adapter.is_safe_url(url))
settings.ALLOWED_HOSTS = ["example.com"]
url = "https://example.com"
# True because request host is same
assert adapter.is_safe_url("https://example.com")
self.assertTrue(adapter.is_safe_url(url))
settings.ALLOWED_HOSTS = ["*", "example.com"]
url = "//evil.com"
# False because request host is not in allowed hosts
assert not adapter.is_safe_url("//evil.com")
self.assertFalse(adapter.is_safe_url(url))
def test_pre_authenticate(
self,
settings: SettingsWrapper,
mocker: MockerFixture,
) -> None:
mocker.patch("allauth.core.internal.ratelimit.consume", return_value=True)
@mock.patch("allauth.core.internal.ratelimit.consume", return_value=True)
def test_pre_authenticate(self, mock_consume) -> None:
adapter = get_adapter()
request = HttpRequest()
request.get_host = lambda: "example.com"
request.get_host = mock.Mock(return_value="example.com")
settings.DISABLE_REGULAR_LOGIN = False
adapter.pre_authenticate(request)
settings.DISABLE_REGULAR_LOGIN = True
with pytest.raises(ValidationError):
with self.assertRaises(ValidationError):
adapter.pre_authenticate(request)
def test_get_reset_password_from_key_url(self, settings: SettingsWrapper) -> None:
def test_get_reset_password_from_key_url(self) -> None:
request = HttpRequest()
request.get_host = lambda: "foo.org"
request.get_host = mock.Mock(return_value="foo.org")
with context.request_context(request):
adapter = get_adapter()
settings.PAPERLESS_URL = None
settings.ACCOUNT_DEFAULT_HTTP_PROTOCOL = "https"
expected_url = f"https://foo.org{reverse('account_reset_password_from_key', kwargs={'uidb36': 'UID', 'key': 'KEY'})}"
assert adapter.get_reset_password_from_key_url("UID-KEY") == expected_url
# Test when PAPERLESS_URL is None
with override_settings(
PAPERLESS_URL=None,
ACCOUNT_DEFAULT_HTTP_PROTOCOL="https",
):
expected_url = f"https://foo.org{reverse('account_reset_password_from_key', kwargs={'uidb36': 'UID', 'key': 'KEY'})}"
self.assertEqual(
adapter.get_reset_password_from_key_url("UID-KEY"),
expected_url,
)
settings.PAPERLESS_URL = "https://bar.com"
expected_url = f"https://bar.com{reverse('account_reset_password_from_key', kwargs={'uidb36': 'UID', 'key': 'KEY'})}"
assert adapter.get_reset_password_from_key_url("UID-KEY") == expected_url
# Test when PAPERLESS_URL is not None
with override_settings(PAPERLESS_URL="https://bar.com"):
expected_url = f"https://bar.com{reverse('account_reset_password_from_key', kwargs={'uidb36': 'UID', 'key': 'KEY'})}"
self.assertEqual(
adapter.get_reset_password_from_key_url("UID-KEY"),
expected_url,
)
def test_save_user_adds_groups(
self,
settings: SettingsWrapper,
mocker: MockerFixture,
) -> None:
settings.ACCOUNT_DEFAULT_GROUPS = ["group1", "group2"]
@override_settings(ACCOUNT_DEFAULT_GROUPS=["group1", "group2"])
def test_save_user_adds_groups(self) -> None:
Group.objects.create(name="group1")
user = User.objects.create_user("testuser")
adapter = get_adapter()
form = mocker.MagicMock(
form = mock.Mock(
cleaned_data={
"username": "testuser",
"email": "user@example.com",
@@ -103,81 +110,88 @@ class TestCustomAccountAdapter:
user = adapter.save_user(HttpRequest(), user, form, commit=True)
assert user.groups.count() == 1
assert user.groups.filter(name="group1").exists()
assert not user.groups.filter(name="group2").exists()
self.assertEqual(user.groups.count(), 1)
self.assertTrue(user.groups.filter(name="group1").exists())
self.assertFalse(user.groups.filter(name="group2").exists())
def test_fresh_install_save_creates_superuser(self, mocker: MockerFixture) -> None:
def test_fresh_install_save_creates_superuser(self) -> None:
adapter = get_adapter()
form = mocker.MagicMock(
form = mock.Mock(
cleaned_data={
"username": "testuser",
"email": "user@paperless-ngx.com",
},
)
user = adapter.save_user(HttpRequest(), User(), form, commit=True)
assert user.is_superuser
self.assertTrue(user.is_superuser)
form = mocker.MagicMock(
# Next time, it should not create a superuser
form = mock.Mock(
cleaned_data={
"username": "testuser2",
"email": "user2@paperless-ngx.com",
},
)
user2 = adapter.save_user(HttpRequest(), User(), form, commit=True)
assert not user2.is_superuser
self.assertFalse(user2.is_superuser)
class TestCustomSocialAccountAdapter:
@pytest.mark.django_db
def test_is_open_for_signup(self, settings: SettingsWrapper) -> None:
class TestCustomSocialAccountAdapter(TestCase):
def test_is_open_for_signup(self) -> None:
adapter = get_social_adapter()
# Test when SOCIALACCOUNT_ALLOW_SIGNUPS is True
settings.SOCIALACCOUNT_ALLOW_SIGNUPS = True
assert adapter.is_open_for_signup(None, None)
self.assertTrue(adapter.is_open_for_signup(None, None))
# Test when SOCIALACCOUNT_ALLOW_SIGNUPS is False
settings.SOCIALACCOUNT_ALLOW_SIGNUPS = False
assert not adapter.is_open_for_signup(None, None)
self.assertFalse(adapter.is_open_for_signup(None, None))
def test_get_connect_redirect_url(self) -> None:
adapter = get_social_adapter()
assert adapter.get_connect_redirect_url(None, None) == reverse("base")
request = None
socialaccount = None
@pytest.mark.django_db
def test_save_user_adds_groups(
self,
settings: SettingsWrapper,
mocker: MockerFixture,
) -> None:
settings.SOCIAL_ACCOUNT_DEFAULT_GROUPS = ["group1", "group2"]
# Test the default URL
expected_url = reverse("base")
self.assertEqual(
adapter.get_connect_redirect_url(request, socialaccount),
expected_url,
)
@override_settings(SOCIAL_ACCOUNT_DEFAULT_GROUPS=["group1", "group2"])
def test_save_user_adds_groups(self) -> None:
Group.objects.create(name="group1")
adapter = get_social_adapter()
request = HttpRequest()
user = User.objects.create_user("testuser")
sociallogin = mocker.MagicMock(user=user)
sociallogin = mock.Mock(
user=user,
)
user = adapter.save_user(HttpRequest(), sociallogin, None)
user = adapter.save_user(request, sociallogin, None)
assert user.groups.count() == 1
assert user.groups.filter(name="group1").exists()
assert not user.groups.filter(name="group2").exists()
self.assertEqual(user.groups.count(), 1)
self.assertTrue(user.groups.filter(name="group1").exists())
self.assertFalse(user.groups.filter(name="group2").exists())
def test_error_logged_on_authentication_error(
self,
caplog: pytest.LogCaptureFixture,
) -> None:
def test_error_logged_on_authentication_error(self) -> None:
adapter = get_social_adapter()
with caplog.at_level(logging.INFO, logger="paperless.auth"):
request = HttpRequest()
with self.assertLogs("paperless.auth", level="INFO") as log_cm:
adapter.on_authentication_error(
HttpRequest(),
request,
provider="test-provider",
error="Error",
exception="Test authentication error",
)
assert any("Test authentication error" in msg for msg in caplog.messages)
self.assertTrue(
any("Test authentication error" in message for message in log_cm.output),
)
@pytest.mark.django_db
class TestDrfTokenStrategy:
class TestDrfTokenStrategy(TestCase):
def test_create_access_token_creates_new_token(self) -> None:
"""
GIVEN:
@@ -187,6 +201,7 @@ class TestDrfTokenStrategy:
THEN:
- A new token is created and its key is returned
"""
user = User.objects.create_user("testuser")
request = HttpRequest()
request.user = user
@@ -194,9 +209,13 @@ class TestDrfTokenStrategy:
strategy = DrfTokenStrategy()
token_key = strategy.create_access_token(request)
assert token_key is not None
assert Token.objects.filter(user=user).exists()
assert token_key == Token.objects.get(user=user).key
# Verify a token was created
self.assertIsNotNone(token_key)
self.assertTrue(Token.objects.filter(user=user).exists())
# Verify the returned key matches the created token
token = Token.objects.get(user=user)
self.assertEqual(token_key, token.key)
def test_create_access_token_returns_existing_token(self) -> None:
"""
@@ -207,6 +226,7 @@ class TestDrfTokenStrategy:
THEN:
- The same token key is returned (no new token created)
"""
user = User.objects.create_user("testuser")
existing_token = Token.objects.create(user=user)
@@ -216,8 +236,11 @@ class TestDrfTokenStrategy:
strategy = DrfTokenStrategy()
token_key = strategy.create_access_token(request)
assert token_key == existing_token.key
assert Token.objects.filter(user=user).count() == 1
# Verify the existing token key is returned
self.assertEqual(token_key, existing_token.key)
# Verify only one token exists (no duplicate created)
self.assertEqual(Token.objects.filter(user=user).count(), 1)
def test_create_access_token_returns_none_for_unauthenticated_user(self) -> None:
"""
@@ -228,11 +251,12 @@ class TestDrfTokenStrategy:
THEN:
- None is returned and no token is created
"""
request = HttpRequest()
request.user = AnonymousUser()
strategy = DrfTokenStrategy()
token_key = strategy.create_access_token(request)
assert token_key is None
assert Token.objects.count() == 0
self.assertIsNone(token_key)
self.assertEqual(Token.objects.count(), 0)

View File

@@ -1,12 +1,15 @@
import os
from dataclasses import dataclass
from pathlib import Path
from unittest import mock
import pytest
from django.core.checks import Warning
from pytest_django.fixtures import SettingsWrapper
from django.test import TestCase
from django.test import override_settings
from pytest_mock import MockerFixture
from documents.tests.utils import DirectoriesMixin
from documents.tests.utils import FileSystemAssertsMixin
from paperless.checks import audit_log_check
from paperless.checks import binaries_check
from paperless.checks import check_deprecated_db_settings
@@ -15,84 +18,54 @@ from paperless.checks import paths_check
from paperless.checks import settings_values_check
@dataclass(frozen=True, slots=True)
class PaperlessTestDirs:
data_dir: Path
media_dir: Path
consumption_dir: Path
# TODO: consolidate with documents/tests/conftest.py PaperlessDirs/paperless_dirs
# once the paperless and documents test suites are ready to share fixtures.
@pytest.fixture()
def directories(tmp_path: Path, settings: SettingsWrapper) -> PaperlessTestDirs:
data_dir = tmp_path / "data"
media_dir = tmp_path / "media"
consumption_dir = tmp_path / "consumption"
for d in (data_dir, media_dir, consumption_dir):
d.mkdir()
settings.DATA_DIR = data_dir
settings.MEDIA_ROOT = media_dir
settings.CONSUMPTION_DIR = consumption_dir
return PaperlessTestDirs(
data_dir=data_dir,
media_dir=media_dir,
consumption_dir=consumption_dir,
)
class TestChecks:
class TestChecks(DirectoriesMixin, TestCase):
def test_binaries(self) -> None:
assert binaries_check(None) == []
self.assertEqual(binaries_check(None), [])
def test_binaries_fail(self, settings: SettingsWrapper) -> None:
settings.CONVERT_BINARY = "uuuhh"
assert len(binaries_check(None)) == 1
@override_settings(CONVERT_BINARY="uuuhh")
def test_binaries_fail(self) -> None:
self.assertEqual(len(binaries_check(None)), 1)
@pytest.mark.usefixtures("directories")
def test_paths_check(self) -> None:
assert paths_check(None) == []
self.assertEqual(paths_check(None), [])
def test_paths_check_dont_exist(self, settings: SettingsWrapper) -> None:
settings.MEDIA_ROOT = Path("uuh")
settings.DATA_DIR = Path("whatever")
settings.CONSUMPTION_DIR = Path("idontcare")
@override_settings(
MEDIA_ROOT=Path("uuh"),
DATA_DIR=Path("whatever"),
CONSUMPTION_DIR=Path("idontcare"),
)
def test_paths_check_dont_exist(self) -> None:
msgs = paths_check(None)
self.assertEqual(len(msgs), 3, str(msgs))
for msg in msgs:
self.assertTrue(msg.msg.endswith("is set but doesn't exist."))
def test_paths_check_no_access(self) -> None:
Path(self.dirs.data_dir).chmod(0o000)
Path(self.dirs.media_dir).chmod(0o000)
Path(self.dirs.consumption_dir).chmod(0o000)
self.addCleanup(os.chmod, self.dirs.data_dir, 0o777)
self.addCleanup(os.chmod, self.dirs.media_dir, 0o777)
self.addCleanup(os.chmod, self.dirs.consumption_dir, 0o777)
msgs = paths_check(None)
self.assertEqual(len(msgs), 3)
assert len(msgs) == 3, str(msgs)
for msg in msgs:
assert msg.msg.endswith("is set but doesn't exist.")
self.assertTrue(msg.msg.endswith("is not writeable"))
def test_paths_check_no_access(self, directories: PaperlessTestDirs) -> None:
directories.data_dir.chmod(0o000)
directories.media_dir.chmod(0o000)
directories.consumption_dir.chmod(0o000)
@override_settings(DEBUG=False)
def test_debug_disabled(self) -> None:
self.assertEqual(debug_mode_check(None), [])
try:
msgs = paths_check(None)
finally:
directories.data_dir.chmod(0o777)
directories.media_dir.chmod(0o777)
directories.consumption_dir.chmod(0o777)
assert len(msgs) == 3
for msg in msgs:
assert msg.msg.endswith("is not writeable")
def test_debug_disabled(self, settings: SettingsWrapper) -> None:
settings.DEBUG = False
assert debug_mode_check(None) == []
def test_debug_enabled(self, settings: SettingsWrapper) -> None:
settings.DEBUG = True
assert len(debug_mode_check(None)) == 1
@override_settings(DEBUG=True)
def test_debug_enabled(self) -> None:
self.assertEqual(len(debug_mode_check(None)), 1)
class TestSettingsChecksAgainstDefaults:
class TestSettingsChecksAgainstDefaults(DirectoriesMixin, TestCase):
def test_all_valid(self) -> None:
"""
GIVEN:
@@ -103,71 +76,104 @@ class TestSettingsChecksAgainstDefaults:
- No system check errors reported
"""
msgs = settings_values_check(None)
assert len(msgs) == 0
self.assertEqual(len(msgs), 0)
class TestOcrSettingsChecks:
@pytest.mark.parametrize(
("setting", "value", "expected_msg"),
[
pytest.param(
"OCR_OUTPUT_TYPE",
"notapdf",
'OCR output type "notapdf"',
id="invalid-output-type",
),
pytest.param(
"OCR_MODE",
"makeitso",
'OCR output mode "makeitso"',
id="invalid-mode",
),
pytest.param(
"OCR_MODE",
"skip_noarchive",
"deprecated",
id="deprecated-mode",
),
pytest.param(
"OCR_SKIP_ARCHIVE_FILE",
"invalid",
'OCR_SKIP_ARCHIVE_FILE setting "invalid"',
id="invalid-skip-archive-file",
),
pytest.param(
"OCR_CLEAN",
"cleanme",
'OCR clean mode "cleanme"',
id="invalid-clean",
),
],
)
def test_invalid_setting_produces_one_error(
self,
settings: SettingsWrapper,
setting: str,
value: str,
expected_msg: str,
) -> None:
class TestOcrSettingsChecks(DirectoriesMixin, TestCase):
@override_settings(OCR_OUTPUT_TYPE="notapdf")
def test_invalid_output_type(self) -> None:
"""
GIVEN:
- Default settings
- One OCR setting is set to an invalid value
- OCR output type is invalid
WHEN:
- Settings are validated
THEN:
- Exactly one system check error is reported containing the expected message
- system check error reported for OCR output type
"""
setattr(settings, setting, value)
msgs = settings_values_check(None)
self.assertEqual(len(msgs), 1)
assert len(msgs) == 1
assert expected_msg in msgs[0].msg
msg = msgs[0]
self.assertIn('OCR output type "notapdf"', msg.msg)
@override_settings(OCR_MODE="makeitso")
def test_invalid_ocr_type(self) -> None:
"""
GIVEN:
- Default settings
- OCR type is invalid
WHEN:
- Settings are validated
THEN:
- system check error reported for OCR type
"""
msgs = settings_values_check(None)
self.assertEqual(len(msgs), 1)
msg = msgs[0]
self.assertIn('OCR output mode "makeitso"', msg.msg)
@override_settings(OCR_MODE="skip_noarchive")
def test_deprecated_ocr_type(self) -> None:
"""
GIVEN:
- Default settings
- OCR type is deprecated
WHEN:
- Settings are validated
THEN:
- deprecation warning reported for OCR type
"""
msgs = settings_values_check(None)
self.assertEqual(len(msgs), 1)
msg = msgs[0]
self.assertIn("deprecated", msg.msg)
@override_settings(OCR_SKIP_ARCHIVE_FILE="invalid")
def test_invalid_ocr_skip_archive_file(self) -> None:
"""
GIVEN:
- Default settings
- OCR_SKIP_ARCHIVE_FILE is invalid
WHEN:
- Settings are validated
THEN:
- system check error reported for OCR_SKIP_ARCHIVE_FILE
"""
msgs = settings_values_check(None)
self.assertEqual(len(msgs), 1)
msg = msgs[0]
self.assertIn('OCR_SKIP_ARCHIVE_FILE setting "invalid"', msg.msg)
@override_settings(OCR_CLEAN="cleanme")
def test_invalid_ocr_clean(self) -> None:
"""
GIVEN:
- Default settings
- OCR cleaning type is invalid
WHEN:
- Settings are validated
THEN:
- system check error reported for OCR cleaning type
"""
msgs = settings_values_check(None)
self.assertEqual(len(msgs), 1)
msg = msgs[0]
self.assertIn('OCR clean mode "cleanme"', msg.msg)
class TestTimezoneSettingsChecks:
def test_invalid_timezone(self, settings: SettingsWrapper) -> None:
class TestTimezoneSettingsChecks(DirectoriesMixin, TestCase):
@override_settings(TIME_ZONE="TheMoon\\MyCrater")
def test_invalid_timezone(self) -> None:
"""
GIVEN:
- Default settings
@@ -177,16 +183,17 @@ class TestTimezoneSettingsChecks:
THEN:
- system check error reported for timezone
"""
settings.TIME_ZONE = "TheMoon\\MyCrater"
msgs = settings_values_check(None)
self.assertEqual(len(msgs), 1)
assert len(msgs) == 1
assert 'Timezone "TheMoon\\MyCrater"' in msgs[0].msg
msg = msgs[0]
self.assertIn('Timezone "TheMoon\\MyCrater"', msg.msg)
class TestEmailCertSettingsChecks:
def test_not_valid_file(self, settings: SettingsWrapper) -> None:
class TestEmailCertSettingsChecks(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
@override_settings(EMAIL_CERTIFICATE_FILE=Path("/tmp/not_actually_here.pem"))
def test_not_valid_file(self) -> None:
"""
GIVEN:
- Default settings
@@ -196,22 +203,19 @@ class TestEmailCertSettingsChecks:
THEN:
- system check error reported for email certificate
"""
cert_path = Path("/tmp/not_actually_here.pem")
assert not cert_path.is_file()
settings.EMAIL_CERTIFICATE_FILE = cert_path
self.assertIsNotFile("/tmp/not_actually_here.pem")
msgs = settings_values_check(None)
assert len(msgs) == 1
assert "Email cert /tmp/not_actually_here.pem is not a file" in msgs[0].msg
self.assertEqual(len(msgs), 1)
msg = msgs[0]
self.assertIn("Email cert /tmp/not_actually_here.pem is not a file", msg.msg)
class TestAuditLogChecks:
def test_was_enabled_once(
self,
settings: SettingsWrapper,
mocker: MockerFixture,
) -> None:
class TestAuditLogChecks(TestCase):
def test_was_enabled_once(self) -> None:
"""
GIVEN:
- Audit log is not enabled
@@ -220,18 +224,23 @@ class TestAuditLogChecks:
THEN:
- system check error reported for disabling audit log
"""
settings.AUDIT_LOG_ENABLED = False
introspect_mock = mocker.MagicMock()
introspect_mock = mock.MagicMock()
introspect_mock.introspection.table_names.return_value = ["auditlog_logentry"]
mocker.patch.dict(
"paperless.checks.connections",
{"default": introspect_mock},
)
with override_settings(AUDIT_LOG_ENABLED=False):
with mock.patch.dict(
"paperless.checks.connections",
{"default": introspect_mock},
):
msgs = audit_log_check(None)
msgs = audit_log_check(None)
self.assertEqual(len(msgs), 1)
assert len(msgs) == 1
assert "auditlog table was found but audit log is disabled." in msgs[0].msg
msg = msgs[0]
self.assertIn(
("auditlog table was found but audit log is disabled."),
msg.msg,
)
DEPRECATED_VARS: dict[str, str] = {
@@ -260,16 +269,20 @@ class TestDeprecatedDbSettings:
@pytest.mark.parametrize(
("env_var", "db_option_key"),
[
pytest.param("PAPERLESS_DB_TIMEOUT", "timeout", id="db-timeout"),
pytest.param(
"PAPERLESS_DB_POOLSIZE",
"pool.min_size / pool.max_size",
id="db-poolsize",
),
pytest.param("PAPERLESS_DBSSLMODE", "sslmode", id="ssl-mode"),
pytest.param("PAPERLESS_DBSSLROOTCERT", "sslrootcert", id="ssl-rootcert"),
pytest.param("PAPERLESS_DBSSLCERT", "sslcert", id="ssl-cert"),
pytest.param("PAPERLESS_DBSSLKEY", "sslkey", id="ssl-key"),
("PAPERLESS_DB_TIMEOUT", "timeout"),
("PAPERLESS_DB_POOLSIZE", "pool.min_size / pool.max_size"),
("PAPERLESS_DBSSLMODE", "sslmode"),
("PAPERLESS_DBSSLROOTCERT", "sslrootcert"),
("PAPERLESS_DBSSLCERT", "sslcert"),
("PAPERLESS_DBSSLKEY", "sslkey"),
],
ids=[
"db-timeout",
"db-poolsize",
"ssl-mode",
"ssl-rootcert",
"ssl-cert",
"ssl-key",
],
)
def test_single_deprecated_var_produces_one_warning(

View File

@@ -9,50 +9,35 @@ from paperless.utils import ocr_to_dateparser_languages
@pytest.mark.parametrize(
("ocr_language", "expected"),
[
pytest.param("eng", ["en"], id="single-language"),
pytest.param("fra+ita+lao", ["fr", "it", "lo"], id="multiple-languages"),
pytest.param("fil", ["fil"], id="no-two-letter-equivalent"),
pytest.param(
"aze_cyrl+srp_latn",
["az-Cyrl", "sr-Latn"],
id="script-supported-by-dateparser",
),
pytest.param(
"deu_frak",
["de"],
id="script-not-supported-falls-back-to-language",
),
pytest.param(
"chi_tra+chi_sim",
["zh"],
id="chinese-variants-collapse-to-general",
),
pytest.param(
"eng+unsupported_language+por",
["en", "pt"],
id="unsupported-language-skipped",
),
pytest.param(
"unsupported1+unsupported2",
[],
id="all-unsupported-returns-empty",
),
pytest.param("eng+eng", ["en"], id="duplicates-deduplicated"),
pytest.param(
"ita_unknownscript",
["it"],
id="unknown-script-falls-back-to-language",
),
# One language
("eng", ["en"]),
# Multiple languages
("fra+ita+lao", ["fr", "it", "lo"]),
# Languages that don't have a two-letter equivalent
("fil", ["fil"]),
# Languages with a script part supported by dateparser
("aze_cyrl+srp_latn", ["az-Cyrl", "sr-Latn"]),
# Languages with a script part not supported by dateparser
# In this case, default to the language without script
("deu_frak", ["de"]),
# Traditional and simplified chinese don't have the same name in dateparser,
# so they're converted to the general chinese language
("chi_tra+chi_sim", ["zh"]),
# If a language is not supported by dateparser, fallback to the supported ones
("eng+unsupported_language+por", ["en", "pt"]),
# If no language is supported, fallback to default
("unsupported1+unsupported2", []),
# Duplicate languages, should not duplicate in result
("eng+eng", ["en"]),
# Language with script, but script is not mapped
("ita_unknownscript", ["it"]),
],
)
def test_ocr_to_dateparser_languages(ocr_language: str, expected: list[str]) -> None:
def test_ocr_to_dateparser_languages(ocr_language, expected):
assert sorted(ocr_to_dateparser_languages(ocr_language)) == sorted(expected)
def test_ocr_to_dateparser_languages_exception(
monkeypatch: pytest.MonkeyPatch,
caplog: pytest.LogCaptureFixture,
) -> None:
def test_ocr_to_dateparser_languages_exception(monkeypatch, caplog):
# Patch LocaleDataLoader.get_locale_map to raise an exception
class DummyLoader:
def get_locale_map(self, locales=None):

View File

@@ -1,31 +1,24 @@
import tempfile
from pathlib import Path
from django.test import Client
from pytest_django.fixtures import SettingsWrapper
from django.test import override_settings
def test_favicon_view(
client: Client,
tmp_path: Path,
settings: SettingsWrapper,
) -> None:
favicon_path = tmp_path / "paperless" / "img" / "favicon.ico"
favicon_path.parent.mkdir(parents=True)
favicon_path.write_bytes(b"FAKE ICON DATA")
def test_favicon_view(client):
with tempfile.TemporaryDirectory() as tmpdir:
static_dir = Path(tmpdir)
favicon_path = static_dir / "paperless" / "img" / "favicon.ico"
favicon_path.parent.mkdir(parents=True, exist_ok=True)
favicon_path.write_bytes(b"FAKE ICON DATA")
settings.STATIC_ROOT = tmp_path
response = client.get("/favicon.ico")
assert response.status_code == 200
assert response["Content-Type"] == "image/x-icon"
assert b"".join(response.streaming_content) == b"FAKE ICON DATA"
with override_settings(STATIC_ROOT=static_dir):
response = client.get("/favicon.ico")
assert response.status_code == 200
assert response["Content-Type"] == "image/x-icon"
assert b"".join(response.streaming_content) == b"FAKE ICON DATA"
def test_favicon_view_missing_file(
client: Client,
tmp_path: Path,
settings: SettingsWrapper,
) -> None:
settings.STATIC_ROOT = tmp_path
response = client.get("/favicon.ico")
assert response.status_code == 404
def test_favicon_view_missing_file(client):
with override_settings(STATIC_ROOT=Path(tempfile.mkdtemp())):
response = client.get("/favicon.ico")
assert response.status_code == 404