diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..280c745 --- /dev/null +++ b/.flake8 @@ -0,0 +1,9 @@ +[flake8] +count = True +max-line-length = 127 +max-complecity = 10 +exclude = + .git, + .tox, + .venv +statistics = True diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 849a16b..f21adb1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,15 +1,16 @@ -name: Run linter +name: Tests on: [push, pull_request] jobs: - test: + build: runs-on: ubuntu-latest strategy: fail-fast: false matrix: - python-version: - - "3.8" - + python-version: ['3.7', '3.8', 'pypy3'] + django-version: ['2.2', '3.1', '3.2'] + pytest-version: ['pytest'] + pytest-django-version: ['pytest-django'] steps: - uses: actions/checkout@v2 @@ -18,14 +19,26 @@ jobs: with: python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install flake8 + - name: Install CI dependencies + run: pip install flake8 codecov ${{ matrix.pytest-version }} ${{ matrix.pytest-django-version }} + + - name: Install Django ${{ matrix.django-version }} + run: pip install 'Django==${{ matrix.django-version }}' + - name: Install Request Cache + run: pip install . - name: Lint with flake8 run: | # stop the build if there are Python syntax errors or undefined names - flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + flake8 . --select=E9,F63,F7,F82 # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide - flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + flake8 . --exit-zero + + - name: Run tests with coverage + run: | + # run tests with coverage + coverage run -m pytest tests + coverage xml + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v1 diff --git a/setup.cfg b/setup.cfg index 11e9ec4..2df317e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,2 +1,9 @@ [metadata] -description-file = README.rst \ No newline at end of file +description-file = README.rst + +[tool:pytest] +python_files = tests.py test_*.py *_tests.py +DJANGO_SETTINGS_MODULE = testapp.settings + +[coverage:run] +source = ./django_request_cache diff --git a/tests/functional/test_sync.py b/tests/functional/test_sync.py new file mode 100644 index 0000000..22b4794 --- /dev/null +++ b/tests/functional/test_sync.py @@ -0,0 +1,25 @@ +from unittest import mock + +from django.test import TestCase + +from django_request_cache import cache_for_request + + +class RequestCacheMiddlewareTest(TestCase): + + @mock.patch("testapp.views.sync.retrieve_time", spec_set=["__call__"]) + def test_sync_one_request(self, mock_func): + cached_func = mock.MagicMock(spec_set=["__call__", "__name__"]) + mock_func.side_effect = cache_for_request(cached_func) + self.client.get('/') + self.assertEqual(mock_func.call_count, 2) + self.assertEqual(cached_func.call_count, 1) + + @mock.patch("testapp.views.sync.retrieve_time", spec_set=["__call__"]) + def test_sync_two_requests(self, mock_func): + cached_func = mock.MagicMock(spec_set=["__call__", "__name__"]) + mock_func.side_effect = cache_for_request(cached_func) + self.client.get('/') + self.client.get('/') + self.assertEqual(mock_func.call_count, 4) + self.assertEqual(cached_func.call_count, 2) diff --git a/tests/manage.py b/tests/manage.py new file mode 100755 index 0000000..6c75a82 --- /dev/null +++ b/tests/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python + +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'testapp.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + 'available on your PYTHONPATH environment variable? Did you ' + 'forget to activate a virtual environment?' + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main() diff --git a/tests/testapp/__init__.py b/tests/testapp/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/testapp/settings.py b/tests/testapp/settings.py new file mode 100644 index 0000000..0a7601c --- /dev/null +++ b/tests/testapp/settings.py @@ -0,0 +1,35 @@ +import os + +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +SECRET_KEY = 'secret' +DEBUG = True +ALLOWED_HOSTS = [] + +INSTALLED_APPS = [ + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django_request_cache', +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'django_userforeignkey.middleware.UserForeignKeyMiddleware', + 'django_request_cache.middleware.RequestCacheMiddleware', +] + +ROOT_URLCONF = 'testapp.urls' + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': ':memory:', + } +} diff --git a/tests/testapp/urls.py b/tests/testapp/urls.py new file mode 100644 index 0000000..ba88d50 --- /dev/null +++ b/tests/testapp/urls.py @@ -0,0 +1,7 @@ +from django.urls import path + +from testapp.views.sync import index_view + +urlpatterns = [ + path('', index_view, name='index'), +] diff --git a/tests/testapp/utils.py b/tests/testapp/utils.py new file mode 100644 index 0000000..5652686 --- /dev/null +++ b/tests/testapp/utils.py @@ -0,0 +1,8 @@ +from datetime import datetime + +from django_request_cache import cache_for_request + + +@cache_for_request +def retrieve_time(): + return datetime.now() diff --git a/tests/testapp/views/__init__.py b/tests/testapp/views/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/testapp/views/sync.py b/tests/testapp/views/sync.py new file mode 100644 index 0000000..81532e1 --- /dev/null +++ b/tests/testapp/views/sync.py @@ -0,0 +1,8 @@ +from django.http import HttpResponse +from testapp.utils import retrieve_time + + +def index_view(request): + retrieve_time() + retrieve_time() + return HttpResponse() diff --git a/tests/testapp/wsgi.py b/tests/testapp/wsgi.py new file mode 100644 index 0000000..aa17590 --- /dev/null +++ b/tests/testapp/wsgi.py @@ -0,0 +1,7 @@ +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'testapp.settings') + +application = get_wsgi_application()