From 7ff9bcb707af0c8bcdf55ed4fe29c2ea438748ae Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Sun, 10 Nov 2019 16:53:29 -0400 Subject: [PATCH 01/14] Add support for auto importing dependecies - No need to use: from .dependencies import * # NOQA Signed-off-by: Roberto Rosario --- HISTORY.rst | 6 ++++++ mayan/apps/appearance/apps.py | 2 -- mayan/apps/autoadmin/apps.py | 1 - mayan/apps/cabinets/apps.py | 1 - mayan/apps/common/apps.py | 1 - mayan/apps/converter/apps.py | 1 - mayan/apps/dependencies/apps.py | 3 ++- mayan/apps/dependencies/classes.py | 14 ++++++++++++++ mayan/apps/django_gpg/apps.py | 1 - mayan/apps/document_parsing/apps.py | 1 - mayan/apps/document_states/apps.py | 1 - mayan/apps/events/apps.py | 1 - mayan/apps/file_metadata/apps.py | 1 - mayan/apps/mayan_statistics/apps.py | 1 - mayan/apps/metadata/apps.py | 1 - mayan/apps/mimetype/apps.py | 2 -- mayan/apps/mirroring/apps.py | 1 - mayan/apps/ocr/apps.py | 1 - mayan/apps/rest_api/apps.py | 1 - mayan/apps/sources/apps.py | 1 - mayan/apps/tags/apps.py | 1 - mayan/apps/task_manager/apps.py | 2 +- 22 files changed, 23 insertions(+), 22 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 29860aa690..73a7181ca5 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -1,3 +1,9 @@ +3.2.10 (2019-XX-XX) +=================== +- Auto-import dependecies. No need to use: + from .dependencies import * # NOQA + + 3.2.9 (2019-11-03) ================== - Move IMAPMockServer to its own module. diff --git a/mayan/apps/appearance/apps.py b/mayan/apps/appearance/apps.py index cd345d4acb..dbd480a203 100644 --- a/mayan/apps/appearance/apps.py +++ b/mayan/apps/appearance/apps.py @@ -4,8 +4,6 @@ from django.utils.translation import ugettext_lazy as _ from mayan.apps.common.apps import MayanAppConfig -from .dependencies import * # NOQA - class AppearanceApp(MayanAppConfig): name = 'mayan.apps.appearance' diff --git a/mayan/apps/autoadmin/apps.py b/mayan/apps/autoadmin/apps.py index e1224fef9f..18596da92a 100644 --- a/mayan/apps/autoadmin/apps.py +++ b/mayan/apps/autoadmin/apps.py @@ -6,7 +6,6 @@ from django.utils.translation import ugettext_lazy as _ from mayan.apps.common.apps import MayanAppConfig -from .dependencies import * # NOQA from .handlers import handler_auto_admin_account_password_change diff --git a/mayan/apps/cabinets/apps.py b/mayan/apps/cabinets/apps.py index 6011bd4874..65c3955067 100644 --- a/mayan/apps/cabinets/apps.py +++ b/mayan/apps/cabinets/apps.py @@ -18,7 +18,6 @@ from mayan.apps.events.permissions import permission_events_view from mayan.apps.documents.search import document_page_search, document_search from mayan.apps.navigation.classes import SourceColumn -from .dependencies import * # NOQA from .events import ( event_cabinet_edited, event_cabinet_add_document, event_cabinet_remove_document diff --git a/mayan/apps/common/apps.py b/mayan/apps/common/apps.py index fffbd109f3..dc37935ae2 100644 --- a/mayan/apps/common/apps.py +++ b/mayan/apps/common/apps.py @@ -16,7 +16,6 @@ from django.utils.module_loading import import_string from django.utils.translation import ugettext_lazy as _ from .classes import Template -from .dependencies import * # NOQA from .handlers import ( handler_pre_initial_setup, handler_pre_upgrade, handler_user_locale_profile_session_config, handler_user_locale_profile_create diff --git a/mayan/apps/converter/apps.py b/mayan/apps/converter/apps.py index af8a668c6f..c13c7239e0 100644 --- a/mayan/apps/converter/apps.py +++ b/mayan/apps/converter/apps.py @@ -7,7 +7,6 @@ from mayan.apps.common.apps import MayanAppConfig from mayan.apps.common.menus import menu_object, menu_secondary from mayan.apps.navigation.classes import SourceColumn -from .dependencies import * # NOQA from .links import ( link_transformation_create, link_transformation_delete, link_transformation_edit diff --git a/mayan/apps/dependencies/apps.py b/mayan/apps/dependencies/apps.py index 8fb528921f..35000e2c3f 100644 --- a/mayan/apps/dependencies/apps.py +++ b/mayan/apps/dependencies/apps.py @@ -10,7 +10,6 @@ from mayan.apps.common.html_widgets import TwoStateWidget from mayan.apps.navigation.classes import SourceColumn from .classes import Dependency, DependencyGroup, DependencyGroupEntry -from .dependencies import * # NOQA from .links import ( link_check_version, link_dependency_group_entry_detail, link_dependency_group_entry_list, link_dependency_group_list, @@ -29,6 +28,8 @@ class DependenciesApp(MayanAppConfig): def ready(self): super(DependenciesApp, self).ready() + Dependency.initialize() + SourceColumn( attribute='get_label', label=_('Label'), order=-1, source=Dependency ) diff --git a/mayan/apps/dependencies/classes.py b/mayan/apps/dependencies/classes.py index 95caf74d12..02d3f07bb9 100644 --- a/mayan/apps/dependencies/classes.py +++ b/mayan/apps/dependencies/classes.py @@ -2,6 +2,8 @@ from __future__ import print_function, unicode_literals import fileinput import json +from importlib import import_module +import logging import pkg_resources import shutil import sys @@ -158,6 +160,18 @@ class DependencyGroupEntry(object): class Dependency(object): _registry = {} + @staticmethod + def initialize(): + for app in apps.get_app_configs(): + try: + import_module('{}.dependencies'.format(app.name)) + except ImportError as exception: + if force_text(exception) not in ('No module named dependencies', 'No module named \'{}.dependencies\''.format(app.name)): + logger.error( + 'Error importing %s dependencies.py file; %s', app.name, + exception + ) + @staticmethod def return_sorted(dependencies): return sorted(dependencies, key=lambda x: x.get_label()) diff --git a/mayan/apps/django_gpg/apps.py b/mayan/apps/django_gpg/apps.py index 2d877d5405..1bfd48d2e7 100644 --- a/mayan/apps/django_gpg/apps.py +++ b/mayan/apps/django_gpg/apps.py @@ -12,7 +12,6 @@ from mayan.apps.common.menus import ( from mayan.apps.navigation.classes import SourceColumn from .classes import KeyStub -from .dependencies import * # NOQA from .links import ( link_key_delete, link_key_detail, link_key_download, link_key_query, link_key_receive, link_key_setup, link_key_upload, link_private_keys, diff --git a/mayan/apps/document_parsing/apps.py b/mayan/apps/document_parsing/apps.py index f481252e8b..c53b44b927 100644 --- a/mayan/apps/document_parsing/apps.py +++ b/mayan/apps/document_parsing/apps.py @@ -17,7 +17,6 @@ from mayan.apps.documents.signals import post_version_upload from mayan.apps.events.classes import ModelEventType from mayan.apps.navigation.classes import SourceColumn -from .dependencies import * # NOQA from .events import ( event_parsing_document_content_deleted, event_parsing_document_version_submit, diff --git a/mayan/apps/document_states/apps.py b/mayan/apps/document_states/apps.py index 84625d9072..4e69e0d474 100644 --- a/mayan/apps/document_states/apps.py +++ b/mayan/apps/document_states/apps.py @@ -23,7 +23,6 @@ from mayan.apps.navigation.classes import SourceColumn from .classes import DocumentStateHelper, WorkflowAction from .events import event_workflow_edited -from .dependencies import * # NOQA from .handlers import ( handler_index_document, handler_launch_workflow, handler_trigger_transition ) diff --git a/mayan/apps/events/apps.py b/mayan/apps/events/apps.py index 164f301131..418cf2371c 100644 --- a/mayan/apps/events/apps.py +++ b/mayan/apps/events/apps.py @@ -10,7 +10,6 @@ from mayan.apps.common.menus import ( ) from mayan.apps.navigation.classes import SourceColumn -from .dependencies import * # NOQA from .html_widgets import ( ObjectLinkWidget, widget_event_actor_link, widget_event_type_link ) diff --git a/mayan/apps/file_metadata/apps.py b/mayan/apps/file_metadata/apps.py index a9830d467d..5bae454af2 100644 --- a/mayan/apps/file_metadata/apps.py +++ b/mayan/apps/file_metadata/apps.py @@ -18,7 +18,6 @@ from mayan.apps.events.classes import ModelEventType from mayan.apps.navigation.classes import SourceColumn from .classes import FileMetadataHelper -from .dependencies import * # NOQA from .drivers import * # NOQA from .events import ( event_file_metadata_document_version_finish, diff --git a/mayan/apps/mayan_statistics/apps.py b/mayan/apps/mayan_statistics/apps.py index 3c6880e3c0..e155abccbd 100644 --- a/mayan/apps/mayan_statistics/apps.py +++ b/mayan/apps/mayan_statistics/apps.py @@ -7,7 +7,6 @@ from mayan.apps.common.menus import menu_object, menu_secondary, menu_tools from mayan.apps.navigation.classes import SourceColumn from .classes import StatisticLineChart, StatisticNamespace -from .dependencies import * # NOQA from .links import ( link_execute, link_namespace_details, link_namespace_list, link_statistics, link_view diff --git a/mayan/apps/metadata/apps.py b/mayan/apps/metadata/apps.py index 44fd6bb546..1a3a988bd7 100644 --- a/mayan/apps/metadata/apps.py +++ b/mayan/apps/metadata/apps.py @@ -26,7 +26,6 @@ from mayan.apps.events.permissions import permission_events_view from mayan.apps.navigation.classes import SourceColumn from .classes import DocumentMetadataHelper -from .dependencies import * # NOQA from .events import ( event_document_metadata_added, event_document_metadata_edited, event_document_metadata_removed, event_metadata_type_edited, diff --git a/mayan/apps/mimetype/apps.py b/mayan/apps/mimetype/apps.py index 65fec9814e..0d1fd111b1 100644 --- a/mayan/apps/mimetype/apps.py +++ b/mayan/apps/mimetype/apps.py @@ -4,8 +4,6 @@ from django.utils.translation import ugettext_lazy as _ from mayan.apps.common.apps import MayanAppConfig -from .dependencies import * # NOQA - class MIMETypesApp(MayanAppConfig): name = 'mayan.apps.mimetype' diff --git a/mayan/apps/mirroring/apps.py b/mayan/apps/mirroring/apps.py index 81d894c3b0..e0594bec7b 100644 --- a/mayan/apps/mirroring/apps.py +++ b/mayan/apps/mirroring/apps.py @@ -6,7 +6,6 @@ from django.utils.translation import ugettext_lazy as _ from mayan.apps.common.apps import MayanAppConfig -from .dependencies import * # NOQA from .handlers import handler_document_cache_delete, handler_node_cache_delete diff --git a/mayan/apps/ocr/apps.py b/mayan/apps/ocr/apps.py index 1636761ca8..6b21094c7c 100644 --- a/mayan/apps/ocr/apps.py +++ b/mayan/apps/ocr/apps.py @@ -17,7 +17,6 @@ from mayan.apps.documents.signals import post_version_upload from mayan.apps.events.classes import ModelEventType from mayan.apps.navigation.classes import SourceColumn -from .dependencies import * # NOQA from .events import ( event_ocr_document_content_deleted, event_ocr_document_version_finish, event_ocr_document_version_submit diff --git a/mayan/apps/rest_api/apps.py b/mayan/apps/rest_api/apps.py index 9601345e20..49c61b2c9c 100644 --- a/mayan/apps/rest_api/apps.py +++ b/mayan/apps/rest_api/apps.py @@ -8,7 +8,6 @@ from django.utils.translation import ugettext_lazy as _ from mayan.apps.common.apps import MayanAppConfig from mayan.apps.common.menus import menu_tools -from .dependencies import * # NOQA from .links import ( link_api, link_api_documentation, link_api_documentation_redoc ) diff --git a/mayan/apps/sources/apps.py b/mayan/apps/sources/apps.py index f74954b481..3ad959da42 100644 --- a/mayan/apps/sources/apps.py +++ b/mayan/apps/sources/apps.py @@ -15,7 +15,6 @@ from mayan.apps.documents.signals import post_version_upload from mayan.apps.navigation.classes import SourceColumn from .classes import StagingFile -from .dependencies import * # NOQA from .handlers import ( handler_copy_transformations_to_version, handler_create_default_document_source, handler_initialize_periodic_tasks diff --git a/mayan/apps/tags/apps.py b/mayan/apps/tags/apps.py index c64811558a..172e27c7ce 100644 --- a/mayan/apps/tags/apps.py +++ b/mayan/apps/tags/apps.py @@ -21,7 +21,6 @@ from mayan.apps.events.links import ( from mayan.apps.events.permissions import permission_events_view from mayan.apps.navigation.classes import SourceColumn -from .dependencies import * # NOQA from .events import ( event_tag_attach, event_tag_edited, event_tag_remove ) diff --git a/mayan/apps/task_manager/apps.py b/mayan/apps/task_manager/apps.py index 52a8985982..6c5840883a 100644 --- a/mayan/apps/task_manager/apps.py +++ b/mayan/apps/task_manager/apps.py @@ -8,12 +8,12 @@ from mayan.apps.common.menus import menu_object, menu_secondary, menu_tools from mayan.apps.navigation.classes import SourceColumn from .classes import CeleryQueue, Task -from .dependencies import * # NOQA from .links import ( link_queue_list, link_queue_active_task_list, link_queue_scheduled_task_list, link_queue_reserved_task_list, link_task_manager ) +from .links import link_task_manager class TaskManagerApp(MayanAppConfig): From 680a4875f5c1f01dadbc8acc25fb9dbc958b3095 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Sun, 10 Nov 2019 00:43:46 -0400 Subject: [PATCH 02/14] Clean up docs config module - Add substitution for the Mayan EDMS container image version. Signed-off-by: Roberto Rosario --- docs/conf.py | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 796d2e5e27..f2d5458e5c 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -263,13 +263,22 @@ extlinks = { } -def _load_env_file(filename='../config.env'): - result = [] +def load_env_file(filename='../config.env'): + result = {} with open(filename) as file_object: for line in file_object: if not line.startswith('#'): key, value = line.strip().split('=') - result.append(('|{}|'.format(key), value)) + result[key] = value + + return result + + +def generate_substitutions(dictionary): + result = [] + + for key, value in dictionary.items(): + result.append(('|{}|'.format(key), value)) return result @@ -279,9 +288,9 @@ def global_substitution_function(app, docname, source): source[0] = source[0].replace(old, new) -def monkey_path_include(): +def monkey_patch_include(): """ - Monkey path docutil's Include directive to support global substitutions. + Monkey patch docutil's Include directive to support global substitutions. The Include class doesn't have a hook to modify the content before inserting it back, so we add a call to our own transformation method. We patch the base Include class, recreate Sphinx's Include class, @@ -312,7 +321,11 @@ def monkey_path_include(): def setup(app): app.add_stylesheet('css/custom.css') app.connect('source-read', global_substitution_function) - directives.register_directive('include', monkey_path_include()) + directives.register_directive('include', monkey_patch_include()) -global_subtitutions_list = _load_env_file() +environment_variables = load_env_file() +environment_variables['DOCKER_MAYAN_IMAGE_VERSION'] = mayan.__version__ +global_subtitutions_list = generate_substitutions( + dictionary=environment_variables +) From 15dc4e848930371c11c878ebebb1da6de6382aeb Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Sun, 10 Nov 2019 01:01:17 -0400 Subject: [PATCH 03/14] Organize global substitution code Signed-off-by: Roberto Rosario --- docs/__init__.py | 0 docs/callbacks.py | 8 +++++ docs/conf.py | 88 +++++++++++------------------------------------ docs/patches.py | 36 +++++++++++++++++++ docs/utils.py | 21 +++++++++++ 5 files changed, 86 insertions(+), 67 deletions(-) create mode 100644 docs/__init__.py create mode 100644 docs/callbacks.py create mode 100644 docs/patches.py create mode 100644 docs/utils.py diff --git a/docs/__init__.py b/docs/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/callbacks.py b/docs/callbacks.py new file mode 100644 index 0000000000..e6434d9c9a --- /dev/null +++ b/docs/callbacks.py @@ -0,0 +1,8 @@ +from __future__ import unicode_literals + + +def get_source_read_callback(substitutions): + def global_substitution_function(app, docname, source): + for old, new in substitutions: + source[0] = source[0].replace(old, new) + return global_substitution_function diff --git a/docs/conf.py b/docs/conf.py index f2d5458e5c..a0ec0a5f37 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -14,17 +14,19 @@ from __future__ import unicode_literals # serve to show the default. import os -import inspect import sys from docutils.parsers.rst import directives -import docutils.parsers.rst.directives.misc -from sphinx.directives.other import Include as SphinxInclude sys.path.insert(0, os.path.abspath('..')) +sys.path.insert(1, os.path.abspath('.')) import mayan +import callbacks +import patches +import utils + # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. @@ -263,69 +265,21 @@ extlinks = { } -def load_env_file(filename='../config.env'): - result = {} - with open(filename) as file_object: - for line in file_object: - if not line.startswith('#'): - key, value = line.strip().split('=') - result[key] = value - - return result - - -def generate_substitutions(dictionary): - result = [] - - for key, value in dictionary.items(): - result.append(('|{}|'.format(key), value)) - - return result - - -def global_substitution_function(app, docname, source): - for old, new in global_subtitutions_list: - source[0] = source[0].replace(old, new) - - -def monkey_patch_include(): - """ - Monkey patch docutil's Include directive to support global substitutions. - The Include class doesn't have a hook to modify the content before - inserting it back, so we add a call to our own transformation - method. We patch the base Include class, recreate Sphinx's Include class, - and register it as the new main include directive. - All this avoids copy and paste of the original code here. - """ - source_code = ''.join(inspect.getsourcelines( - docutils.parsers.rst.directives.misc.Include)[0] - ) - source_code = source_code.replace( - 'self.state_machine.insert_input(include_lines, path)', - 'include_lines=self.global_substitution(lines=include_lines)\n self.state_machine.insert_input(include_lines, path)', - ) - exec(source_code, docutils.parsers.rst.directives.misc.__dict__) - - class Include(docutils.parsers.rst.directives.misc.Include, SphinxInclude): - def global_substitution(self, lines): - result = [] - for line in lines: - for old, new in global_subtitutions_list: - line = line.replace(old, new) - result.append(line) - return result - - return Include - - def setup(app): - app.add_stylesheet('css/custom.css') - app.connect('source-read', global_substitution_function) - directives.register_directive('include', monkey_patch_include()) + environment_variables = utils.load_env_file() + environment_variables['DOCKER_MAYAN_IMAGE_VERSION'] = mayan.__version__ + substitutions = utils.generate_substitutions( + dictionary=environment_variables + ) - -environment_variables = load_env_file() -environment_variables['DOCKER_MAYAN_IMAGE_VERSION'] = mayan.__version__ -global_subtitutions_list = generate_substitutions( - dictionary=environment_variables -) + app.add_stylesheet(filename='css/custom.css') + app.connect( + event='source-read', callback=callbacks.get_source_read_callback( + substitutions=substitutions + ) + ) + directives.register_directive( + name='include', directive=patches.monkey_patch_include( + substitutions=substitutions + ) + ) diff --git a/docs/patches.py b/docs/patches.py new file mode 100644 index 0000000000..ad8d31d733 --- /dev/null +++ b/docs/patches.py @@ -0,0 +1,36 @@ +from __future__ import unicode_literals + +import inspect + +import docutils.parsers.rst.directives.misc +from sphinx.directives.other import Include as SphinxInclude + + +def monkey_patch_include(substitutions): + """ + Monkey patch docutil's Include directive to support global substitutions. + The Include class doesn't have a hook to modify the content before + inserting it back, so we add a call to our own transformation + method. We patch the base Include class, recreate Sphinx's Include class, + and register it as the new main include directive. + All this avoids copy and paste of the original code here. + """ + source_code = ''.join(inspect.getsourcelines( + docutils.parsers.rst.directives.misc.Include)[0] + ) + source_code = source_code.replace( + 'self.state_machine.insert_input(include_lines, path)', + 'include_lines=self.global_substitution(lines=include_lines)\n self.state_machine.insert_input(include_lines, path)', + ) + exec(source_code, docutils.parsers.rst.directives.misc.__dict__) + + class Include(docutils.parsers.rst.directives.misc.Include, SphinxInclude): + def global_substitution(self, lines): + result = [] + for line in lines: + for old, new in substitutions: + line = line.replace(old, new) + result.append(line) + return result + + return Include diff --git a/docs/utils.py b/docs/utils.py new file mode 100644 index 0000000000..bed070d934 --- /dev/null +++ b/docs/utils.py @@ -0,0 +1,21 @@ +from __future__ import unicode_literals + + +def load_env_file(filename='../config.env'): + result = {} + with open(filename) as file_object: + for line in file_object: + if not line.startswith('#'): + key, value = line.strip().split('=') + result[key] = value + + return result + + +def generate_substitutions(dictionary): + result = [] + + for key, value in dictionary.items(): + result.append(('|{}|'.format(key), value)) + + return result From 8ce4d5fb30e568fc6c7350c2f0c12c6bdfab6ca5 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Mon, 11 Nov 2019 02:14:00 -0400 Subject: [PATCH 04/14] Add target to run all tests in debug mode Signed-off-by: Roberto Rosario --- Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Makefile b/Makefile index f49736c6dd..760a6566fb 100644 --- a/Makefile +++ b/Makefile @@ -32,6 +32,10 @@ test-all: ## Run all tests. test-all: clean-pyc ./manage.py test --mayan-apps --settings=mayan.settings.testing.development --nomigrations $(ARGUMENTS) +test-all-debug: ## Run all tests in debug mode. +test-all-debug: clean-pyc + ./manage.py test --mayan-apps --settings=mayan.settings.testing.development --nomigrations --debug-mode $(ARGUMENTS) + test-launch-postgres: @docker rm -f test-postgres || true @docker volume rm test-postgres || true From 05ceeca8fff9041566e687752767f124218e744a Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Mon, 11 Nov 2019 02:20:58 -0400 Subject: [PATCH 05/14] Allow multitle expected_content_types in tests Some tests return 'text/html' or 'text/html; charset=utf-8' which are essentially the same if they are tested in debug mode. Signed-off-by: Roberto Rosario --- HISTORY.rst | 6 ++++ mayan/apps/common/tests/mixins.py | 10 +++--- mayan/apps/django_gpg/tests/test_views.py | 2 +- .../apps/document_parsing/tests/test_views.py | 2 +- .../documents/tests/test_document_views.py | 32 ++++++++++++------- mayan/apps/documents/tests/test_events.py | 2 +- mayan/apps/ocr/tests/test_views.py | 2 +- mayan/apps/rest_api/tests/base.py | 2 +- 8 files changed, 36 insertions(+), 22 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 73a7181ca5..ac4d722229 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -2,6 +2,12 @@ =================== - Auto-import dependecies. No need to use: from .dependencies import * # NOQA +- Add makefile target to run all tests in debug mode. + This mode is more strict and sidesteps a Django bug that + causes errors in the template code that to be silent during + tests. +- Rename expected_content_type to expected_content_types + and allow a list of content types to be specified. 3.2.9 (2019-11-03) diff --git a/mayan/apps/common/tests/mixins.py b/mayan/apps/common/tests/mixins.py index afb9501fde..83013acaf8 100644 --- a/mayan/apps/common/tests/mixins.py +++ b/mayan/apps/common/tests/mixins.py @@ -104,7 +104,7 @@ class ConnectionsCheckTestCaseMixin(object): class ContentTypeCheckTestCaseMixin(object): - expected_content_type = 'text/html; charset=utf-8' + expected_content_types = ('text/html', 'text/html; charset=utf-8') def _pre_setup(self): super(ContentTypeCheckTestCaseMixin, self)._pre_setup() @@ -115,11 +115,11 @@ class ContentTypeCheckTestCaseMixin(object): response = super(CustomClient, self).request(*args, **kwargs) content_type = response._headers.get('content-type', [None, ''])[1] - if test_instance.expected_content_type: - test_instance.assertEqual( - content_type, test_instance.expected_content_type, + if test_instance.expected_content_types: + test_instance.assertTrue( + content_type in test_instance.expected_content_types, msg='Unexpected response content type: {}, expected: {}.'.format( - content_type, test_instance.expected_content_type + content_type, ' or '.join(test_instance.expected_content_types) ) ) diff --git a/mayan/apps/django_gpg/tests/test_views.py b/mayan/apps/django_gpg/tests/test_views.py index 5bc46a0b70..7c2c7f2754 100644 --- a/mayan/apps/django_gpg/tests/test_views.py +++ b/mayan/apps/django_gpg/tests/test_views.py @@ -31,7 +31,7 @@ class KeyViewTestCase(KeyTestMixin, KeyViewTestMixin, GenericViewTestCase): self.assertEqual(response.status_code, 403) def test_key_download_view_with_permission(self): - self.expected_content_type = 'application/octet-stream; charset=utf-8' + self.expected_content_types = ('application/octet-stream; charset=utf-8',) self._create_test_key() diff --git a/mayan/apps/document_parsing/tests/test_views.py b/mayan/apps/document_parsing/tests/test_views.py index e7792dfd70..13856cdadf 100644 --- a/mayan/apps/document_parsing/tests/test_views.py +++ b/mayan/apps/document_parsing/tests/test_views.py @@ -111,7 +111,7 @@ class DocumentContentViewsTestCase( self.assertEqual(response.status_code, 403) def test_download_view_with_access(self): - self.expected_content_type = 'application/octet-stream; charset=utf-8' + self.expected_content_types = ('application/octet-stream; charset=utf-8',) self.grant_access( permission=permission_content_view, obj=self.test_document ) diff --git a/mayan/apps/documents/tests/test_document_views.py b/mayan/apps/documents/tests/test_document_views.py index bd57dc905e..8ade3d7efd 100644 --- a/mayan/apps/documents/tests/test_document_views.py +++ b/mayan/apps/documents/tests/test_document_views.py @@ -186,10 +186,12 @@ class DocumentsViewsTestCase(DocumentViewTestMixin, GenericDocumentViewTestCase) self.assertEqual(response.status_code, 403) def test_document_download_view_with_permission(self): - # Set the expected_content_type for + # Set the expected_content_types for # common.tests.mixins.ContentTypeCheckMixin - self.expected_content_type = '{}; charset=utf-8'.format( - self.test_document.file_mimetype + self.expected_content_types = ( + '{}; charset=utf-8'.format( + self.test_document.file_mimetype + ), ) self.grant_access( @@ -211,10 +213,12 @@ class DocumentsViewsTestCase(DocumentViewTestMixin, GenericDocumentViewTestCase) self.assertEqual(response.status_code, 403) def test_document_multiple_download_view_with_permission(self): - # Set the expected_content_type for + # Set the expected_content_types for # common.tests.mixins.ContentTypeCheckMixin - self.expected_content_type = '{}; charset=utf-8'.format( - self.test_document.file_mimetype + self.expected_content_types = ( + '{}; charset=utf-8'.format( + self.test_document.file_mimetype + ), ) self.grant_access( obj=self.test_document, permission=permission_document_download @@ -235,10 +239,12 @@ class DocumentsViewsTestCase(DocumentViewTestMixin, GenericDocumentViewTestCase) self.assertEqual(response.status_code, 403) def test_document_version_download_view_with_permission(self): - # Set the expected_content_type for + # Set the expected_content_types for # common.tests.mixins.ContentTypeCheckMixin - self.expected_content_type = '{}; charset=utf-8'.format( - self.test_document.latest_version.mimetype + self.expected_content_types = ( + '{}; charset=utf-8'.format( + self.test_document.latest_version.mimetype + ), ) self.grant_access( @@ -258,10 +264,12 @@ class DocumentsViewsTestCase(DocumentViewTestMixin, GenericDocumentViewTestCase) ) def test_document_version_download_preserve_extension_view_with_permission(self): - # Set the expected_content_type for + # Set the expected_content_types for # common.tests.mixins.ContentTypeCheckMixin - self.expected_content_type = '{}; charset=utf-8'.format( - self.test_document.latest_version.mimetype + self.expected_content_types = ( + '{}; charset=utf-8'.format( + self.test_document.latest_version.mimetype + ), ) self.grant_access( diff --git a/mayan/apps/documents/tests/test_events.py b/mayan/apps/documents/tests/test_events.py index 2f9428054b..7f7594356c 100644 --- a/mayan/apps/documents/tests/test_events.py +++ b/mayan/apps/documents/tests/test_events.py @@ -32,7 +32,7 @@ class DocumentEventsTestCase(GenericDocumentViewTestCase): self.assertEqual(list(Action.objects.any(obj=self.test_document)), []) def test_document_download_event_with_permissions(self): - self.expected_content_type = 'image/png; charset=utf-8' + self.expected_content_types = ('image/png; charset=utf-8',) Action.objects.all().delete() diff --git a/mayan/apps/ocr/tests/test_views.py b/mayan/apps/ocr/tests/test_views.py index 49b865189d..2935039a98 100644 --- a/mayan/apps/ocr/tests/test_views.py +++ b/mayan/apps/ocr/tests/test_views.py @@ -173,7 +173,7 @@ class OCRViewsTestCase(OCRViewTestMixin, GenericDocumentViewTestCase): def test_document_ocr_download_view_with_access(self): self.test_document.submit_for_ocr() - self.expected_content_type = 'application/octet-stream; charset=utf-8' + self.expected_content_types = ('application/octet-stream; charset=utf-8',) self.grant_access( obj=self.test_document, permission=permission_ocr_content_view diff --git a/mayan/apps/rest_api/tests/base.py b/mayan/apps/rest_api/tests/base.py index 114549d89e..8f77430952 100644 --- a/mayan/apps/rest_api/tests/base.py +++ b/mayan/apps/rest_api/tests/base.py @@ -11,7 +11,7 @@ class BaseAPITestCase(APITestCase, GenericViewTestCase): """ API test case class that invalidates permissions and smart settings """ - expected_content_type = None + expected_content_types = None def setUp(self): super(BaseAPITestCase, self).setUp() From 03e4978106a67888339a24bbbe158e6f8859cc25 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Mon, 11 Nov 2019 02:25:44 -0400 Subject: [PATCH 06/14] Add target to run specific tests in debug mode Signed-off-by: Roberto Rosario --- Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Makefile b/Makefile index 760a6566fb..194df3a46f 100644 --- a/Makefile +++ b/Makefile @@ -28,6 +28,10 @@ test: clean-pyc test: ## MODULE= - Run tests for a single app, module or test class. ./manage.py test $(MODULE) --settings=mayan.settings.testing.development --nomigrations $(ARGUMENTS) +test-debug: clean-pyc +test-debug: ## MODULE= - Run tests for a single app, module or test class, in debug mode. + ./manage.py test $(MODULE) --settings=mayan.settings.testing.development --nomigrations --debug-mode $(ARGUMENTS) + test-all: ## Run all tests. test-all: clean-pyc ./manage.py test --mayan-apps --settings=mayan.settings.testing.development --nomigrations $(ARGUMENTS) From 588ab4c728dca50294fa11b53d796a5f18ae7836 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Mon, 11 Nov 2019 02:12:04 -0400 Subject: [PATCH 07/14] Add document checkout view get test Signed-off-by: Roberto Rosario --- mayan/apps/checkouts/tests/mixins.py | 9 +- mayan/apps/checkouts/tests/test_views.py | 24 +- .../document_signatures/tests/test_views.py | 215 ++++++++++++++++++ 3 files changed, 243 insertions(+), 5 deletions(-) diff --git a/mayan/apps/checkouts/tests/mixins.py b/mayan/apps/checkouts/tests/mixins.py index 2e7d34adb8..731506a8c2 100644 --- a/mayan/apps/checkouts/tests/mixins.py +++ b/mayan/apps/checkouts/tests/mixins.py @@ -74,7 +74,14 @@ class DocumentCheckoutViewTestMixin(object): def _request_test_document_check_out_list_view(self): return self.get(viewname='checkouts:check_out_list') - def _request_test_document_check_out_view(self): + def _request_test_document_check_out_get_view(self): + return self.get( + viewname='checkouts:check_out_document', kwargs={ + 'pk': self.test_document.pk + }, + ) + + def _request_test_document_check_out_post_view(self): return self.post( viewname='checkouts:check_out_document', kwargs={ 'pk': self.test_document.pk diff --git a/mayan/apps/checkouts/tests/test_views.py b/mayan/apps/checkouts/tests/test_views.py index 8cae22a1f4..3258c5a351 100644 --- a/mayan/apps/checkouts/tests/test_views.py +++ b/mayan/apps/checkouts/tests/test_views.py @@ -67,13 +67,29 @@ class DocumentCheckoutViewTestCase( ) ) - def test_document_check_out_view_no_permission(self): - response = self._request_test_document_check_out_view() + def test_document_check_out_get_view_no_permission(self): + response = self._request_test_document_check_out_get_view() self.assertEqual(response.status_code, 403) self.assertFalse(self.test_document.is_checked_out()) - def test_document_check_out_view_with_access(self): + def test_document_check_out_get_view_with_access(self): + self.grant_access( + obj=self.test_document, permission=permission_document_check_out + ) + + response = self._request_test_document_check_out_get_view() + self.assertEqual(response.status_code, 200) + + self.assertFalse(self.test_document.is_checked_out()) + + def test_document_check_out_post_view_no_permission(self): + response = self._request_test_document_check_out_post_view() + self.assertEqual(response.status_code, 403) + + self.assertFalse(self.test_document.is_checked_out()) + + def test_document_check_out_post_view_with_access(self): self.grant_access( obj=self.test_document, permission=permission_document_check_out ) @@ -82,7 +98,7 @@ class DocumentCheckoutViewTestCase( permission=permission_document_check_out_detail_view ) - response = self._request_test_document_check_out_view() + response = self._request_test_document_check_out_post_view() self.assertEqual(response.status_code, 302) self.assertTrue(self.test_document.is_checked_out()) diff --git a/mayan/apps/document_signatures/tests/test_views.py b/mayan/apps/document_signatures/tests/test_views.py index 07c75aa6dd..b699dfcd0e 100644 --- a/mayan/apps/document_signatures/tests/test_views.py +++ b/mayan/apps/document_signatures/tests/test_views.py @@ -288,3 +288,218 @@ class SignaturesViewTestCase( EmbeddedSignature.objects.unsigned_document_versions().count(), TEST_UNSIGNED_DOCUMENT_COUNT ) + + +class DetachedSignaturesViewTestCase( + KeyTestMixin, SignatureTestMixin, DetachedSignatureViewTestMixin, + GenericDocumentViewTestCase +): + auto_upload_document = False + + def test_detached_signature_create_view_with_no_permission(self): + self.upload_document() + self._create_test_key_private() + + signatures = self.test_document.latest_version.signatures.count() + + response = self._request_test_document_version_signature_create_view() + self.assertEqual(response.status_code, 403) + + self.assertEqual( + self.test_document.latest_version.signatures.count(), + signatures + ) + + def test_detached_signature_create_view_with_document_access(self): + self.upload_document() + self._create_test_key_private() + + signatures = self.test_document.latest_version.signatures.count() + + self.grant_access( + obj=self.test_document, + permission=permission_document_version_sign_detached + ) + + response = self._request_test_document_version_signature_create_view() + self.assertEqual(response.status_code, 200) + + self.assertEqual( + self.test_document.latest_version.signatures.count(), + signatures + ) + + def test_detached_signature_create_view_with_key_access(self): + self.upload_document() + self._create_test_key_private() + + signatures = self.test_document.latest_version.signatures.count() + + self.grant_access( + obj=self.test_key_private, + permission=permission_key_sign + ) + + response = self._request_test_document_version_signature_create_view() + self.assertEqual(response.status_code, 403) + + self.assertEqual( + self.test_document.latest_version.signatures.count(), + signatures + ) + + def test_detached_signature_create_view_with_full_access(self): + self.upload_document() + self._create_test_key_private() + + signatures = self.test_document.latest_version.signatures.count() + + self.grant_access( + obj=self.test_document, + permission=permission_document_version_sign_detached + ) + self.grant_access( + obj=self.test_key_private, + permission=permission_key_sign + ) + + response = self._request_test_document_version_signature_create_view() + self.assertEqual(response.status_code, 302) + + self.assertEqual( + self.test_document.latest_version.signatures.count(), + signatures + 1 + ) + + def test_signature_download_view_no_permission(self): + self.test_document_path = TEST_DOCUMENT_PATH + self.upload_document() + + self._create_test_detached_signature() + + response = self._request_test_document_version_signature_download_view() + self.assertEqual(response.status_code, 403) + + def test_signature_download_view_with_access(self): + self.test_document_path = TEST_DOCUMENT_PATH + self.upload_document() + + self._create_test_detached_signature() + + self.grant_access( + obj=self.test_document, + permission=permission_document_version_signature_download + ) + + self.expected_content_types = ('application/octet-stream; charset=utf-8',) + + response = self._request_test_document_version_signature_download_view() + + with self.test_signature.signature_file as file_object: + assert_download_response( + self, response=response, content=file_object.read(), + ) + + def test_signature_upload_view_no_permission(self): + self.test_document_path = TEST_DOCUMENT_PATH + self.upload_document() + + response = self._request_test_document_version_signature_upload_view() + self.assertEqual(response.status_code, 403) + + self.assertEqual(DetachedSignature.objects.count(), 0) + + def test_signature_upload_view_with_access(self): + self.test_document_path = TEST_DOCUMENT_PATH + self.upload_document() + + self.grant_access( + obj=self.test_document, + permission=permission_document_version_signature_upload + ) + + response = self._request_test_document_version_signature_upload_view() + self.assertEqual(response.status_code, 302) + + self.assertEqual(DetachedSignature.objects.count(), 1) + + +class EmbeddedSignaturesViewTestCase( + KeyTestMixin, EmbeddedSignatureViewTestMixin, GenericDocumentViewTestCase +): + auto_upload_document = False + + def test_embedded_signature_create_view_with_no_permission(self): + self.upload_document() + self._create_test_key_private() + + signatures = self.test_document.latest_version.signatures.count() + + response = self._request_test_document_version_signature_create_view() + self.assertEqual(response.status_code, 403) + + self.assertEqual( + self.test_document.latest_version.signatures.count(), + signatures + ) + + def test_embedded_signature_create_view_with_document_access(self): + self.upload_document() + self._create_test_key_private() + + signatures = self.test_document.latest_version.signatures.count() + + self.grant_access( + obj=self.test_document, + permission=permission_document_version_sign_embedded + ) + + response = self._request_test_document_version_signature_create_view() + self.assertEqual(response.status_code, 200) + + self.assertEqual( + self.test_document.latest_version.signatures.count(), + signatures + ) + + def test_embedded_signature_create_view_with_key_access(self): + self.upload_document() + self._create_test_key_private() + + signatures = self.test_document.latest_version.signatures.count() + + self.grant_access( + obj=self.test_key_private, + permission=permission_key_sign + ) + + response = self._request_test_document_version_signature_create_view() + self.assertEqual(response.status_code, 403) + + self.assertEqual( + self.test_document.latest_version.signatures.count(), + signatures + ) + + def test_embedded_signature_create_view_with_full_access(self): + self.upload_document() + self._create_test_key_private() + + signatures = self.test_document.latest_version.signatures.count() + + self.grant_access( + obj=self.test_document, + permission=permission_document_version_sign_embedded + ) + self.grant_access( + obj=self.test_key_private, + permission=permission_key_sign + ) + + response = self._request_test_document_version_signature_create_view() + self.assertEqual(response.status_code, 302) + + self.assertEqual( + self.test_document.latest_version.signatures.count(), + signatures + 1 + ) From f0755a4cd9ca824a7d70a07303bd880994f55bf4 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Mon, 11 Nov 2019 19:15:44 -0400 Subject: [PATCH 08/14] Backport tests improvements - Test mixin improvements for django_gpg - Detached and embedded signatures and signing tests for the document signatures app. - Improved mixins in both. Signed-off-by: Roberto Rosario --- mayan/apps/django_gpg/tests/literals.py | 8 +-- mayan/apps/django_gpg/tests/mixins.py | 47 +++++++++++- mayan/apps/django_gpg/tests/test_api.py | 41 +++-------- mayan/apps/django_gpg/tests/test_models.py | 42 +++++------ mayan/apps/django_gpg/tests/test_views.py | 28 +++----- .../document_signatures/tests/literals.py | 4 +- .../apps/document_signatures/tests/mixins.py | 70 ++++++++++++++++-- .../document_signatures/tests/test_links.py | 6 +- .../document_signatures/tests/test_models.py | 52 +++++++------- .../document_signatures/tests/test_views.py | 71 ++++++------------- mayan/apps/documents/tests/mixins.py | 2 + 11 files changed, 208 insertions(+), 163 deletions(-) diff --git a/mayan/apps/django_gpg/tests/literals.py b/mayan/apps/django_gpg/tests/literals.py index c7dca32a6a..55782e1872 100644 --- a/mayan/apps/django_gpg/tests/literals.py +++ b/mayan/apps/django_gpg/tests/literals.py @@ -26,7 +26,7 @@ TEST_FILE = os.path.join( 'test_files', 'test_file.txt' ) -TEST_KEY_DATA = '''-----BEGIN PGP PRIVATE KEY BLOCK----- +TEST_KEY_PRIVATE_DATA = '''-----BEGIN PGP PRIVATE KEY BLOCK----- Version: GnuPG v1 lQO+BFbxfC8BCACnUZoD96W4+CSIaU9G8I08kXu2zJLzy2XgUtwLx8VQ8dOHr0E/ @@ -86,9 +86,9 @@ h4oCbUV5JHhOyB+89Y1w8haFU9LrgOER2kXff1xU6wMfLdcO5ApV/sRJcNdYL7Cg =JZ5G -----END PGP PRIVATE KEY BLOCK-----''' -TEST_KEY_ID = '4125E9C571F378AC' -TEST_KEY_FINGERPRINT = '6A24574E0A35004CDDFD22704125E9C571F378AC' -TEST_KEY_PASSPHRASE = 'testpassphrase' +TEST_KEY_PRIVATE_ID = '4125E9C571F378AC' +TEST_KEY_PRIVATE_FINGERPRINT = '6A24574E0A35004CDDFD22704125E9C571F378AC' +TEST_KEY_PRIVATE_PASSPHRASE = 'testpassphrase' TEST_KEYSERVERS = ['pool.sks-keyservers.net'] diff --git a/mayan/apps/django_gpg/tests/mixins.py b/mayan/apps/django_gpg/tests/mixins.py index 82bf007147..edf32185af 100644 --- a/mayan/apps/django_gpg/tests/mixins.py +++ b/mayan/apps/django_gpg/tests/mixins.py @@ -2,9 +2,50 @@ from __future__ import unicode_literals from ..models import Key -from .literals import TEST_KEY_DATA +from .literals import TEST_KEY_PRIVATE_DATA + + +class KeyAPIViewTestMixin(object): + def _request_test_key_create_view(self): + return self.post( + viewname='rest_api:key-list', data={ + 'key_data': TEST_KEY_PRIVATE_DATA + } + ) + + def _request_test_key_delete_view(self): + return self.delete( + viewname='rest_api:key-detail', kwargs={ + 'pk': self.test_key_private.pk + } + ) + + def _request_test_key_detail_view(self): + return self.get( + viewname='rest_api:key-detail', kwargs={ + 'pk': self.test_key_private.pk + } + ) class KeyTestMixin(object): - def _create_test_key(self): - self.test_key = Key.objects.create(key_data=TEST_KEY_DATA) + def _create_test_key_private(self): + self.test_key_private = Key.objects.create( + key_data=TEST_KEY_PRIVATE_DATA + ) + + +class KeyViewTestMixin(object): + def _request_test_key_download_view(self): + return self.get( + viewname='django_gpg:key_download', kwargs={ + 'pk': self.test_key_private.pk + } + ) + + def _request_test_key_upload_view(self): + return self.post( + viewname='django_gpg:key_upload', data={ + 'key_data': TEST_KEY_PRIVATE_DATA + } + ) diff --git a/mayan/apps/django_gpg/tests/test_api.py b/mayan/apps/django_gpg/tests/test_api.py index 1f976e1107..6ece51be0b 100644 --- a/mayan/apps/django_gpg/tests/test_api.py +++ b/mayan/apps/django_gpg/tests/test_api.py @@ -9,27 +9,8 @@ from ..permissions import ( permission_key_delete, permission_key_upload, permission_key_view ) -from .literals import TEST_KEY_DATA, TEST_KEY_FINGERPRINT -from .mixins import KeyTestMixin - - -class KeyAPIViewTestMixin(object): - def _request_test_key_create_view(self): - return self.post( - viewname='rest_api:key-list', data={ - 'key_data': TEST_KEY_DATA - } - ) - - def _request_test_key_delete_view(self): - return self.delete( - viewname='rest_api:key-detail', kwargs={'pk': self.test_key.pk} - ) - - def _request_test_key_detail_view(self): - return self.get( - viewname='rest_api:key-detail', kwargs={'pk': self.test_key.pk} - ) +from .literals import TEST_KEY_PRIVATE_FINGERPRINT +from .mixins import KeyAPIViewTestMixin, KeyTestMixin class KeyAPITestCase(KeyTestMixin, KeyAPIViewTestMixin, BaseAPITestCase): @@ -44,14 +25,14 @@ class KeyAPITestCase(KeyTestMixin, KeyAPIViewTestMixin, BaseAPITestCase): response = self._request_test_key_create_view() self.assertEqual(response.status_code, status.HTTP_201_CREATED) - self.assertEqual(response.data['fingerprint'], TEST_KEY_FINGERPRINT) + self.assertEqual(response.data['fingerprint'], TEST_KEY_PRIVATE_FINGERPRINT) key = Key.objects.first() self.assertEqual(Key.objects.count(), 1) - self.assertEqual(key.fingerprint, TEST_KEY_FINGERPRINT) + self.assertEqual(key.fingerprint, TEST_KEY_PRIVATE_FINGERPRINT) def test_key_delete_view_no_access(self): - self._create_test_key() + self._create_test_key_private() response = self._request_test_key_delete_view() self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) @@ -59,9 +40,9 @@ class KeyAPITestCase(KeyTestMixin, KeyAPIViewTestMixin, BaseAPITestCase): self.assertEqual(Key.objects.count(), 1) def test_key_delete_view_with_access(self): - self._create_test_key() + self._create_test_key_private() self.grant_access( - obj=self.test_key, permission=permission_key_delete + obj=self.test_key_private, permission=permission_key_delete ) response = self._request_test_key_delete_view() @@ -70,19 +51,19 @@ class KeyAPITestCase(KeyTestMixin, KeyAPIViewTestMixin, BaseAPITestCase): self.assertEqual(Key.objects.count(), 0) def test_key_detail_view_no_access(self): - self._create_test_key() + self._create_test_key_private() response = self._request_test_key_detail_view() self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) def test_key_detail_view_with_access(self): - self._create_test_key() + self._create_test_key_private() self.grant_access( - obj=self.test_key, permission=permission_key_view + obj=self.test_key_private, permission=permission_key_view ) response = self._request_test_key_detail_view() self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual( - response.data['fingerprint'], self.test_key.fingerprint + response.data['fingerprint'], self.test_key_private.fingerprint ) diff --git a/mayan/apps/django_gpg/tests/test_models.py b/mayan/apps/django_gpg/tests/test_models.py index fe02e338b2..624fe1358a 100644 --- a/mayan/apps/django_gpg/tests/test_models.py +++ b/mayan/apps/django_gpg/tests/test_models.py @@ -18,9 +18,9 @@ from ..models import Key from .literals import ( MOCK_SEARCH_KEYS_RESPONSE, TEST_DETACHED_SIGNATURE, TEST_FILE, - TEST_KEY_DATA, TEST_KEY_FINGERPRINT, TEST_KEY_PASSPHRASE, - TEST_SEARCH_FINGERPRINT, TEST_SEARCH_UID, TEST_SIGNED_FILE, - TEST_SIGNED_FILE_CONTENT + TEST_KEY_PRIVATE_DATA, TEST_KEY_PRIVATE_FINGERPRINT, + TEST_KEY_PRIVATE_PASSPHRASE, TEST_SEARCH_FINGERPRINT, TEST_SEARCH_UID, + TEST_SIGNED_FILE, TEST_SIGNED_FILE_CONTENT ) from .mocks import mock_recv_keys @@ -28,9 +28,9 @@ from .mocks import mock_recv_keys class KeyTestCase(BaseTestCase): def test_key_instance_creation(self): # Creating a Key instance is analogous to importing a key - key = Key.objects.create(key_data=TEST_KEY_DATA) + key = Key.objects.create(key_data=TEST_KEY_PRIVATE_DATA) - self.assertEqual(key.fingerprint, TEST_KEY_FINGERPRINT) + self.assertEqual(key.fingerprint, TEST_KEY_PRIVATE_FINGERPRINT) @mock.patch.object(gnupg.GPG, 'search_keys', autospec=True) def test_key_search(self, search_keys): @@ -69,36 +69,36 @@ class KeyTestCase(BaseTestCase): with open(TEST_SIGNED_FILE, mode='rb') as signed_file: result = Key.objects.verify_file(signed_file) - self.assertTrue(result.key_id in TEST_KEY_FINGERPRINT) + self.assertTrue(result.key_id in TEST_KEY_PRIVATE_FINGERPRINT) def test_embedded_verification_with_key(self): - Key.objects.create(key_data=TEST_KEY_DATA) + Key.objects.create(key_data=TEST_KEY_PRIVATE_DATA) with open(TEST_SIGNED_FILE, mode='rb') as signed_file: result = Key.objects.verify_file(signed_file) - self.assertEqual(result.fingerprint, TEST_KEY_FINGERPRINT) + self.assertEqual(result.fingerprint, TEST_KEY_PRIVATE_FINGERPRINT) def test_embedded_verification_with_correct_fingerprint(self): - Key.objects.create(key_data=TEST_KEY_DATA) + Key.objects.create(key_data=TEST_KEY_PRIVATE_DATA) with open(TEST_SIGNED_FILE, mode='rb') as signed_file: result = Key.objects.verify_file( - signed_file, key_fingerprint=TEST_KEY_FINGERPRINT + signed_file, key_fingerprint=TEST_KEY_PRIVATE_FINGERPRINT ) self.assertTrue(result.valid) - self.assertEqual(result.fingerprint, TEST_KEY_FINGERPRINT) + self.assertEqual(result.fingerprint, TEST_KEY_PRIVATE_FINGERPRINT) def test_embedded_verification_with_incorrect_fingerprint(self): - Key.objects.create(key_data=TEST_KEY_DATA) + Key.objects.create(key_data=TEST_KEY_PRIVATE_DATA) with open(TEST_SIGNED_FILE, mode='rb') as signed_file: with self.assertRaises(KeyDoesNotExist): Key.objects.verify_file(signed_file, key_fingerprint='999') def test_signed_file_decryption(self): - Key.objects.create(key_data=TEST_KEY_DATA) + Key.objects.create(key_data=TEST_KEY_PRIVATE_DATA) with open(TEST_SIGNED_FILE, mode='rb') as signed_file: result = Key.objects.decrypt_file(file_object=signed_file) @@ -122,10 +122,10 @@ class KeyTestCase(BaseTestCase): file_object=test_file, signature_file=signature_file ) - self.assertTrue(result.key_id in TEST_KEY_FINGERPRINT) + self.assertTrue(result.key_id in TEST_KEY_PRIVATE_FINGERPRINT) def test_detached_verification_with_key(self): - Key.objects.create(key_data=TEST_KEY_DATA) + Key.objects.create(key_data=TEST_KEY_PRIVATE_DATA) with open(TEST_DETACHED_SIGNATURE, mode='rb') as signature_file: with open(TEST_FILE, mode='rb') as test_file: @@ -134,10 +134,10 @@ class KeyTestCase(BaseTestCase): ) self.assertTrue(result) - self.assertEqual(result.fingerprint, TEST_KEY_FINGERPRINT) + self.assertEqual(result.fingerprint, TEST_KEY_PRIVATE_FINGERPRINT) def test_detached_signing_no_passphrase(self): - key = Key.objects.create(key_data=TEST_KEY_DATA) + key = Key.objects.create(key_data=TEST_KEY_PRIVATE_DATA) with self.assertRaises(NeedPassphrase): with open(TEST_FILE, mode='rb') as test_file: @@ -146,7 +146,7 @@ class KeyTestCase(BaseTestCase): ) def test_detached_signing_bad_passphrase(self): - key = Key.objects.create(key_data=TEST_KEY_DATA) + key = Key.objects.create(key_data=TEST_KEY_PRIVATE_DATA) with self.assertRaises(PassphraseError): with open(TEST_FILE, mode='rb') as test_file: @@ -156,12 +156,12 @@ class KeyTestCase(BaseTestCase): ) def test_detached_signing_with_passphrase(self): - key = Key.objects.create(key_data=TEST_KEY_DATA) + key = Key.objects.create(key_data=TEST_KEY_PRIVATE_DATA) with open(TEST_FILE, mode='rb') as test_file: detached_signature = key.sign_file( file_object=test_file, detached=True, - passphrase=TEST_KEY_PASSPHRASE + passphrase=TEST_KEY_PRIVATE_PASSPHRASE ) signature_file = io.BytesIO() @@ -175,4 +175,4 @@ class KeyTestCase(BaseTestCase): signature_file.close() self.assertTrue(result) - self.assertEqual(result.fingerprint, TEST_KEY_FINGERPRINT) + self.assertEqual(result.fingerprint, TEST_KEY_PRIVATE_FINGERPRINT) diff --git a/mayan/apps/django_gpg/tests/test_views.py b/mayan/apps/django_gpg/tests/test_views.py index 7c2c7f2754..64c9fc482f 100644 --- a/mayan/apps/django_gpg/tests/test_views.py +++ b/mayan/apps/django_gpg/tests/test_views.py @@ -7,25 +7,13 @@ from mayan.apps.common.tests import GenericViewTestCase from ..models import Key from ..permissions import permission_key_download, permission_key_upload -from .literals import TEST_KEY_DATA, TEST_KEY_FINGERPRINT -from .mixins import KeyTestMixin - - -class KeyViewTestMixin(object): - def _request_test_key_download_view(self): - return self.get( - viewname='django_gpg:key_download', kwargs={'pk': self.test_key.pk} - ) - - def _request_test_key_upload_view(self): - return self.post( - viewname='django_gpg:key_upload', data={'key_data': TEST_KEY_DATA} - ) +from .literals import TEST_KEY_PRIVATE_FINGERPRINT +from .mixins import KeyTestMixin, KeyViewTestMixin class KeyViewTestCase(KeyTestMixin, KeyViewTestMixin, GenericViewTestCase): def test_key_download_view_no_permission(self): - self._create_test_key() + self._create_test_key_private() response = self._request_test_key_download_view() self.assertEqual(response.status_code, 403) @@ -33,16 +21,16 @@ class KeyViewTestCase(KeyTestMixin, KeyViewTestMixin, GenericViewTestCase): def test_key_download_view_with_permission(self): self.expected_content_types = ('application/octet-stream; charset=utf-8',) - self._create_test_key() + self._create_test_key_private() self.grant_access( - obj=self.test_key, permission=permission_key_download + obj=self.test_key_private, permission=permission_key_download ) response = self._request_test_key_download_view() assert_download_response( - self, response=response, content=self.test_key.key_data, - basename=self.test_key.key_id, + self, response=response, content=self.test_key_private.key_data, + basename=self.test_key_private.key_id, ) def test_key_upload_view_no_permission(self): @@ -59,5 +47,5 @@ class KeyViewTestCase(KeyTestMixin, KeyViewTestMixin, GenericViewTestCase): self.assertEqual(Key.objects.count(), 1) self.assertEqual( - Key.objects.first().fingerprint, TEST_KEY_FINGERPRINT + Key.objects.first().fingerprint, TEST_KEY_PRIVATE_FINGERPRINT ) diff --git a/mayan/apps/document_signatures/tests/literals.py b/mayan/apps/document_signatures/tests/literals.py index 040366be1e..49aebf355a 100644 --- a/mayan/apps/document_signatures/tests/literals.py +++ b/mayan/apps/document_signatures/tests/literals.py @@ -12,9 +12,9 @@ TEST_SIGNATURE_FILE_PATH = os.path.join( settings.BASE_DIR, 'apps', 'document_signatures', 'tests', 'contrib', 'sample_documents', 'mayan_11_1.pdf.sig' ) -TEST_KEY_FILE = os.path.join( +TEST_KEY_FILE_PATH = os.path.join( settings.BASE_DIR, 'apps', 'document_signatures', 'tests', 'contrib', 'sample_documents', 'key0x5F3F7F75D210724D.asc' ) -TEST_KEY_ID = '5F3F7F75D210724D' +TEST_KEY_PUBLIC_ID = '5F3F7F75D210724D' TEST_SIGNATURE_ID = 'XVkoGKw35yU1iq11dZPiv7uAY7k' diff --git a/mayan/apps/document_signatures/tests/mixins.py b/mayan/apps/document_signatures/tests/mixins.py index aafafa93f8..6d352abdfb 100644 --- a/mayan/apps/document_signatures/tests/mixins.py +++ b/mayan/apps/document_signatures/tests/mixins.py @@ -3,13 +3,50 @@ from __future__ import absolute_import, unicode_literals from django.core.files import File from mayan.apps.django_gpg.models import Key +from mayan.apps.django_gpg.tests.literals import TEST_KEY_PRIVATE_PASSPHRASE from ..models import DetachedSignature -from .literals import TEST_KEY_FILE, TEST_SIGNATURE_FILE_PATH +from .literals import TEST_KEY_FILE_PATH, TEST_SIGNATURE_FILE_PATH -class SignaturesTestMixin(object): +class DetachedSignatureViewTestMixin(object): + def _request_test_document_version_signature_create_view(self): + return self.post( + viewname='signatures:document_version_signature_detached_create', + kwargs={'pk': self.test_document_version.pk}, data={ + 'key': self.test_key_private.pk, + 'passphrase': TEST_KEY_PRIVATE_PASSPHRASE + } + ) + + def _request_test_document_version_signature_download_view(self): + return self.get( + viewname='signatures:document_version_signature_download', + kwargs={'pk': self.test_signature.pk} + ) + + def _request_test_document_version_signature_upload_view(self): + with open(TEST_SIGNATURE_FILE_PATH, mode='rb') as file_object: + return self.post( + viewname='signatures:document_version_signature_upload', + kwargs={'pk': self.test_document.latest_version.pk}, + data={'signature_file': file_object} + ) + + +class EmbeddedSignatureViewTestMixin(object): + def _request_test_document_version_signature_create_view(self): + return self.post( + viewname='signatures:document_version_signature_embedded_create', + kwargs={'pk': self.test_document_version.pk}, data={ + 'key': self.test_key_private.pk, + 'passphrase': TEST_KEY_PRIVATE_PASSPHRASE + } + ) + + +class SignatureTestMixin(object): def _create_test_detached_signature(self): with open(TEST_SIGNATURE_FILE_PATH, mode='rb') as file_object: self.test_signature = DetachedSignature.objects.create( @@ -17,6 +54,31 @@ class SignaturesTestMixin(object): signature_file=File(file_object) ) - def _create_test_key(self): - with open(TEST_KEY_FILE, mode='rb') as file_object: + def _create_test_key_private(self): + with open(TEST_KEY_FILE_PATH, mode='rb') as file_object: self.test_key = Key.objects.create(key_data=file_object.read()) + + +class SignatureViewTestMixin(object): + def _request_test_document_version_signature_delete_view(self): + return self.post( + viewname='signatures:document_version_signature_delete', + kwargs={'pk': self.test_signature.pk} + ) + + def _request_test_document_version_signature_details_view(self): + return self.get( + viewname='signatures:document_version_signature_details', + kwargs={'pk': self.test_signature.pk} + ) + + def _request_test_document_version_signature_list_view(self, document): + return self.get( + viewname='signatures:document_version_signature_list', + kwargs={'pk': self.test_document.latest_version.pk} + ) + + def _request_all_test_document_version_signature_verify_view(self): + return self.post( + viewname='signatures:all_document_version_signature_verify' + ) diff --git a/mayan/apps/document_signatures/tests/test_links.py b/mayan/apps/document_signatures/tests/test_links.py index 5f2b4678c3..ef37d7078d 100644 --- a/mayan/apps/document_signatures/tests/test_links.py +++ b/mayan/apps/document_signatures/tests/test_links.py @@ -15,10 +15,12 @@ from ..permissions import ( permission_document_version_signature_view ) from .literals import TEST_SIGNED_DOCUMENT_PATH -from .mixins import SignaturesTestMixin +from .mixins import SignatureTestMixin -class DocumentSignatureLinksTestCase(SignaturesTestMixin, GenericDocumentViewTestCase): +class DocumentSignatureLinksTestCase( + SignatureTestMixin, GenericDocumentViewTestCase +): def test_document_version_signature_detail_link_no_permission(self): self.test_document_path = TEST_SIGNED_DOCUMENT_PATH self.upload_document() diff --git a/mayan/apps/document_signatures/tests/test_models.py b/mayan/apps/document_signatures/tests/test_models.py index 957b87a159..1bbec89de2 100644 --- a/mayan/apps/document_signatures/tests/test_models.py +++ b/mayan/apps/document_signatures/tests/test_models.py @@ -3,10 +3,8 @@ from __future__ import unicode_literals import hashlib import time -from mayan.apps.django_gpg.models import Key -from mayan.apps.django_gpg.tests.literals import ( - TEST_KEY_DATA, TEST_KEY_PASSPHRASE -) +from mayan.apps.django_gpg.tests.literals import TEST_KEY_PRIVATE_PASSPHRASE +from mayan.apps.django_gpg.tests.mixins import KeyTestMixin from mayan.apps.documents.models import DocumentVersion from mayan.apps.documents.tests import ( GenericDocumentTestCase, TEST_DOCUMENT_PATH @@ -15,11 +13,13 @@ from mayan.apps.documents.tests import ( from ..models import DetachedSignature, EmbeddedSignature from ..tasks import task_verify_missing_embedded_signature -from .literals import TEST_SIGNED_DOCUMENT_PATH, TEST_KEY_ID, TEST_SIGNATURE_ID -from .mixins import SignaturesTestMixin +from .literals import ( + TEST_SIGNED_DOCUMENT_PATH, TEST_KEY_PUBLIC_ID, TEST_SIGNATURE_ID +) +from .mixins import SignatureTestMixin -class DocumentSignaturesTestCase(SignaturesTestMixin, GenericDocumentTestCase): +class DocumentSignaturesTestCase(SignatureTestMixin, GenericDocumentTestCase): auto_upload_document = False def test_embedded_signature_no_key(self): @@ -32,7 +32,7 @@ class DocumentSignaturesTestCase(SignaturesTestMixin, GenericDocumentTestCase): self.assertEqual( signature.document_version, self.test_document.latest_version ) - self.assertEqual(signature.key_id, TEST_KEY_ID) + self.assertEqual(signature.key_id, TEST_KEY_PUBLIC_ID) self.assertEqual(signature.signature_id, None) def test_embedded_signature_post_key_verify(self): @@ -45,17 +45,17 @@ class DocumentSignaturesTestCase(SignaturesTestMixin, GenericDocumentTestCase): self.assertEqual( signature.document_version, self.test_document.latest_version ) - self.assertEqual(signature.key_id, TEST_KEY_ID) + self.assertEqual(signature.key_id, TEST_KEY_PUBLIC_ID) self.assertEqual(signature.signature_id, None) - self._create_test_key() + self._create_test_key_private() signature = EmbeddedSignature.objects.first() self.assertEqual(signature.signature_id, TEST_SIGNATURE_ID) def test_embedded_signature_post_no_key_verify(self): - self._create_test_key() + self._create_test_key_private() self.test_document_path = TEST_SIGNED_DOCUMENT_PATH self.upload_document() @@ -66,7 +66,7 @@ class DocumentSignaturesTestCase(SignaturesTestMixin, GenericDocumentTestCase): self.assertEqual( signature.document_version, self.test_document.latest_version ) - self.assertEqual(signature.key_id, TEST_KEY_ID) + self.assertEqual(signature.key_id, TEST_KEY_PUBLIC_ID) self.assertEqual(signature.signature_id, TEST_SIGNATURE_ID) self.test_key.delete() @@ -76,7 +76,7 @@ class DocumentSignaturesTestCase(SignaturesTestMixin, GenericDocumentTestCase): self.assertEqual(signature.signature_id, None) def test_embedded_signature_with_key(self): - self._create_test_key() + self._create_test_key_private() self.test_document_path = TEST_SIGNED_DOCUMENT_PATH self.upload_document() @@ -88,7 +88,7 @@ class DocumentSignaturesTestCase(SignaturesTestMixin, GenericDocumentTestCase): signature.document_version, self.test_document.latest_version ) - self.assertEqual(signature.key_id, TEST_KEY_ID) + self.assertEqual(signature.key_id, TEST_KEY_PUBLIC_ID) self.assertEqual( signature.public_key_fingerprint, self.test_key.fingerprint ) @@ -105,11 +105,11 @@ class DocumentSignaturesTestCase(SignaturesTestMixin, GenericDocumentTestCase): self.assertEqual( self.test_signature.document_version, self.test_document.latest_version ) - self.assertEqual(self.test_signature.key_id, TEST_KEY_ID) + self.assertEqual(self.test_signature.key_id, TEST_KEY_PUBLIC_ID) self.assertEqual(self.test_signature.public_key_fingerprint, None) def test_detached_signature_with_key(self): - self._create_test_key() + self._create_test_key_private() self.test_document_path = TEST_DOCUMENT_PATH self.upload_document() @@ -120,7 +120,7 @@ class DocumentSignaturesTestCase(SignaturesTestMixin, GenericDocumentTestCase): self.assertEqual( self.test_signature.document_version, self.test_document.latest_version ) - self.assertEqual(self.test_signature.key_id, TEST_KEY_ID) + self.assertEqual(self.test_signature.key_id, TEST_KEY_PUBLIC_ID) self.assertEqual( self.test_signature.public_key_fingerprint, self.test_key.fingerprint @@ -138,10 +138,10 @@ class DocumentSignaturesTestCase(SignaturesTestMixin, GenericDocumentTestCase): self.test_signature.document_version, self.test_document.latest_version ) - self.assertEqual(self.test_signature.key_id, TEST_KEY_ID) + self.assertEqual(self.test_signature.key_id, TEST_KEY_PUBLIC_ID) self.assertEqual(self.test_signature.public_key_fingerprint, None) - self._create_test_key() + self._create_test_key_private() signature = DetachedSignature.objects.first() @@ -150,7 +150,7 @@ class DocumentSignaturesTestCase(SignaturesTestMixin, GenericDocumentTestCase): ) def test_detached_signature_post_no_key_verify(self): - self._create_test_key() + self._create_test_key_private() self.test_document_path = TEST_DOCUMENT_PATH self.upload_document() @@ -162,7 +162,7 @@ class DocumentSignaturesTestCase(SignaturesTestMixin, GenericDocumentTestCase): self.test_signature.document_version, self.test_document.latest_version ) - self.assertEqual(self.test_signature.key_id, TEST_KEY_ID) + self.assertEqual(self.test_signature.key_id, TEST_KEY_PUBLIC_ID) self.assertEqual( self.test_signature.public_key_fingerprint, self.test_key.fingerprint @@ -199,10 +199,10 @@ class DocumentSignaturesTestCase(SignaturesTestMixin, GenericDocumentTestCase): signature = EmbeddedSignature.objects.first() self.assertEqual(signature.document_version, signed_version) - self.assertEqual(signature.key_id, TEST_KEY_ID) + self.assertEqual(signature.key_id, TEST_KEY_PUBLIC_ID) -class EmbeddedSignaturesTestCase(GenericDocumentTestCase): +class EmbeddedSignaturesTestCase(KeyTestMixin, GenericDocumentTestCase): auto_upload_document = False def test_unsigned_document_version_method(self): @@ -256,7 +256,7 @@ class EmbeddedSignaturesTestCase(GenericDocumentTestCase): ) def test_signing(self): - self.test_key = Key.objects.create(key_data=TEST_KEY_DATA) + self._create_test_key_private() self.test_document_path = TEST_DOCUMENT_PATH self.upload_document() @@ -269,8 +269,8 @@ class EmbeddedSignaturesTestCase(GenericDocumentTestCase): new_version = EmbeddedSignature.objects.sign_document_version( document_version=self.test_document.latest_version, - key=self.test_key, - passphrase=TEST_KEY_PASSPHRASE + key=self.test_key_private, + passphrase=TEST_KEY_PRIVATE_PASSPHRASE ) self.assertEqual(EmbeddedSignature.objects.count(), 1) diff --git a/mayan/apps/document_signatures/tests/test_views.py b/mayan/apps/document_signatures/tests/test_views.py index b699dfcd0e..96b14618fb 100644 --- a/mayan/apps/document_signatures/tests/test_views.py +++ b/mayan/apps/document_signatures/tests/test_views.py @@ -2,6 +2,8 @@ from __future__ import absolute_import, unicode_literals from django_downloadview.test import assert_download_response +from mayan.apps.django_gpg.permissions import permission_key_sign +from mayan.apps.django_gpg.tests.mixins import KeyTestMixin from mayan.apps.documents.models import DocumentVersion from mayan.apps.documents.tests import ( GenericDocumentViewTestCase, TEST_DOCUMENT_PATH @@ -9,6 +11,8 @@ from mayan.apps.documents.tests import ( from ..models import DetachedSignature, EmbeddedSignature from ..permissions import ( + permission_document_version_sign_detached, + permission_document_version_sign_embedded, permission_document_version_signature_delete, permission_document_version_signature_download, permission_document_version_signature_upload, @@ -16,59 +20,23 @@ from ..permissions import ( permission_document_version_signature_view ) -from .literals import TEST_SIGNATURE_FILE_PATH, TEST_SIGNED_DOCUMENT_PATH -from .mixins import SignaturesTestMixin +from .literals import TEST_SIGNED_DOCUMENT_PATH +from .mixins import ( + DetachedSignatureViewTestMixin, EmbeddedSignatureViewTestMixin, + SignatureTestMixin, SignatureViewTestMixin +) TEST_UNSIGNED_DOCUMENT_COUNT = 4 TEST_SIGNED_DOCUMENT_COUNT = 2 -class SignaturesViewTestMixin(object): - def _request_test_document_version_signature_delete_view(self): - return self.post( - viewname='signatures:document_version_signature_delete', - kwargs={'pk': self.test_signature.pk} - ) - - def _request_test_document_version_signature_details_view(self): - return self.get( - viewname='signatures:document_version_signature_details', - kwargs={'pk': self.test_signature.pk} - ) - - def _request_test_document_version_signature_download_view(self): - return self.get( - viewname='signatures:document_version_signature_download', - kwargs={'pk': self.test_signature.pk} - ) - - def _request_test_document_version_signature_list_view(self, document): - return self.get( - viewname='signatures:document_version_signature_list', - kwargs={'pk': self.test_document.latest_version.pk} - ) - - def _request_test_document_version_signature_upload_view(self): - with open(TEST_SIGNATURE_FILE_PATH, mode='rb') as file_object: - return self.post( - viewname='signatures:document_version_signature_upload', - kwargs={'pk': self.test_document.latest_version.pk}, - data={'signature_file': file_object} - ) - - def _request_all_test_document_version_signature_verify_view(self): - return self.post( - viewname='signatures:all_document_version_signature_verify' - ) - - class SignaturesViewTestCase( - SignaturesTestMixin, SignaturesViewTestMixin, GenericDocumentViewTestCase + SignatureTestMixin, SignatureViewTestMixin, GenericDocumentViewTestCase ): auto_upload_document = False def test_signature_delete_view_no_permission(self): - self._create_test_key() + self._create_test_key_private() self.test_document_path = TEST_DOCUMENT_PATH self.upload_document() @@ -85,7 +53,7 @@ class SignaturesViewTestCase( self.assertEqual(DetachedSignature.objects.count(), 1) def test_signature_delete_view_with_access(self): - self._create_test_key() + self._create_test_key_private() self.test_document_path = TEST_DOCUMENT_PATH self.upload_document() @@ -106,7 +74,7 @@ class SignaturesViewTestCase( self.assertEqual(DetachedSignature.objects.count(), 0) def test_signature_detail_view_no_permission(self): - self._create_test_key() + self._create_test_key_private() self.test_document_path = TEST_DOCUMENT_PATH self.upload_document() @@ -117,7 +85,7 @@ class SignaturesViewTestCase( self.assertEqual(response.status_code, 404) def test_signature_detail_view_with_access(self): - self._create_test_key() + self._create_test_key_private() self.test_document_path = TEST_DOCUMENT_PATH self.upload_document() @@ -135,6 +103,7 @@ class SignaturesViewTestCase( status_code=200 ) + """ def test_signature_download_view_no_permission(self): self.test_document_path = TEST_DOCUMENT_PATH self.upload_document() @@ -163,9 +132,9 @@ class SignaturesViewTestCase( assert_download_response( self, response=response, content=file_object.read(), ) - + """ def test_signature_list_view_no_permission(self): - self._create_test_key() + self._create_test_key_private() self.test_document_path = TEST_DOCUMENT_PATH self.upload_document() @@ -178,7 +147,7 @@ class SignaturesViewTestCase( self.assertEqual(response.status_code, 403) def test_signature_list_view_with_access(self): - self._create_test_key() + self._create_test_key_private() self.test_document_path = TEST_DOCUMENT_PATH self.upload_document() @@ -195,7 +164,7 @@ class SignaturesViewTestCase( ) self.assertEqual(response.status_code, 200) self.assertEqual(response.context['object_list'].count(), 1) - + """ def test_signature_upload_view_no_permission(self): self.test_document_path = TEST_DOCUMENT_PATH self.upload_document() @@ -218,7 +187,7 @@ class SignaturesViewTestCase( self.assertEqual(response.status_code, 302) self.assertEqual(DetachedSignature.objects.count(), 1) - + """ def test_missing_signature_verify_view_no_permission(self): # Silence converter logging self._silence_logger(name='mayan.apps.converter.backends') diff --git a/mayan/apps/documents/tests/mixins.py b/mayan/apps/documents/tests/mixins.py index cb692a1f10..27579eb129 100644 --- a/mayan/apps/documents/tests/mixins.py +++ b/mayan/apps/documents/tests/mixins.py @@ -58,6 +58,8 @@ class DocumentTestMixin(object): self.test_document = document self.test_documents.append(document) + self.test_document_page = document.latest_version.pages.first() + self.test_document_version = document.latest_version def _calculate_test_document_path(self): if not self.test_document_path: From 774d09479c5b6986138c4e1cbc70a249e97b8939 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Mon, 11 Nov 2019 02:23:16 -0400 Subject: [PATCH 09/14] Add missing ModelProperty label entires Signed-off-by: Roberto Rosario --- HISTORY.rst | 2 ++ mayan/apps/file_metadata/apps.py | 5 +++-- mayan/apps/metadata/apps.py | 4 ++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index ac4d722229..c245f4de70 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -8,6 +8,8 @@ tests. - Rename expected_content_type to expected_content_types and allow a list of content types to be specified. +- Add missing label to metadata and file metadata model + properties entries. 3.2.9 (2019-11-03) diff --git a/mayan/apps/file_metadata/apps.py b/mayan/apps/file_metadata/apps.py index 5bae454af2..837c065856 100644 --- a/mayan/apps/file_metadata/apps.py +++ b/mayan/apps/file_metadata/apps.py @@ -92,10 +92,11 @@ class FileMetadataApp(MayanAppConfig): ) ModelAttribute( - model=Document, name='file_metadata_value_of', + model=Document, + name='file_metadata_value_of.< dotted path to driver and property >', description=_( 'Return the value of a specific file metadata.' - ) + ), label=_('File metadata value of') ) ModelEventType.register( diff --git a/mayan/apps/metadata/apps.py b/mayan/apps/metadata/apps.py index 1a3a988bd7..5fb3f59075 100644 --- a/mayan/apps/metadata/apps.py +++ b/mayan/apps/metadata/apps.py @@ -93,10 +93,10 @@ class MetadataApp(MayanAppConfig): ) ModelAttribute( - model=Document, name='metadata_value_of', + model=Document, name='metadata_value_of.< metadata type name >', description=_( 'Return the value of a specific document metadata' - ) + ), label=_('Metadata value of') ) ModelField( From 1c39b3c84dc9ed3758c3be44d5a2bbb86b9d93c1 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Fri, 8 Nov 2019 19:32:39 -0400 Subject: [PATCH 10/14] Improve workflow field help text Signed-off-by: Roberto Rosario --- .../migrations/0016_auto_20191108_2332.py | 30 +++++++++++++++++++ mayan/apps/document_states/models.py | 8 ++--- 2 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 mayan/apps/document_states/migrations/0016_auto_20191108_2332.py diff --git a/mayan/apps/document_states/migrations/0016_auto_20191108_2332.py b/mayan/apps/document_states/migrations/0016_auto_20191108_2332.py new file mode 100644 index 0000000000..6e175e00df --- /dev/null +++ b/mayan/apps/document_states/migrations/0016_auto_20191108_2332.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.25 on 2019-11-08 23:32 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('document_states', '0015_auto_20190701_1311'), + ] + + operations = [ + migrations.AlterField( + model_name='workflowstate', + name='completion', + field=models.IntegerField(blank=True, default=0, help_text='The percent of completion that this state represents in relation to the workflow. Use numbers without the percent sign.', verbose_name='Completion'), + ), + migrations.AlterField( + model_name='workflowstate', + name='initial', + field=models.BooleanField(default=False, help_text='The state at which the workflow will start in. Only one state can be the initial state.', verbose_name='Initial'), + ), + migrations.AlterField( + model_name='workflowstateaction', + name='when', + field=models.PositiveIntegerField(choices=[(1, 'On entry'), (2, 'On exit')], default=1, help_text='At which moment of the state this action will execute.', verbose_name='When'), + ), + ] diff --git a/mayan/apps/document_states/models.py b/mayan/apps/document_states/models.py index a5da8ab106..24e966d3a6 100644 --- a/mayan/apps/document_states/models.py +++ b/mayan/apps/document_states/models.py @@ -200,13 +200,13 @@ class WorkflowState(models.Model): initial = models.BooleanField( default=False, help_text=_( - 'Select if this will be the state with which you want the ' - 'workflow to start in. Only one state can be the initial state.' + 'The state at which the workflow will start in. Only one state ' + 'can be the initial state.' ), verbose_name=_('Initial') ) completion = models.IntegerField( blank=True, default=0, help_text=_( - 'Enter the percent of completion that this state represents in ' + 'The percent of completion that this state represents in ' 'relation to the workflow. Use numbers without the percent sign.' ), verbose_name=_('Completion') ) @@ -283,7 +283,7 @@ class WorkflowStateAction(models.Model): when = models.PositiveIntegerField( choices=WORKFLOW_ACTION_WHEN_CHOICES, default=WORKFLOW_ACTION_ON_ENTRY, help_text=_( - 'At which moment of the state this action will execute' + 'At which moment of the state this action will execute.' ), verbose_name=_('When') ) action_path = models.CharField( From f030bae926cfabf6578bbff998b36f07dadd71ce Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Mon, 11 Nov 2019 19:20:01 -0400 Subject: [PATCH 11/14] Update changelog Signed-off-by: Roberto Rosario --- HISTORY.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/HISTORY.rst b/HISTORY.rst index c245f4de70..9becd9ffef 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -10,7 +10,9 @@ and allow a list of content types to be specified. - Add missing label to metadata and file metadata model properties entries. - +- Improve workflow field help text. Make it usable + for the creation/edit form help text and for the + column pop over. 3.2.9 (2019-11-03) ================== From 41214fa90ec7ba037f599b7830d9d48a817c9adf Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Mon, 11 Nov 2019 03:00:17 -0400 Subject: [PATCH 12/14] Fix NamedMultiWidget issue on Python 3 Affects document checkout form Signed-off-by: Roberto Rosario --- mayan/apps/common/widgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mayan/apps/common/widgets.py b/mayan/apps/common/widgets.py index 30ac5cb223..1af27ad451 100644 --- a/mayan/apps/common/widgets.py +++ b/mayan/apps/common/widgets.py @@ -79,7 +79,7 @@ class NamedMultiWidget(forms.widgets.Widget): def id_for_label(self, id_): if id_: - id_ += '_{}'.format(self.widgets.keys()[0]) + id_ += '_{}'.format(list(self.widgets.keys())[0]) return id_ def value_from_datadict(self, data, files, name): From a60c2fa94c7eaeada2cad6d4a5aa45a165b074c7 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Mon, 11 Nov 2019 19:25:32 -0400 Subject: [PATCH 13/14] Add 3.2.10 release notes Signed-off-by: Roberto Rosario --- HISTORY.rst | 3 ++ docs/releases/3.2.10.rst | 106 +++++++++++++++++++++++++++++++++++++++ docs/releases/index.rst | 3 +- 3 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 docs/releases/3.2.10.rst diff --git a/HISTORY.rst b/HISTORY.rst index 9becd9ffef..ae149a759e 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -13,6 +13,9 @@ - Improve workflow field help text. Make it usable for the creation/edit form help text and for the column pop over. +- Fix NamedMultiWidget issue on Python 3. Affects + document checkout form. GitLab issue #683. Thanks + to John Bentley (@johnbentleyii) for the report. 3.2.9 (2019-11-03) ================== diff --git a/docs/releases/3.2.10.rst b/docs/releases/3.2.10.rst new file mode 100644 index 0000000000..40aa9f8683 --- /dev/null +++ b/docs/releases/3.2.10.rst @@ -0,0 +1,106 @@ +Version 3.2.10 +============== + +Released: November XX, 2019 + + +Changes +------- + + + + +Removals +-------- + +- None + + +Upgrading from a previous version +--------------------------------- + +If installed via Python's PIP +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Remove deprecated requirements:: + + sudo -u mayan curl https://gitlab.com/mayan-edms/mayan-edms/raw/master/removals.txt -o /tmp/removals.txt && sudo -u mayan /opt/mayan-edms/bin/pip uninstall -y -r /tmp/removals.txt + +Type in the console:: + + sudo -u mayan /opt/mayan-edms/bin/pip install mayan-edms==3.2.10 + +the requirements will also be updated automatically. + + +Using Git +^^^^^^^^^ + +If you installed Mayan EDMS by cloning the Git repository issue the commands:: + + $ git reset --hard HEAD + $ git pull + +otherwise download the compressed archived and uncompress it overriding the +existing installation. + +Remove deprecated requirements:: + + $ pip uninstall -y -r removals.txt + +Next upgrade/add the new requirements:: + + $ pip install --upgrade -r requirements.txt + + +Common steps +^^^^^^^^^^^^ + +Perform these steps after updating the code from either step above. + +Make a backup of your supervisord file:: + + sudo cp /etc/supervisor/conf.d/mayan.conf /etc/supervisor/conf.d/mayan.conf.bck + +Update the supervisord configuration file. Replace the environment +variables values show here with your respective settings. This step will refresh +the supervisord configuration file with the new queues and the latest +recommended layout:: + + sudo MAYAN_DATABASE_ENGINE=django.db.backends.postgresql MAYAN_DATABASE_NAME=mayan \ + MAYAN_DATABASE_PASSWORD=mayanuserpass MAYAN_DATABASE_USER=mayan \ + MAYAN_DATABASE_HOST=127.0.0.1 MAYAN_MEDIA_ROOT=/opt/mayan-edms/media \ + /opt/mayan-edms/bin/mayan-edms.py platformtemplate supervisord > /etc/supervisor/conf.d/mayan.conf + +Edit the supervisord configuration file and update any setting the template +generator missed:: + + sudo vi /etc/supervisor/conf.d/mayan.conf + +Migrate existing database schema with:: + + sudo -u mayan MAYAN_DATABASE_ENGINE=django.db.backends.postgresql MAYAN_DATABASE_NAME=mayan \ + MAYAN_DATABASE_PASSWORD=mayanuserpass MAYAN_DATABASE_USER=mayan \ + MAYAN_DATABASE_HOST=127.0.0.1 MAYAN_MEDIA_ROOT=/opt/mayan-edms/media \ + /opt/mayan-edms/bin/mayan-edms.py performupgrade + +Add new static media:: + + sudo -u mayan MAYAN_MEDIA_ROOT=/opt/mayan-edms/media \ + /opt/mayan-edms/bin/mayan-edms.py preparestatic --noinput + +The upgrade procedure is now complete. + + +Backward incompatible changes +----------------------------- + +- None + + +Bugs fixed or issues closed +--------------------------- + +- :gitlab-issue:`683` Checkout Form Does not Appear in Python 3.6 + +.. _PyPI: https://pypi.python.org/pypi/mayan-edms/ diff --git a/docs/releases/index.rst b/docs/releases/index.rst index 1716541086..17620f23c7 100644 --- a/docs/releases/index.rst +++ b/docs/releases/index.rst @@ -19,7 +19,8 @@ versions of the documentation contain the release notes for any later releases. ********** .. toctree:: :maxdepth: 1 - + + 3.2.10 3.2.9 3.2.8 3.2.7 From e593dcb5d8c1d927910a2f0a0d86ab045695c290 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Tue, 12 Nov 2019 00:23:18 -0400 Subject: [PATCH 14/14] Add missing Event class cache invalidation Needed when calling the refresh() method. Otherwise null StoredEvent instances will be returned. Signed-off-by: Roberto Rosario --- HISTORY.rst | 2 ++ mayan/apps/events/classes.py | 3 +++ 2 files changed, 5 insertions(+) diff --git a/HISTORY.rst b/HISTORY.rst index ae149a759e..2ecc02d918 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -16,6 +16,8 @@ - Fix NamedMultiWidget issue on Python 3. Affects document checkout form. GitLab issue #683. Thanks to John Bentley (@johnbentleyii) for the report. +- Add missing Event class cache invalidation when + calling the refresh() method. 3.2.9 (2019-11-03) ================== diff --git a/mayan/apps/events/classes.py b/mayan/apps/events/classes.py index 66feb551b8..320f02a3a4 100644 --- a/mayan/apps/events/classes.py +++ b/mayan/apps/events/classes.py @@ -73,6 +73,9 @@ class EventType(object): @classmethod def refresh(cls): for event_type in cls.all(): + # Invalidate cache and recreate store events while repopulating + # cache + event_type.stored_event_type = None event_type.get_stored_event_type() def __init__(self, namespace, name, label):