Commits

Mikhail Korobov committed 95ebc27

Improved tests (they are passed on Ubuntu now, 84% test coverage)

Comments (0)

Files changed (22)

 - Ubuntu 10.10 maverick initial support (need better testing);
 - `fabtest <https://bitbucket.org/kmike/fabtest>`_ package is extracted
   from the test suite;
-- more and better tests;
+- improved tests;
 - :func:`fab_deploy.system.ssh_add_key` can now add ssh key even
-  if is is the first key for user
+  if is is the first key for user;
+- 'print' calls are replaced with 'puts' calls in fabfile commands.
 
 0.4.2 (2011-02-16)
 ------------------
     cd fab_deploy_tests
     ./runtests.py <VM name or uid> <what to run>
 
-<what to run> can be ``fast``, ``slow`` or any value acceptable by
-``unittest.main()`` (e.g. a list of test cases).
+<what to run> can be ``fast``, ``slow``, ``all``, ``prepare`` or any
+value acceptable by ``unittest.main()`` (e.g. a list of test cases).
 
 Some tests require additional prepared snapshots in order to greatly speedup
 test execution. But there is a chicken or the egg dilemma: these
 snapshots can be only taken if software works fine for the VM (at least
-tests are passed). So tests are divided into 'slow' and 'fast' suites.
+tests are passed). So there is a very slow ``prepare`` test suite that ensures
+preparing will work.
 
 1. make sure slow tests are passing::
 
-       ./runtests.py "VM_NAME" slow
+       ./runtests.py "VM_NAME" prepare
 
 2. prepare snapshots::
 
        ./preparevm "VM_NAME"
 
-3. faster tests can be run now::
+3. tests can be run now::
 
        ./runtests.py "VM_NAME" fast
 

fab_deploy_tests/runtests.py

     FabDeployTest.vm_name = sys.argv[1]
     common_tests = load([BasicTest, SshTest, MysqlTest, CrontabTest])
     suites = {
-        'fast': TestSuite(common_tests + load([FastPrepareServerTest])),
-        'slow': TestSuite(common_tests + load([PrepareServerTest])),
+        'fast': TestSuite(common_tests + load([FastPrepareServerTest, ApacheSetupTest])),
+        'slow': TestSuite(load([DeployTest])),
+        'prepare': TestSuite(common_tests + load([PrepareServerTest])),
     }
+    suites['all'] = TestSuite([suites['fast'], suites['slow']])
 
     suite_name = sys.argv[2]
     if suite_name in suites:
Add a comment to this file

fab_deploy_tests/test_project/__init__.py

Empty file added.

fab_deploy_tests/test_project/config.py

+DATABASES = {
+    'default': {
+        'ENGINE': 'django.db.backends.sqlite3',
+        'NAME': 'testdb.sqlite',
+    }
+}
+INSTANCE_NAME = 'local'

fab_deploy_tests/test_project/config.server.py

+# my_project/config.server.py
+# config file for environment-specific settings
+
+DEBUG = True
+DATABASES = {
+    'default': {
+        'ENGINE': 'django.db.backends.mysql',
+        'NAME': '{{ DB_NAME }}',
+        'USER': '{{ DB_USER }}',
+        'PASSWORD': '{{ DB_PASSWORD }}',
+        'HOST': '',
+        'PORT': '',
+        'OPTIONS': {
+            "init_command": "SET storage_engine=INNODB"
+        },
+    }
+}
+INSTANCE_NAME = '{{ INSTANCE_NAME }}'

fab_deploy_tests/test_project/config_templates/apache.config

+NameVirtualHost 127.0.0.1:{{ APACHE_PORT }}
+<VirtualHost 127.0.0.1:{{ APACHE_PORT }}>
+    ServerName {{ SERVER_NAME }}
+    ServerAlias www.{{ SERVER_NAME }}
+    ServerAdmin {{ SERVER_ADMIN }}
+
+    WSGIDaemonProcess {{ INSTANCE_NAME }} user={{ USER }} group={{ USER }} processes={{ PROCESSES }} threads={{ THREADS }}
+    WSGIProcessGroup {{ INSTANCE_NAME }}
+
+    WSGIScriptAlias / {{ ENV_DIR }}/var/wsgi/{{ INSTANCE_NAME }}.py
+    <Directory {{ ENV_DIR }}/var/wsgi/>
+        Order deny,allow
+        allow from all
+    </Directory>
+
+    ErrorLog /var/log/apache2/{{ INSTANCE_NAME }}-error.log
+    ErrorDocument 500 {{ SRC_DIR }}/templates/500.html
+
+    # Possible values include: debug, info, notice, warn, error, crit, alert, emerg
+    LogLevel error
+</VirtualHost>

fab_deploy_tests/test_project/config_templates/django_wsgi.py

+import os
+import sys
+import site
+
+# prevent errors with 'print' commands
+sys.stdout = sys.stderr
+
+# adopted from http://code.google.com/p/modwsgi/wiki/VirtualEnvironments
+def add_to_path(dirs):
+    # Remember original sys.path.
+    prev_sys_path = list(sys.path)
+
+    # Add each new site-packages directory.
+    for directory in dirs:
+        site.addsitedir(directory)
+
+    # Reorder sys.path so new directories at the front.
+    new_sys_path = []
+    for item in list(sys.path):
+        if item not in prev_sys_path:
+            new_sys_path.append(item)
+            sys.path.remove(item)
+    sys.path[:0] = new_sys_path
+
+add_to_path([
+     os.path.normpath('{{ ENV_DIR }}/lib/python2.5/site-packages'),
+     os.path.normpath('{{ ENV_DIR }}/lib/python2.6/site-packages'),
+     os.path.normpath('{{ SRC_DIR }}' + '/..'),
+     '{{ SRC_DIR }}'
+])
+
+os.environ['DJANGO_SETTINGS_MODULE'] = '{{ INSTANCE_NAME }}.settings'
+
+#print sys.path
+
+import django.core.handlers.wsgi
+application = django.core.handlers.wsgi.WSGIHandler()

fab_deploy_tests/test_project/config_templates/hgrc

+# [paths]
+# bitbucket = ssh://hg@bitbucket.org/me/myrepo/
+
+# [hooks]
+# changegroup.push = screen -d -m hg push bitbucket

fab_deploy_tests/test_project/config_templates/nginx.config

+server {
+    listen 80;
+    server_name {{ SERVER_NAME }} www.{{ SERVER_NAME }};
+    access_log /var/log/nginx/{{ SERVER_NAME }}.access.log;
+    charset utf-8;
+    client_max_body_size 8m;
+
+    gzip_types text/plain text/xml text/css application/javascript application/x-javascript;
+
+    location / {
+        proxy_pass http://localhost:{{ APACHE_PORT }};
+        proxy_set_header Host $host;
+        proxy_set_header X-Real-IP $remote_addr;
+        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+    }
+
+    # change /static to your static folder name
+    location /static {
+        root {{ SRC_DIR }};
+        autoindex off;
+        expires 1M;
+    }
+
+    # admin media serving
+    # (this location is available if django is installed from svn)
+    location /media {
+        root {{ ENV_DIR }}/src/django/django/contrib/admin;
+        expires 10m;
+        autoindex off;
+    }
+
+    # alternative django admin media path
+#    location /static/admin {
+#        root {{ SRC_DIR }};
+#        autoindex off;
+#        expires 10m;
+#    }
+
+    #error_page  404  /404.html;
+
+    # redirect server error pages to the static page /50x.html
+    error_page   500 502 503 504  /50x.html;
+    location = /50x.html {
+        root   /var/www/nginx-default;
+    }
+}

fab_deploy_tests/test_project/fabfile.py

+from fab_deploy import *
+
+def foo_site():
+    env.hosts = ['foo@127.0.0.1:2222']
+    env.conf = dict(
+        DB_PASSWORD = '123',
+        VCS = 'none',
+        SERVER_NAME = 'foo.example.com'
+    )
+    update_env()
+
+def bar_site():
+    env.hosts = ['foo@127.0.0.1:2222']
+    env.conf = dict(
+        DB_PASSWORD = '123',
+        VCS = 'none',
+        SERVER_NAME = 'bar.example.com',
+        INSTANCE_NAME = 'bar',
+    )
+    update_env()

fab_deploy_tests/test_project/manage.py

+#!/usr/bin/env python
+from django.core.management import execute_manager
+try:
+    import settings # Assumed to be in the same directory.
+except ImportError:
+    import sys
+    sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
+    sys.exit(1)
+
+if __name__ == "__main__":
+    execute_manager(settings)

fab_deploy_tests/test_project/reqs/all.txt

+# this is just an example how requirements can be handled
+-r basic.txt
+-r django.txt
+-r apps.txt

fab_deploy_tests/test_project/reqs/apps.txt

+# django apps and related python packages, e.g. django-registration

fab_deploy_tests/test_project/reqs/basic.txt

+# packages that are widely used and rarely updated
+# PIL
+mysql-python >= 1.2

fab_deploy_tests/test_project/reqs/django.txt

+# django 1.2 svn
+# -e svn+http://code.djangoproject.com/svn/django/branches/releases/1.2.X#egg=Django
+
+django==1.2.5

fab_deploy_tests/test_project/settings.py

+DEBUG = True
+TEMPLATE_DEBUG = DEBUG
+
+ADMINS = (
+    # ('Your Name', 'your_email@domain.com'),
+)
+
+MANAGERS = ADMINS
+
+from config import *
+
+# Local time zone for this installation. Choices can be found here:
+# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
+# although not all choices may be available on all operating systems.
+# On Unix systems, a value of None will cause Django to use the same
+# timezone as the operating system.
+# If running in a Windows environment this must be set to the same as your
+# system time zone.
+TIME_ZONE = 'America/Chicago'
+
+# Language code for this installation. All choices can be found here:
+# http://www.i18nguy.com/unicode/language-identifiers.html
+LANGUAGE_CODE = 'en-us'
+
+SITE_ID = 1
+
+# If you set this to False, Django will make some optimizations so as not
+# to load the internationalization machinery.
+USE_I18N = True
+
+# If you set this to False, Django will not format dates, numbers and
+# calendars according to the current locale
+USE_L10N = True
+
+# Absolute filesystem path to the directory that will hold user-uploaded files.
+# Example: "/home/media/media.lawrence.com/"
+MEDIA_ROOT = ''
+
+# URL that handles the media served from MEDIA_ROOT. Make sure to use a
+# trailing slash if there is a path component (optional in other cases).
+# Examples: "http://media.lawrence.com", "http://example.com/media/"
+MEDIA_URL = ''
+
+# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
+# trailing slash.
+# Examples: "http://foo.com/media/", "/media/".
+ADMIN_MEDIA_PREFIX = '/media/'
+
+# Make this unique, and don't share it with anybody.
+SECRET_KEY = 'kdpm23v31@#u63@8!eq6%%^t8z=^ak2g6uyjbc!_6bs(!48(+^'
+
+# List of callables that know how to import templates from various sources.
+TEMPLATE_LOADERS = (
+    'django.template.loaders.filesystem.Loader',
+    'django.template.loaders.app_directories.Loader',
+#     'django.template.loaders.eggs.Loader',
+)
+
+MIDDLEWARE_CLASSES = (
+    'django.middleware.common.CommonMiddleware',
+    'django.contrib.sessions.middleware.SessionMiddleware',
+    'django.middleware.csrf.CsrfViewMiddleware',
+    'django.contrib.auth.middleware.AuthenticationMiddleware',
+    'django.contrib.messages.middleware.MessageMiddleware',
+)
+
+ROOT_URLCONF = 'urls'
+
+TEMPLATE_DIRS = (
+    # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
+    # Always use forward slashes, even on Windows.
+    # Don't forget to use absolute paths, not relative paths.
+)
+
+INSTALLED_APPS = (
+    'django.contrib.auth',
+    'django.contrib.contenttypes',
+    'django.contrib.sessions',
+    'django.contrib.sites',
+    'django.contrib.messages',
+    'django.contrib.admin',
+)

fab_deploy_tests/test_project/templates/404.html

+404

fab_deploy_tests/test_project/templates/500.html

+500

fab_deploy_tests/test_project/urls.py

+from django.conf.urls.defaults import *
+from django.http import HttpResponse
+from django.conf import settings
+
+from django.contrib import admin
+admin.autodiscover()
+
+urlpatterns = patterns('',
+    (r'^admin/', include(admin.site.urls)),
+    (r'^instance/', lambda r: HttpResponse(settings.INSTANCE_NAME)),
+)

fab_deploy_tests/tests/__init__.py

 from .system_tests import *
 from .utils_tests import *
 from .crontab import *
+from .deploy import *

fab_deploy_tests/tests/deploy.py

+from __future__ import absolute_import
+import os
+from fab_deploy.utils import run_as
+from fabric.api import *
+from fabtest import fab, vbox_urlopen
+from fab_deploy.deploy import deploy_project
+from fab_deploy.mysql import mysql_create_db
+from fab_deploy.apache import apache_make_config, apache_make_wsgi, apache_restart, apache_install
+from .base import FabDeployTest
+from ..test_project.fabfile import foo_site, bar_site
+
+def get_file_content(remote_file):
+    @run_as('root')
+    def cat():
+        with(hide('stdout')):
+            return run('cat '+remote_file)
+    return fab(cat)[0]
+
+def get_ports():
+    @run_as('root')
+    def ports():
+        return run('netstat -A inet -lnp')
+    return fab(ports)[0]
+
+def is_local_port_binded(port):
+    port_string = '127.0.0.1:%s' % port
+    ports = get_ports()
+    return port_string in ports
+
+class FabDeployProjectTest(FabDeployTest):
+    snapshot = 'fabtest-prepared-server'
+
+    def setup_conf(self):
+        self.cwd = os.getcwd()
+        os.chdir('test_project')
+        fab(foo_site)
+
+    def tearDown(self):
+        os.chdir(self.cwd)
+        super(FabDeployProjectTest, self).tearDown()
+
+
+class ApacheSetupTest(FabDeployProjectTest):
+
+    def assertPortBinded(self, port):
+        self.assertTrue(is_local_port_binded(port))
+
+    def assertPortNotBinded(self, port):
+        self.assertFalse(is_local_port_binded(port))
+
+    def test_apache_config(self):
+        fab(apache_install)
+
+        # first site
+        fab(foo_site)
+        fab(apache_make_config)
+
+        foo_port = env.conf.APACHE_PORT
+        self.assertPortNotBinded(foo_port)
+        self.assertTrue(get_file_content('/etc/apache2/sites-enabled/foo'))
+
+        fab(apache_restart)
+        self.assertPortBinded(foo_port)
+
+        # second site
+        fab(bar_site)
+        fab(apache_make_config)
+
+        bar_port = env.conf.APACHE_PORT
+        self.assertNotEqual(foo_port, bar_port)
+        self.assertPortNotBinded(bar_port)
+
+        fab(apache_restart)
+        self.assertPortBinded(bar_port)
+
+        # re-configuring doesn't lead to errors
+        fab(apache_make_config)
+        fab(apache_restart)
+        self.assertPortBinded(bar_port)
+
+    def test_apache_make_wsgi(self):
+        fab(apache_make_wsgi)
+        wsgi_file = get_file_content(env.conf.ENV_DIR+'/var/wsgi/foo.py')
+        self.assertTrue(wsgi_file)
+
+
+class DeployTest(FabDeployProjectTest):
+
+    def assertResponse(self, url, data):
+        self.assertEqual(vbox_urlopen(url).read(), data)
+
+    def test_deploy(self):
+        # deploy first site
+        fab(foo_site)
+        fab(mysql_create_db)
+        fab(deploy_project)
+
+        # first site works
+        self.assertResponse('http://foo.example.com/instance/', 'foo')
+
+        # deploy second site
+        fab(bar_site)
+        fab(mysql_create_db)
+        fab(deploy_project)
+
+        # second site works
+        self.assertResponse('http://bar.example.com/instance/', 'bar')
+        # first site is still available
+        self.assertResponse('http://foo.example.com/instance/', 'foo')
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.