From b3815b6b98df95f68b06f401417ac1be464e1f01 Mon Sep 17 00:00:00 2001 From: Felix Date: Tue, 18 Dec 2018 15:33:26 +0100 Subject: [PATCH] #23 Add tests for caching --- django_migration_linter/constants.py | 21 +++++++ django_migration_linter/utils.py | 6 ++ setup.py | 8 ++- tests/functional/test_cache.py | 85 ++++++++++++++++++++++++++ tests/functional/test_cmd_line_call.py | 45 ++++++++++++++ tox.ini | 1 + 6 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 django_migration_linter/constants.py create mode 100644 tests/functional/test_cache.py diff --git a/django_migration_linter/constants.py b/django_migration_linter/constants.py new file mode 100644 index 0000000..3086825 --- /dev/null +++ b/django_migration_linter/constants.py @@ -0,0 +1,21 @@ +# Copyright 2018 3YOURMIND GmbH + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from pathlib import Path + +from appdirs import user_cache_dir + +__VERSION__ = '0.0.7' + +DEFAULT_CACHE_PATH = Path(user_cache_dir("django-migration-linter", version=__VERSION__)) diff --git a/django_migration_linter/utils.py b/django_migration_linter/utils.py index b2b8f23..4575a4f 100644 --- a/django_migration_linter/utils.py +++ b/django_migration_linter/utils.py @@ -16,6 +16,8 @@ from __future__ import print_function import os import sys +from django_migration_linter.constants import DEFAULT_CACHE_PATH + def is_django_project(path): django_manage_file = os.path.join(path, 'manage.py') @@ -54,3 +56,7 @@ def split_path(path): def clean_bytes_to_str(byte_input): return byte_input.decode('utf-8').strip() + + +def get_default_cache_file(project_name): + os.join(DEFAULT_CACHE_PATH, '{0}.pickle'.format(project_name)) diff --git a/setup.py b/setup.py index 79ca34d..c1f728c 100644 --- a/setup.py +++ b/setup.py @@ -18,11 +18,16 @@ from setuptools import setup, find_packages PROJECT_DIR = path.abspath(path.dirname(__file__)) +# Read __VERSION__ +exec(open('./django_migration_linter/constants.py').read()) + + with open(path.join(PROJECT_DIR, 'README.rst')) as f: long_description = f.read() install_requirements = [ 'django>=1.11', + 'appdirs==1.4.3' ] test_requirements = [ @@ -31,9 +36,10 @@ test_requirements = [ 'django-fake-database-backends', ] +# noinspection PyUnresolvedReferences setup( name='django-migration-linter', - version='0.0.7', + version=__VERSION__, description='Detect backward incompatible migrations for your django project', long_description=long_description, diff --git a/tests/functional/test_cache.py b/tests/functional/test_cache.py new file mode 100644 index 0000000..85a94ef --- /dev/null +++ b/tests/functional/test_cache.py @@ -0,0 +1,85 @@ +# Copyright 2018 3YOURMIND GmbH + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import os +import shutil +import tempfile +import unittest +from unittest import mock + +from django_migration_linter.utils import get_default_cache_file +from tests import fixtures + +from django_migration_linter import MigrationLinter +from tests.fixtures import ADD_NOT_NULL_COLUMN_PROJECT + + +class CacheTest(unittest.TestCase): + MIGRATION_FILE = os.path.join(ADD_NOT_NULL_COLUMN_PROJECT, 'test_app', 'migrations', '0001_create_table.py') + fd, temp_path = tempfile.mkstemp() + + def setUp(self): + shutil.copy2(self.MIGRATION_FILE, self.temp_path) + + def tearDown(self): + shutil.copy2(self.temp_path, self.MIGRATION_FILE) + os.remove(self.temp_path) + + def test_cache_normal(self): + if os.path.exists(get_default_cache_file('test_project_add_not_null_column')): + os.remove(get_default_cache_file('test_project_add_not_null_column')) + linter = MigrationLinter(fixtures.ADD_NOT_NULL_COLUMN_PROJECT) + linter.lint_all_migrations() + cache = linter.get_cache() + self.assertEqual(cache['hash']['result'], 'OK') + self.assertEqual(cache['hashERR']['result'], 'ERR') + self.assertDictEqual(cache['hashERR']['errors'], ...) + + with mock.patch( + 'subprocess.Popen' + ) as popen_mock: + linter.lint_all_migrations() + self.assertEqual(popen_mock.call_count, 2) + + with mock.patch( + 'subprocess.Popen' + ) as popen_mock: + linter.lint_all_migrations() + + self.assertEqual(popen_mock.call_count, 0) + self.assertFalse(linter.has_errors()) + + def test_cache_ignored(self): + pass + + def test_cache_modified(self): + if os.path.exists(get_default_cache_file('test_project_add_not_null_column')): + os.remove(get_default_cache_file('test_project_add_not_null_column')) + linter = MigrationLinter(fixtures.ADD_NOT_NULL_COLUMN_PROJECT) + linter.lint_all_migrations() + cache = linter.get_cache() + self.assertEqual(cache['hash']['result'], 'OK') + self.assertEqual(cache['hashERR']['result'], 'ERR') + self.assertDictEqual(cache['hashERR']['errors'], ...) + + with open( self.MIGRATION_FILE, "a") as f: + f.write("# modification at the end of the file\n") + + linter.lint_all_migrations() + cache = linter.get_cache() + + self.assertNotIn('hash', cache) + self.assertEqual(cache['newhash']['result'], 'OK') + + + diff --git a/tests/functional/test_cmd_line_call.py b/tests/functional/test_cmd_line_call.py index a3afd33..4f1c4b5 100644 --- a/tests/functional/test_cmd_line_call.py +++ b/tests/functional/test_cmd_line_call.py @@ -12,9 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. +import os import unittest from subprocess import Popen, PIPE from django_migration_linter import utils +from django_migration_linter.utils import get_default_cache_file from tests import fixtures import sys @@ -115,6 +117,49 @@ class CallLinterFromCommandLineTest(unittest.TestCase): self.assertTrue(lines[1].endswith('OK')) self.assertTrue(lines[2].endswith('IGNORE')) + def test_call_linter_cmd_line_cache(self): + if os.path.exists(get_default_cache_file('test_correct_project')): + os.remove(get_default_cache_file('test_correct_project')) + cmd = '{0} {1}'.format( + self.linter_exec, + fixtures.CORRECT_PROJECT + ) + + process = Popen( + cmd, shell=True, stdout=PIPE, stderr=PIPE) + process.wait() + self.assertTrue(os.path.exists(get_default_cache_file('test_correct_project'))) + + def test_call_linter_cmd_line_cache_path(self): + if os.path.exists('/tmp/migration-linter-cache-tests'): + os.remove('/tmp/migration-linter-cache-tests') + cmd = '{0} {1} --cache-path={2}'.format( + self.linter_exec, + fixtures.CORRECT_PROJECT, + '/tmp/migration-linter-cache-tests' + ) + + process = Popen( + cmd, shell=True, stdout=PIPE, stderr=PIPE) + process.wait() + self.assertEqual(process.returncode, 0) + self.assertTrue(os.path.exists('/tmp/migration-linter-cache-tests')) + + def test_call_linter_cmd_line_no_cache(self): + if os.path.exists(get_default_cache_file('test_correct_project')): + os.remove(get_default_cache_file('test_correct_project')) + + cmd = '{0} {1} --no-cache'.format( + self.linter_exec, + fixtures.CORRECT_PROJECT + ) + + process = Popen( + cmd, shell=True, stdout=PIPE, stderr=PIPE) + process.wait() + self.assertEqual(process.returncode, 0) + self.assertFalse(os.path.exists(get_default_cache_file('test_correct_project'))) + def test_call_linter_cmd_line_git_id(self): cmd = '{0} {1} d7125d5f4f0cc9623f670a66c54f131acc50032d'.format( self.linter_exec, diff --git a/tox.ini b/tox.ini index 7ffc655..a1e4ccc 100644 --- a/tox.ini +++ b/tox.ini @@ -11,6 +11,7 @@ pip_pre = true deps = django111: django>=1.11,<2.0 django20: django>=2.0 + appdirs==1.4.3 [testenv:lint] basepython = python3.6