on the way to #8 #9

Manually merged
heinzel merged 3 commits from heinzel into master 2019-12-03 16:41:53 +01:00
13 changed files with 429 additions and 26 deletions

View File

@@ -1,2 +1,5 @@
[run] [run]
source = django_deploy source = django_deploy
omit = */tests/generic.py
src/django_deploy/tests/fake_app1/migrations/0001_initial.py
src/django_deploy/tests/fake_app1/models.py

View File

@@ -2,6 +2,7 @@ import errno
import importlib import importlib
import json import json
import os import os
import subprocess
import sys import sys
from .base_types import OrderedDict from .base_types import OrderedDict
@@ -136,7 +137,37 @@ class DjangoProject(object):
text += '\n' text += '\n'
self._append_to_pythonfile(urlconf_file, text) self._append_to_pythonfile(urlconf_file, text)
def add_app_settings(self, module_path, hooks_config_name=None):
try:
self.create()
except DjangoDeployError as e:
if e.code != errno.EEXIST: # pragma: no cover
raise e
hooks_config = self._get_hooks_config_from_app(module_path, hooks_config_name)
app_settings = getattr(hooks_config, 'SETTINGS', None)
if not app_settings:
return
project_dir = self._project_dir
if not os.path.exists(project_dir): # pragma: no cover
raise DjangoDeployError('No such project directory: {}'.format(project_dir),
code=errno.ENOENT)
settings_dir = os.path.join(project_dir, DJANGO_SETTINGS_DIR)
settings_file = os.path.join(settings_dir, 'settings.py')
text = '\n'
text += '# django-deploy\n'
text += app_settings
text += '\n'
self._append_to_pythonfile(settings_file, text)
def install_app(self, module_path, hooks_config_name=None): def install_app(self, module_path, hooks_config_name=None):
self.add_app_settings(module_path, hooks_config_name=hooks_config_name)
hooks_config = self._get_hooks_config_from_app(module_path, hooks_config_name) hooks_config = self._get_hooks_config_from_app(module_path, hooks_config_name)
installed_apps = getattr(hooks_config, 'INSTALLED_APPS', []) installed_apps = getattr(hooks_config, 'INSTALLED_APPS', [])
@@ -169,5 +200,28 @@ class DjangoProject(object):
config[module_path]['MOUNT'] = [route, urlconf] config[module_path]['MOUNT'] = [route, urlconf]
config.write() config.write()
def migrate_apps(self, apps=None):
project_dir = self._project_dir
management_script = os.path.join(project_dir, 'manage.py')
if not os.path.exists(management_script):
raise DjangoDeployError('No such file: {}. Have you created the django project?'.format(management_script),
code=errno.ENOENT)
if apps:
for app_name in apps:
label = app_name.rpartition('.')[2]
cmd = [management_script, 'migrate', label]
try:
subprocess.check_output(cmd, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
raise DjangoDeployError(e.output.decode('utf8'), exitval=e.returncode)
else:
cmd = [management_script, 'migrate']
try:
subprocess.check_output(cmd, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e: # pragma: no cover
raise DjangoDeployError(e.output.decode('utf8'), exitval=e.returncode)
def __init__(self, project_dir): def __init__(self, project_dir):
self._project_dir = project_dir self._project_dir = project_dir

View File

@@ -1,2 +1,8 @@
# INSTALLED_APPS = ['django_deploy'] # INSTALLED_APPS = ['django_deploy']
# ROOT_URLCONF = '.urls' # ROOT_URLCONF = '.urls'
# SETTINGS = """WSGI_APPLICATION = 'django_deploy.wsgi.application'
# SOME_APP_SETTING = [
# 'item1',
# 'item2']
# DEBUG = False
# """

View File

@@ -31,6 +31,15 @@ class Program(object): # pylint: disable=too-few-public-methods
help='Merge settings from django app MODULE into django project settings' help='Merge settings from django app MODULE into django project settings'
'. Can be used multiple times') '. Can be used multiple times')
parser.add_argument('--migrate',
action='store_true', dest='migrate_all',
help='Migrate database')
parser.add_argument('--migrate-app',
action='append', dest='migrate_apps', metavar='APP',
help='Migrate database up to the newest migration from app'
'. Can be used multiple times')
parser.add_argument('-m', '--mount-app', parser.add_argument('-m', '--mount-app',
nargs=2, nargs=2,
action='append', dest='mount_apps', metavar=('MODULE', 'ROUTE'), action='append', dest='mount_apps', metavar=('MODULE', 'ROUTE'),
@@ -66,6 +75,14 @@ class Program(object): # pylint: disable=too-few-public-methods
project = DjangoProject(project_dir) project = DjangoProject(project_dir)
project.install_app(module_path) project.install_app(module_path)
@staticmethod
def _migrate(project_dir, module_path=None):
project = DjangoProject(project_dir)
if module_path is not None:
project.migrate_apps([module_path])
else:
project.migrate_apps([])
@staticmethod @staticmethod
def _mount_app(project_dir, module_path, route): def _mount_app(project_dir, module_path, route):
project = DjangoProject(project_dir) project = DjangoProject(project_dir)
@@ -94,6 +111,11 @@ class Program(object): # pylint: disable=too-few-public-methods
exceptions.append(e) exceptions.append(e)
continue continue
raise # pragma: no cover raise # pragma: no cover
if cmd_args.migrate_all:
self._migrate(cmd_args.project_dir)
elif cmd_args.migrate_apps:
for app in cmd_args.migrate_apps:
self._migrate(cmd_args.project_dir, app)
if cmd_args.mount_apps: if cmd_args.mount_apps:
for app, route in cmd_args.mount_apps: for app, route in cmd_args.mount_apps:
try: try:
@@ -108,7 +130,10 @@ class Program(object): # pylint: disable=too-few-public-methods
for e in exceptions: for e in exceptions:
if e.message: if e.message:
sys.stderr.write('{}\n'.format(e.message)) msg = str(e.message)
if not msg.endswith('\n'):
msg += '\n'
sys.stderr.write(msg)
elif e.code: # pragma: no cover elif e.code: # pragma: no cover
sys.stderr.write('{}\n'.format(os.strerror(e.code))) sys.stderr.write('{}\n'.format(os.strerror(e.code)))
exitval = e.exitval or os.EX_SOFTWARE exitval = e.exitval or os.EX_SOFTWARE

View File

@@ -1,2 +1,8 @@
INSTALLED_APPS = ['django_deploy.tests.fake_app1'] INSTALLED_APPS = ['django_deploy.tests.fake_app1']
ROOT_URLCONF = '.urls' ROOT_URLCONF = '.urls'
SETTINGS = """WSGI_APPLICATION = 'django_deploy.tests.fake_app1.wsgi.application'
FAKE_APP1_TEST_SETTING = \"\"\"Test
setting from
fake_app1
\"\"\"
"""

View File

@@ -0,0 +1,19 @@
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='MyModel',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('chars', models.CharField(max_length=10)),
],
),
]

View File

@@ -0,0 +1,13 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models
class MyModel(models.Model): # pylint: disable=model-has-unicode
chars = models.CharField(max_length=10)
def __str__(self):
return self.chars
def __unicode__(self):
return self.__str__()

View File

@@ -1,2 +1,4 @@
INSTALLED_APPS = ['django_deploy.tests.fake_app1', 'django_deploy.tests.fake_app2'] INSTALLED_APPS = ['django_deploy.tests.fake_app1', 'django_deploy.tests.fake_app2']
ROOT_URLCONF = 'django_deploy.tests.fake_app2.urls' ROOT_URLCONF = 'django_deploy.tests.fake_app2.urls'
SETTINGS = """WSGI_APPLICATION = 'django_deploy.tests.fake_app2.wsgi.application'
"""

View File

@@ -1,5 +1,6 @@
import importlib import importlib
import os import os
import subprocess
import sys import sys
import unittest import unittest
import mock import mock
@@ -80,6 +81,38 @@ class DjangoDeployTestCase(unittest.TestCase):
haystack = f.read() haystack = f.read()
self.assertNotIn(needle, haystack) self.assertNotIn(needle, haystack)
def assert_text_in_settings(self, project_dir, settings_text):
settings_dir = os.path.join(project_dir, DJANGO_SETTINGS_DIR)
settings_file = os.path.join(settings_dir, 'settings.py')
with open(settings_file, 'r') as f:
complete_settings = f.read()
self.assertIn(settings_text, complete_settings)
def assert_values_in_settings(self, project_dir, settings_dict):
real_settings = self.get_django_settings(project_dir)
for key in settings_dict:
self.assertTrue(hasattr(real_settings, key))
expected_value = settings_dict[key]
real_value = getattr(real_settings, key)
self.assertEqual(expected_value, real_value)
def assert_settings_from_apps(self, project_dir, apps):
hooks_config_name = 'django_deploy_hooks'
wsgi_application_provider = 'main'
for app in apps:
try:
hooks_config = importlib.import_module('{}.{}'.format(app, hooks_config_name))
except ImportError: # pragma: no cover
continue
if not hasattr(hooks_config, 'SETTINGS'):
continue
settings_text = getattr(hooks_config, 'SETTINGS')
self.assert_text_in_settings(project_dir, settings_text)
wsgi_application_provider = app
wsgi_application = '{}.wsgi.application'.format(wsgi_application_provider)
self.assert_values_in_settings(project_dir, {'WSGI_APPLICATION': wsgi_application})
def assert_installed_apps(self, project_dir, apps, default_apps=None): def assert_installed_apps(self, project_dir, apps, default_apps=None):
settings = self.get_django_settings(project_dir) settings = self.get_django_settings(project_dir)
@@ -98,6 +131,7 @@ class DjangoDeployTestCase(unittest.TestCase):
expected_apps += apps expected_apps += apps
self.assertListEqual(expected_apps, settings.INSTALLED_APPS) self.assertListEqual(expected_apps, settings.INSTALLED_APPS)
self.assert_settings_from_apps(project_dir, apps)
def assert_urlpatterns(self, project_dir, patterns): def assert_urlpatterns(self, project_dir, patterns):
root_urlconf = self.get_django_root_urlconf(project_dir) root_urlconf = self.get_django_root_urlconf(project_dir)
@@ -135,3 +169,17 @@ class DjangoDeployTestCase(unittest.TestCase):
self.assertEqual(expected[2], real.urlconf_name.__name__) self.assertEqual(expected[2], real.urlconf_name.__name__)
else: # pragma: no cover else: # pragma: no cover
self.fail('Unknown urlpattern class: {}'.format(real_class_name)) self.fail('Unknown urlpattern class: {}'.format(real_class_name))
def assert_django_database_migration(self, project_dir, app_label=None, migration=None):
management_script = os.path.join(project_dir, 'manage.py')
cmd = [
management_script, 'migrate', '--no-color', '--noinput'
]
if app_label is not None:
cmd.append(app_label)
if migration is not None:
cmd.append(migration)
output = subprocess.check_output(cmd)
text = output.decode('utf8')
self.assertTrue(text.endswith('No migrations to apply.\n'))

View File

@@ -1,10 +1,11 @@
import errno import errno
import os import os
import django
from ..api import DjangoProjectHooksConfig, DjangoProject from ..api import DjangoProjectHooksConfig, DjangoProject
from ..exceptions import DjangoDeployError from ..exceptions import DjangoDeployError
from .base import DjangoDeployTestCase from .generic import DjangoDeployTestCase
class DjangoProjectHooksConfigTestCase(DjangoDeployTestCase): class DjangoProjectHooksConfigTestCase(DjangoDeployTestCase):
@@ -15,10 +16,12 @@ class DjangoProjectHooksConfigTestCase(DjangoDeployTestCase):
DjangoProjectHooksConfig(project_dir='.', settings_dir='.') DjangoProjectHooksConfig(project_dir='.', settings_dir='.')
class DjangoProjectTestCase(DjangoDeployTestCase): class AbstractDjangoProjectTestCase(DjangoDeployTestCase):
def setUp(self): def setUp(self):
self._tmp_dir = str(self.tmpdir) self._tmp_dir = str(self.tmpdir)
class InitDjangoProjectTestCase(AbstractDjangoProjectTestCase):
def test_init(self): def test_init(self):
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
DjangoProject() # pylint: disable=no-value-for-parameter DjangoProject() # pylint: disable=no-value-for-parameter
@@ -31,6 +34,8 @@ class DjangoProjectTestCase(DjangoDeployTestCase):
os.mknod(project_dir) os.mknod(project_dir)
DjangoProject(project_dir) DjangoProject(project_dir)
class DjangoProjectCreateTestCase(AbstractDjangoProjectTestCase):
def test_create(self): def test_create(self):
# the parent of the to-be-created project dir shall also not exist. # the parent of the to-be-created project dir shall also not exist.
project_dir = os.path.join(self._tmp_dir, 'new', 'sub', 'sub') project_dir = os.path.join(self._tmp_dir, 'new', 'sub', 'sub')
@@ -80,6 +85,8 @@ class DjangoProjectTestCase(DjangoDeployTestCase):
else: # pragma: no cover else: # pragma: no cover
self.fail('DjangoDeployError not raised') self.fail('DjangoDeployError not raised')
class DjangoProjectInstallHooksTestCase(AbstractDjangoProjectTestCase):
def test_install_hooks(self): def test_install_hooks(self):
project_dir = os.path.join(self._tmp_dir, 'new') project_dir = os.path.join(self._tmp_dir, 'new')
project = DjangoProject(project_dir) project = DjangoProject(project_dir)
@@ -119,24 +126,58 @@ class DjangoProjectTestCase(DjangoDeployTestCase):
else: # pragma: no cover else: # pragma: no cover
self.fail('DjangoDeployError not raised') self.fail('DjangoDeployError not raised')
def test_install_app(self):
class DjangoProjectAddAppSettingsTestCase(AbstractDjangoProjectTestCase):
def test_add_app_settings_with_create(self):
project_dir = self._tmp_dir project_dir = self._tmp_dir
project = DjangoProject(project_dir) project = DjangoProject(project_dir)
project.create() project.create()
project.install_app('django_deploy.tests.fake_app1') apps = [
installed_apps = [
'django_deploy.tests.fake_app1', 'django_deploy.tests.fake_app1',
'django_deploy.tests.fake_app2',
'django_deploy',
] ]
self.assert_installed_apps(project_dir, installed_apps) for app in apps:
project.add_app_settings(app)
self.assert_settings_from_apps(project_dir, apps)
def test_add_app_settings_without_create(self):
project_dir = self._tmp_dir
project = DjangoProject(project_dir)
apps = [
'django_deploy.tests.fake_app1',
'django_deploy.tests.fake_app2',
'django_deploy',
]
for app in apps:
project.add_app_settings(app)
self.assert_settings_from_apps(project_dir, apps)
class DjangoProjectInstallAppTestCase(AbstractDjangoProjectTestCase):
def test_install_app(self):
project_dir = self._tmp_dir
project = DjangoProject(project_dir)
app = 'django_deploy.tests.fake_app1'
project.install_app(app)
self.assert_installed_apps(project_dir, [app])
def test_install_apps(self): def test_install_apps(self):
project_dir = self._tmp_dir project_dir = self._tmp_dir
project = DjangoProject(project_dir) project = DjangoProject(project_dir)
project.create()
project.install_app('django_deploy.tests.fake_app1') apps = [
project.install_app('django_deploy.tests.fake_app1') 'django_deploy.tests.fake_app1',
project.install_app('django_deploy.tests.fake_app2') 'django_deploy.tests.fake_app1',
'django_deploy.tests.fake_app2',
]
for app in apps:
project.install_app(app)
installed_apps = [ installed_apps = [
'django_deploy.tests.fake_app1', 'django_deploy.tests.fake_app1',
@@ -148,7 +189,6 @@ class DjangoProjectTestCase(DjangoDeployTestCase):
def test_install_nonexisting_app(self): def test_install_nonexisting_app(self):
project_dir = self._tmp_dir project_dir = self._tmp_dir
project = DjangoProject(project_dir) project = DjangoProject(project_dir)
project.create()
try: try:
project.install_app('django_deploy.tests.fake_app0') project.install_app('django_deploy.tests.fake_app0')
except DjangoDeployError as e: except DjangoDeployError as e:
@@ -159,7 +199,6 @@ class DjangoProjectTestCase(DjangoDeployTestCase):
def test_install_nonconforming_app(self): def test_install_nonconforming_app(self):
project_dir = self._tmp_dir project_dir = self._tmp_dir
project = DjangoProject(project_dir) project = DjangoProject(project_dir)
project.create()
try: try:
project.install_app('django_deploy.tests.fake_app1', hooks_config_name='non_existing_submodule') project.install_app('django_deploy.tests.fake_app1', hooks_config_name='non_existing_submodule')
except DjangoDeployError as e: except DjangoDeployError as e:
@@ -167,10 +206,11 @@ class DjangoProjectTestCase(DjangoDeployTestCase):
else: # pragma: no cover else: # pragma: no cover
self.fail('DjangoDeployError not raised') self.fail('DjangoDeployError not raised')
class DjangoProjectMountAppTestCase(AbstractDjangoProjectTestCase):
def test_mount_app(self): def test_mount_app(self):
project_dir = self._tmp_dir project_dir = self._tmp_dir
project = DjangoProject(project_dir) project = DjangoProject(project_dir)
project.create()
project.mount_app('django_deploy.tests.fake_app1', '') project.mount_app('django_deploy.tests.fake_app1', '')
expected_urlpatterns = [ expected_urlpatterns = [
@@ -181,7 +221,6 @@ class DjangoProjectTestCase(DjangoDeployTestCase):
def test_mount_apps(self): def test_mount_apps(self):
project_dir = self._tmp_dir project_dir = self._tmp_dir
project = DjangoProject(project_dir) project = DjangoProject(project_dir)
project.create()
project.mount_app('django_deploy.tests.fake_app1', 'app1') project.mount_app('django_deploy.tests.fake_app1', 'app1')
project.mount_app('django_deploy.tests.fake_app1', 'app1') project.mount_app('django_deploy.tests.fake_app1', 'app1')
project.mount_app('django_deploy.tests.fake_app2', 'app2/') project.mount_app('django_deploy.tests.fake_app2', 'app2/')
@@ -201,7 +240,6 @@ class DjangoProjectTestCase(DjangoDeployTestCase):
def test_mount_unmountable_app(self): def test_mount_unmountable_app(self):
project_dir = self._tmp_dir project_dir = self._tmp_dir
project = DjangoProject(project_dir) project = DjangoProject(project_dir)
project.create()
try: try:
project.mount_app('django_deploy', '/') project.mount_app('django_deploy', '/')
@@ -209,3 +247,71 @@ class DjangoProjectTestCase(DjangoDeployTestCase):
self.assertEqual(errno.ENOLINK, e.code) self.assertEqual(errno.ENOLINK, e.code)
else: # pragma: no cover else: # pragma: no cover
self.fail('DjangoDeployError not raised') self.fail('DjangoDeployError not raised')
class DjangoProjectMigrateAppTestCase(AbstractDjangoProjectTestCase):
def test_migrate_all_apps(self):
project_dir = self._tmp_dir
project = DjangoProject(project_dir)
project.install_app('django_deploy.tests.fake_app1')
project.install_app('django_deploy.tests.fake_app2')
project.migrate_apps([])
self.assert_django_database_migration(project_dir)
def test_migrate_app_by_app_name(self):
project_dir = self._tmp_dir
project = DjangoProject(project_dir)
project.install_app('django_deploy.tests.fake_app1')
project.migrate_apps(['django_deploy.tests.fake_app1'])
self.assert_django_database_migration(project_dir, 'fake_app1')
def test_migrate_app_by_app_label(self):
project_dir = self._tmp_dir
project = DjangoProject(project_dir)
project.install_app('django_deploy.tests.fake_app1')
project.migrate_apps(['fake_app1'])
self.assert_django_database_migration(project_dir, 'fake_app1')
def test_migrate_app_not_installed(self):
project_dir = self._tmp_dir
project = DjangoProject(project_dir)
project.create()
try:
project.migrate_apps(['django_deploy.tests.fake_app1'])
except DjangoDeployError as e:
if django.VERSION[0] < 2: # pragma: no cover
expected_msg = "CommandError: App 'fake_app1' does not have migrations.\n"
else: # pragma: no cover
expected_msg = "CommandError: No installed app with label 'fake_app1'.\n"
self.assertEqual(expected_msg, e.message)
else: # pragma: no cover
self.fail('DjangoDeployError not raised')
def test_migrate_app_without_create(self):
project_dir = self._tmp_dir
project = DjangoProject(project_dir)
try:
project.migrate_apps(['fake_app1'])
except DjangoDeployError as e:
self.assertTrue(e.message.startswith('No such file: '))
self.assertTrue(e.message.endswith('Have you created the django project?'))
self.assertEqual(errno.ENOENT, e.code)
else: # pragma: no cover
self.fail('DjangoDeployError not raised')
def test_migrate_app_without_migrations(self):
project_dir = self._tmp_dir
project = DjangoProject(project_dir)
project.install_app('django_deploy.tests.fake_app2')
try:
project.migrate_apps(['django_deploy.tests.fake_app2'])
except DjangoDeployError as e:
self.assertEqual(e.message, "CommandError: App 'fake_app2' does not have migrations.\n")
else: # pragma: no cover
self.fail('DjangoDeployError not raised')

View File

@@ -1,16 +1,24 @@
import os import os
import django
import pytest
from ..config import DJANGO_SETTINGS_DIR from ..config import DJANGO_SETTINGS_DIR
from ..program import Program from ..program import Program
from .base import DjangoDeployTestCase from .generic import DjangoDeployTestCase
class ProgramTestCase(DjangoDeployTestCase): class AbstractProgramTestCase(DjangoDeployTestCase):
@pytest.fixture(autouse=True)
def capsys(self, capsys): # pylint: disable=method-hidden
self.capsys = capsys
def setUp(self): def setUp(self):
self._program = Program() self._program = Program()
self._tmp_dir = str(self.tmpdir) self._tmp_dir = str(self.tmpdir)
class ProgramCreateTestCase(AbstractProgramTestCase):
def test_create(self): def test_create(self):
project_dir = os.path.join(self._tmp_dir, 'new') project_dir = os.path.join(self._tmp_dir, 'new')
exitval = self._program(argv=['--create', project_dir]) exitval = self._program(argv=['--create', project_dir])
@@ -66,10 +74,11 @@ class ProgramTestCase(DjangoDeployTestCase):
exitval = self._program(argv=['--install-hooks', project_dir]) exitval = self._program(argv=['--install-hooks', project_dir])
self.assertEqual(os.EX_SOFTWARE, exitval) self.assertEqual(os.EX_SOFTWARE, exitval)
class ProgramInstallTestCase(AbstractProgramTestCase):
def test_install_apps(self): def test_install_apps(self):
project_dir = self._tmp_dir project_dir = self._tmp_dir
argv = [ argv = [
'-c',
'--install-app', 'django_deploy.tests.fake_app1', '--install-app', 'django_deploy.tests.fake_app1',
'--install-app', 'django_deploy.tests.fake_app1', '--install-app', 'django_deploy.tests.fake_app1',
'-a', 'django_deploy.tests.fake_app2', '-a', 'django_deploy.tests.fake_app2',
@@ -87,10 +96,19 @@ class ProgramTestCase(DjangoDeployTestCase):
self.assert_installed_apps(project_dir, installed_apps) self.assert_installed_apps(project_dir, installed_apps)
self.assert_urlpatterns(project_dir, []) self.assert_urlpatterns(project_dir, [])
def test_install_apps_with_error(self): def test_install_with_explicit_create(self):
project_dir = self._tmp_dir project_dir = self._tmp_dir
argv = [ argv = [
'-c', '-c',
'--install-app', 'django_deploy.tests.fake_app1',
project_dir
]
exitval = self._program(argv=argv)
self.assertEqual(os.EX_OK, exitval)
def test_install_apps_with_error(self):
project_dir = self._tmp_dir
argv = [
'-a', 'django_deploy.tests.fake_app1', '-a', 'django_deploy.tests.fake_app1',
'-a', 'django_deploy.tests.fake_app0', '-a', 'django_deploy.tests.fake_app0',
'-a', 'django_deploy.tests.fake_app2', '-a', 'django_deploy.tests.fake_app2',
@@ -106,10 +124,11 @@ class ProgramTestCase(DjangoDeployTestCase):
self.assert_installed_apps(project_dir, installed_apps) self.assert_installed_apps(project_dir, installed_apps)
class ProgramMountTestCase(AbstractProgramTestCase):
def test_mount_apps(self): def test_mount_apps(self):
project_dir = self._tmp_dir project_dir = self._tmp_dir
argv = [ argv = [
'-c',
'--mount-app', 'django_deploy.tests.fake_app1', 'app1', '--mount-app', 'django_deploy.tests.fake_app1', 'app1',
'--mount-app', 'django_deploy.tests.fake_app1', 'app1', '--mount-app', 'django_deploy.tests.fake_app1', 'app1',
'-m', 'django_deploy.tests.fake_app2', 'app2/', '-m', 'django_deploy.tests.fake_app2', 'app2/',
@@ -136,7 +155,6 @@ class ProgramTestCase(DjangoDeployTestCase):
def test_mount_apps_with_errors(self): def test_mount_apps_with_errors(self):
project_dir = self._tmp_dir project_dir = self._tmp_dir
self._program(argv=['-c', project_dir])
argv = [ argv = [
'-m', 'django_deploy.tests.fake_app1', '/', '-m', 'django_deploy.tests.fake_app1', '/',
'-m', 'django_deploy.tests.fake_app0', 'app0', '-m', 'django_deploy.tests.fake_app0', 'app0',
@@ -153,3 +171,102 @@ class ProgramTestCase(DjangoDeployTestCase):
] ]
self.assert_urlpatterns(project_dir, expected_urlpatterns) self.assert_urlpatterns(project_dir, expected_urlpatterns)
class ProgramMigrateTestCase(AbstractProgramTestCase):
def test_migrate_all(self):
project_dir = self._tmp_dir
argv = [
'--install-app', 'django_deploy.tests.fake_app1',
'--install-app', 'django_deploy.tests.fake_app2',
'--migrate',
project_dir
]
exitval = self._program(argv=argv)
self.assertEqual(os.EX_OK, exitval)
self.assert_django_database_migration(project_dir)
def test_migrate_app_by_app_name(self):
project_dir = self._tmp_dir
argv = [
'--install-app', 'django_deploy.tests.fake_app1',
'--migrate-app', 'django_deploy.tests.fake_app1',
project_dir
]
exitval = self._program(argv=argv)
self.assertEqual(os.EX_OK, exitval)
self.assert_django_database_migration(project_dir, 'fake_app1')
def test_migrate_app_by_app_label(self):
project_dir = self._tmp_dir
argv = [
'--install-app', 'django_deploy.tests.fake_app1',
'--migrate-app', 'fake_app1',
project_dir
]
exitval = self._program(argv=argv)
self.assertEqual(os.EX_OK, exitval)
self.assert_django_database_migration(project_dir, 'fake_app1')
def test_migrate_app_not_installed(self):
project_dir = self._tmp_dir
argv = [
'-c',
'--migrate-app', 'django_deploy.tests.fake_app1',
project_dir
]
exitval = self._program(argv=argv)
self.assertNotEqual(os.EX_OK, exitval)
captured = self.capsys.readouterr()
if django.VERSION[0] < 2: # pragma: no cover
expected_msg = "CommandError: App 'fake_app1' does not have migrations.\n"
else: # pragma: no cover
expected_msg = "CommandError: No installed app with label 'fake_app1'.\n"
self.assertEqual(expected_msg, captured.err)
def test_migrate_app_without_create(self):
project_dir = self._tmp_dir
argv = [
'--migrate-app', 'django_deploy.tests.fake_app1',
project_dir
]
exitval = self._program(argv=argv)
self.assertNotEqual(os.EX_OK, exitval)
captured = self.capsys.readouterr()
self.assertTrue(captured.err.endswith('Have you created the django project?\n'))
def test_migrate_app_without_migrations(self):
project_dir = self._tmp_dir
argv = [
'--install-app', 'django_deploy.tests.fake_app1',
'--install-app', 'django_deploy.tests.fake_app2',
'--migrate-app', 'fake_app2',
'--migrate-app', 'fake_app1',
project_dir
]
exitval = self._program(argv=argv)
self.assertNotEqual(os.EX_OK, exitval)
captured = self.capsys.readouterr()
self.assertEqual("CommandError: App 'fake_app2' does not have migrations.\n", captured.err)
def test_migrate_app_stop_on_first_error(self):
project_dir = self._tmp_dir
argv = [
'--install-app', 'django_deploy.tests.fake_app1',
'--install-app', 'django_deploy.tests.fake_app2',
'--migrate-app', 'fake_app2',
'--migrate-app', 'fake_app1',
'--migrate-app', 'django_deploy',
project_dir
]
exitval = self._program(argv=argv)
self.assertNotEqual(os.EX_OK, exitval)
captured = self.capsys.readouterr()
self.assertEqual("CommandError: App 'fake_app2' does not have migrations.\n", captured.err)
try:
self.assert_django_database_migration(project_dir, 'fake_app1')
except AssertionError:
pass
else: # pragma: no cover
self.fail('Unexcepted migrations done')

10
tox.ini
View File

@@ -1,10 +1,14 @@
[tox] [tox]
envlist = py3,py2 envlist = py3-django3,py3-django2,py2-django1
[testenv] [testenv]
deps = coverage deps = coverage
py2: pylint-django<2 py2-django1: django<2
py3: pylint-django py2-django1: pylint-django<2
py3-django2: django<3
py3-django2: pylint-django
py3-django3: django
py3-django3: pylint-django
pytest pytest
mock mock
commands = coverage run -m pytest commands = coverage run -m pytest