Esempio n. 1
0
    def test_relative_paths_true(self):
        recipe = Recipe(
            {
                "buildout": {
                    "eggs-directory": self.eggs_dir,
                    "develop-eggs-directory": self.develop_eggs_dir,
                    "python": "python-version",
                    "bin-directory": self.bin_dir,
                    "parts-directory": self.parts_dir,
                    "directory": self.buildout_dir,
                    "find-links": "",
                    "allow-hosts": "",
                    "develop": ".",
                    "relative-paths": "true",
                },
                "python-version": {"executable": sys.executable},
            },
            "django",
            {"recipe": "djangorecipe", "wsgi": "true"},
        )
        recipe.make_wsgi_script([], [])
        recipe.create_manage_script([], [])

        manage = os.path.join(self.bin_dir, "django")
        wsgi_script = os.path.join(self.bin_dir, "django.wsgi")

        expected = "base = os.path.dirname(os.path.abspath(os.path.realpath(__file__)))"
        self.assertTrue(expected in open(manage).read())
        self.assertTrue(expected in open(wsgi_script).read())
Esempio n. 2
0
    def test_relative_paths_true(self):
        recipe = Recipe({
                'buildout': {
                    'eggs-directory': self.eggs_dir,
                    'develop-eggs-directory': self.develop_eggs_dir,
                    'python': 'python-version',
                    'bin-directory': self.bin_dir,
                    'parts-directory': self.parts_dir,
                    'directory': self.buildout_dir,
                    'find-links': '',
                    'allow-hosts': '',
                    'develop': '.',
                    'relative-paths': 'true'
                    },
                'python-version': {'executable': sys.executable}},
                             'django',
                             {'recipe': 'djangorecipe',
                              'wsgi': 'true'})
        recipe.make_scripts([], [])
        recipe.create_manage_script([], [])

        manage = os.path.join(self.bin_dir, 'django')
        wsgi_script = os.path.join(self.bin_dir, 'django.wsgi')

        expected = base = 'base = os.path.dirname(os.path.abspath(os.path.realpath(__file__)))'
        self.assertTrue(expected in open(manage).read())
        self.assertTrue(expected in open(wsgi_script).read())
Esempio n. 3
0
    def setUp(self):
        # Create a directory for our buildout files created by the recipe
        self.buildout_dir = tempfile.mkdtemp('djangorecipe')

        self.bin_dir = os.path.join(self.buildout_dir, 'bin')
        self.develop_eggs_dir = os.path.join(self.buildout_dir,
                                             'develop-eggs')
        self.eggs_dir = os.path.join(self.buildout_dir, 'eggs')
        self.parts_dir = os.path.join(self.buildout_dir, 'parts')

        # We need to create the bin dir since the recipe should be able to
        # expect it exists
        os.mkdir(self.bin_dir)

        self.recipe = Recipe({
                'buildout': {
                    'eggs-directory': self.eggs_dir,
                    'develop-eggs-directory': self.develop_eggs_dir,
                    'python': 'python-version',
                    'bin-directory': self.bin_dir,
                    'parts-directory': self.parts_dir,
                    'directory': self.buildout_dir,
                    'find-links': '',
                    'allow-hosts': '',
                    },
                'python-version': {'executable': sys.executable}},
                             'django',
                             {'recipe': 'djangorecipe'})
Esempio n. 4
0
    def test_boilerplate_1_2(self):
        """Test the boilerplate for django 1.2."""

        recipe = Recipe({
                'buildout': {'eggs-directory': self.eggs_dir,
                             'develop-eggs-directory': self.develop_eggs_dir,
                             'python': 'python-version',
                             'bin-directory': self.bin_dir,
                             'parts-directory': self.parts_dir,
                             'directory': self.buildout_dir,
                             'find-links': '',
                             'allow-hosts': '',
                             'versions': 'versions',
                             },
                'versions': {'django': '1.2.5'},
                'python-version': {'executable': '/python4k'},
                'py5k': {'executable': '/python5k'}},
                        'django',
                        {'recipe': 'djangorecipe',
                         'python': 'py5k', 'wsgi': 'true'})

        secret = '$55upfci7a#gi@&e9o1-hb*k+f$3+(&b$j=cn67h#22*0%-bj0'
        recipe.generate_secret = lambda: secret

        project_dir = os.path.join(self.buildout_dir, 'project')
        recipe.create_project(project_dir)
        settings = open(os.path.join(project_dir, 'settings.py')).read()
        settings_dict = {'project': self.recipe.options['project'],
                         'secret': secret,
                         'urlconf': self.recipe.options['urlconf'],
                         }
        from djangorecipe.boilerplate import versions
        self.assertEquals(versions['1.2']['settings'] % settings_dict,
                          settings)
Esempio n. 5
0
    def test_boilerplate_1_2(self):
        """Test the boilerplate for django 1.2."""

        recipe = Recipe({
                'buildout': {'eggs-directory': self.eggs_dir,
                             'develop-eggs-directory': self.develop_eggs_dir,
                             'python': 'python-version',
                             'bin-directory': self.bin_dir,
                             'parts-directory': self.parts_dir,
                             'directory': self.buildout_dir,
                             'find-links': '',
                             'allow-hosts': '',
                             'versions': 'versions',
                             },
                'versions': {'django': '1.2.5'},
                'python-version': {'executable': '/python4k'},
                'py5k': {'executable': '/python5k'}},
                        'django',
                        {'recipe': 'djangorecipe',
                         'python': 'py5k', 'wsgi': 'true'})

        secret = '$55upfci7a#gi@&e9o1-hb*k+f$3+(&b$j=cn67h#22*0%-bj0'
        recipe.generate_secret = lambda: secret

        project_dir = os.path.join(self.buildout_dir, 'project')
        recipe.create_project(project_dir)
        settings = open(os.path.join(project_dir, 'settings.py')).read()
        settings_dict = {'project': self.recipe.options['project'],
                         'secret': secret,
                         'urlconf': self.recipe.options['urlconf'],
                         }
        from djangorecipe.boilerplate import versions
        self.assertEquals(versions['1.2']['settings'] % settings_dict,
                          settings)
Esempio n. 6
0
 def test_consistent_options(self):
     # Buildout is pretty clever in detecting changing options. If
     # the recipe modifies it's options during initialisation it
     # will store this to determine wheter it needs to update or do
     # a uninstall & install. We need to make sure that we normally
     # do not trigger this. That means running the recipe with the
     # same options should give us the same results.
     self.assertEqual(Recipe(*self.recipe_initialisation).options,
                      Recipe(*self.recipe_initialisation).options)
Esempio n. 7
0
 def test_python_option(self):
     # Changing the option for only the part will change the used Python
     # version.
     recipe_args = copy.deepcopy(self.recipe_initialisation)
     recipe_args[0].update({'py5k': {'executable': '/python5k'}})
     recipe_args[2].update({'python': 'py5k',
                            'wsgi': 'true'})
     recipe = Recipe(*recipe_args)
     wsgi_script = os.path.join(self.bin_dir, 'django.wsgi')
     recipe.make_scripts([], [])
     self.assertEqual(open(wsgi_script).readlines()[0], '#!/python5k\n')
Esempio n. 8
0
    def setUp(self):
        # Create a directory for our buildout files created by the recipe
        self.buildout_dir = tempfile.mkdtemp('djangorecipe')

        self.bin_dir = os.path.join(self.buildout_dir, 'bin')
        self.develop_eggs_dir = os.path.join(self.buildout_dir,
                                             'develop-eggs')
        self.eggs_dir = os.path.join(self.buildout_dir, 'eggs')
        self.parts_dir = os.path.join(self.buildout_dir, 'parts')

        # We need to create the bin dir since the recipe should be able to
        # expect it exists
        os.mkdir(self.bin_dir)

        self.recipe = Recipe({
                'buildout': {
                    'eggs-directory': self.eggs_dir,
                    'develop-eggs-directory': self.develop_eggs_dir,
                    'python': 'python-version',
                    'bin-directory': self.bin_dir,
                    'parts-directory': self.parts_dir,
                    'directory': self.buildout_dir,
                    'find-links': '',
                    'allow-hosts': '',
                    },
                'python-version': {'executable': sys.executable}},
                             'django',
                             {'recipe': 'djangorecipe'})
Esempio n. 9
0
 def test_consistent_options(self):
     # Buildout is pretty clever in detecting changing options. If
     # the recipe modifies it's options during initialisation it
     # will store this to determine wheter it needs to update or do
     # a uninstall & install. We need to make sure that we normally
     # do not trigger this. That means running the recipe with the
     # same options should give us the same results.
     self.assertEqual(*[
         Recipe(
             {
                 'buildout': {
                     'eggs-directory': self.eggs_dir,
                     'develop-eggs-directory': self.develop_eggs_dir,
                     'python': 'python-version',
                     'bin-directory': self.bin_dir,
                     'parts-directory': self.parts_dir,
                     'directory': self.buildout_dir,
                     'find-links': '',
                     'allow-hosts': '',
                 },
                 'python-version': {
                     'executable': sys.executable
                 }
             }, 'django', {
                 'recipe': 'djangorecipe'
             }).options.copy() for i in range(2)
     ])
Esempio n. 10
0
 def test_python_option(self):
     # The python option makes it possible to specify a specific Python
     # executable which is to be used for the generated scripts.
     recipe = Recipe(
         {
             'buildout': {
                 'eggs-directory': self.eggs_dir,
                 'develop-eggs-directory': self.develop_eggs_dir,
                 'python': 'python-version',
                 'bin-directory': self.bin_dir,
                 'parts-directory': self.parts_dir,
                 'directory': self.buildout_dir,
                 'find-links': '',
                 'allow-hosts': '',
             },
             'python-version': {
                 'executable': '/python4k'
             }
         }, 'django', {
             'recipe': 'djangorecipe',
             'wsgi': 'true'
         })
     recipe.make_scripts([], [])
     # This should have created a script in the bin dir
     wsgi_script = os.path.join(self.bin_dir, 'django.wsgi')
     self.assertEqual(open(wsgi_script).readlines()[0], '#!/python4k\n')
     # Changeing the option for only the part will change the used Python
     # version.
     recipe = Recipe(
         {
             'buildout': {
                 'eggs-directory': self.eggs_dir,
                 'develop-eggs-directory': self.develop_eggs_dir,
                 'python': 'python-version',
                 'bin-directory': self.bin_dir,
                 'parts-directory': self.parts_dir,
                 'directory': self.buildout_dir,
                 'find-links': '',
                 'allow-hosts': '',
             },
             'python-version': {
                 'executable': '/python4k'
             },
             'py5k': {
                 'executable': '/python5k'
             }
         }, 'django', {
             'recipe': 'djangorecipe',
             'python': 'py5k',
             'wsgi': 'true'
         })
     recipe.make_scripts([], [])
     self.assertEqual(open(wsgi_script).readlines()[0], '#!/python5k\n')
Esempio n. 11
0
 def test_python_option(self):
     # The python option makes it possible to specify a specific Python 
     # executable which is to be used for the generated scripts.
     recipe = Recipe({'buildout': {'eggs-directory': self.eggs_dir,
                                   'develop-eggs-directory': self.develop_eggs_dir,
                                   'python': 'python-version',
                                   'bin-directory': self.bin_dir,
                                   'parts-directory': self.parts_dir,
                                   'directory': self.buildout_dir,
                                  },
                      'python-version': {'executable': '/python4k'}}, 
                     'django',
                     {'recipe': 'djangorecipe', 'version': 'trunk',
                      'wsgi': 'true'})
     recipe.make_scripts([], [])
     # This should have created a script in the bin dir
     wsgi_script = os.path.join(self.bin_dir, 'django.wsgi')
     self.assertEqual(open(wsgi_script).readlines()[0], '#!/python4k\n')
     # Changeing the option for only the part will change the used Python
     # version.
     recipe = Recipe({'buildout': {'eggs-directory': self.eggs_dir,
                                   'develop-eggs-directory': self.develop_eggs_dir,
                                   'python': 'python-version',
                                   'bin-directory': self.bin_dir,
                                   'parts-directory': self.parts_dir,
                                   'directory': self.buildout_dir,
                                  },
                      'python-version': {'executable': '/python4k'},
                      'py5k': {'executable': '/python5k'}}, 
                     'django',
                     {'recipe': 'djangorecipe', 'version': 'trunk',
                      'python': 'py5k', 'wsgi': 'true'})
     recipe.make_scripts([], [])
     self.assertEqual(open(wsgi_script).readlines()[0], '#!/python5k\n')        
Esempio n. 12
0
    def test_relative_paths_true(self):
        recipe = Recipe(
            {
                'buildout': {
                    'eggs-directory': self.eggs_dir,
                    'develop-eggs-directory': self.develop_eggs_dir,
                    'python': 'python-version',
                    'bin-directory': self.bin_dir,
                    'parts-directory': self.parts_dir,
                    'directory': self.buildout_dir,
                    'find-links': '',
                    'allow-hosts': '',
                    'develop': '.',
                    'relative-paths': 'true'
                },
                'python-version': {
                    'executable': sys.executable
                }
            }, 'django', {
                'recipe': 'djangorecipe',
                'wsgi': 'true'
            })
        recipe.make_scripts([], [])
        recipe.create_manage_script([], [])

        manage = os.path.join(self.bin_dir, 'django')
        wsgi_script = os.path.join(self.bin_dir, 'django.wsgi')

        expected = base = 'base = os.path.dirname(os.path.abspath(os.path.realpath(__file__)))'
        self.assertTrue(expected in open(manage).read())
        self.assertTrue(expected in open(wsgi_script).read())
Esempio n. 13
0
    def test_boilerplate_1_2(self):
        """Test the boilerplate for django 1.2."""

        recipe_args = copy.deepcopy(self.recipe_initialisation)

        recipe_args[0]['versions'] = {'django': '1.2.5'}
        recipe = Recipe(*recipe_args)

        secret = '$55upfci7a#gi@&e9o1-hb*k+f$3+(&b$j=cn67h#22*0%-bj0'
        recipe.generate_secret = lambda: secret

        project_dir = os.path.join(self.buildout_dir, 'project')
        recipe.create_project(project_dir)
        settings = open(os.path.join(project_dir, 'settings.py')).read()
        settings_dict = {'project': self.recipe.options['project'],
                         'secret': secret,
                         'urlconf': self.recipe.options['urlconf'],
                         }
        from djangorecipe.boilerplate import versions

        self.assertEqual(versions['1.2']['settings'] % settings_dict,
                          settings)
Esempio n. 14
0
    def test_boilerplate_1_2(self):
        """Test the boilerplate for django 1.2."""

        recipe_args = copy.deepcopy(self.recipe_initialisation)

        recipe_args[0]["versions"] = {"django": "1.2.5"}
        recipe = Recipe(*recipe_args)

        secret = "$55upfci7a#gi@&e9o1-hb*k+f$3+(&b$j=cn67h#22*0%-bj0"
        recipe.generate_secret = lambda: secret

        project_dir = os.path.join(self.buildout_dir, "project")
        recipe.create_project(project_dir)
        settings = open(os.path.join(project_dir, "settings.py")).read()
        settings_dict = {
            "project": self.recipe.options["project"],
            "secret": secret,
            "urlconf": self.recipe.options["urlconf"],
        }
        from djangorecipe.boilerplate import versions

        self.assertEqual(versions["1.2"]["settings"] % settings_dict, settings)
Esempio n. 15
0
class TestRecipe(unittest.TestCase):

    def setUp(self):
        # Create a directory for our buildout files created by the recipe
        self.buildout_dir = tempfile.mkdtemp('djangorecipe')
        
        self.bin_dir = os.path.join(self.buildout_dir, 'bin')
        self.develop_eggs_dir = os.path.join(self.buildout_dir, 
                                             'develop-eggs')
        self.eggs_dir = os.path.join(self.buildout_dir, 'eggs')
        self.parts_dir = os.path.join(self.buildout_dir, 'parts')

        # We need to create the bin dir since the recipe should be able to expect it exists
        os.mkdir(self.bin_dir)

        self.recipe = Recipe({'buildout': {'eggs-directory': self.eggs_dir,
                                           'develop-eggs-directory': self.develop_eggs_dir,
                                           'python': 'python-version',
                                           'bin-directory': self.bin_dir,
                                           'parts-directory': self.parts_dir,
                                           'directory': self.buildout_dir,
                                           },
                              'python-version': {'executable': sys.executable}}, 
                             'django', 
                             {'recipe': 'djangorecipe',
                              'version': 'trunk'})

    def tearDown(self):
        # Remove our test dir
        shutil.rmtree(self.buildout_dir)

    def test_consistent_options(self):
        # Buildout is pretty clever in detecting changing options. If
        # the recipe modifies it's options during initialisation it
        # will store this to determine wheter it needs to update or do
        # a uninstall & install. We need to make sure that we normally
        # do not trigger this. That means running the recipe with the
        # same options should give us the same results.
        self.assertEqual(*[
                Recipe({'buildout': {'eggs-directory': self.eggs_dir,
                                     'develop-eggs-directory': self.develop_eggs_dir,
                                     'python': 'python-version',
                                     'bin-directory': self.bin_dir,
                                     'parts-directory': self.parts_dir,
                                     'directory': self.buildout_dir,
                                     },
                        'python-version': {'executable': sys.executable}}, 
                       'django', 
                       {'recipe': 'djangorecipe',
                        'version': 'trunk'}).options.copy() for i in range(2)])

    def test_svn_url(self):
        # Make sure that only a few specific type of url's are
        # considered svn url's

        # This is a plain release version so it should indicate it is
        # not a svn url
        self.failIf(self.recipe.is_svn_url('0.96.2'))
        # The next line specifies a proper link with the trunk
        self.assert_(self.recipe.is_svn_url('trunk'))
        # A url looking like trunk should also fail
        self.failIf(self.recipe.is_svn_url('trunka'))
        # A full svn url including version should work
        self.assert_(self.recipe.is_svn_url(
            'http://code.djangoproject.com/svn/django/branches/newforms-admin@7833'))
        # HTTPS should work too
        self.assert_(self.recipe.is_svn_url(
            'https://code.djangoproject.com/svn/django/branches/newforms-admin@7833'))
        # Svn+ssh should work
        self.assert_(self.recipe.is_svn_url(
            'svn+ssh://myserver/newforms-admin@7833'))
        # Svn protocol through any custom tunnel defined in ~/.subversion/config should work
        self.assert_(self.recipe.is_svn_url(
            'svn+MY_Custom-tunnel://myserver/newforms-admin@7833'))
        # Using a non existent protocol should not be a svn url?
        self.failIf(self.recipe.is_svn_url(
            'unknown://myserver/newforms-admin@7833'))

    def test_command(self):
        # The command method is a wrapper for subprocess which excutes
        # a command and return's it's status code. We will demonstrate
        # this with a simple test of running `dir`.
        self.failIf(self.recipe.command('echo'))
        # Executing a non existing command should return an error code
        self.assert_(self.recipe.command('spamspamspameggs'))

    @mock.patch('subprocess', 'Popen')
    def test_command_verbose_mode(self, popen):
        # When buildout is put into verbose mode the command methode
        # should stop capturing the ouput of it's commands.
        popen.return_value = mock.Mock()
        self.recipe.buildout['buildout']['verbosity'] = 'verbose'
        self.recipe.command('silly-command')
        self.assertEqual(
            popen.call_args, 
            (('silly-command',), {'shell': True, 'stdout': None}))
        
    def test_create_file(self):
        # The create file helper should create a file at a certain
        # location unless it already exists. We will need a
        # non-existing file first.
        f, name = tempfile.mkstemp()
        # To show the function in action we need to delete the file
        # before testing.
        os.remove(name)
        # The method accepts a template argument which it will use
        # with the options argument for string substitution.
        self.recipe.create_file(name, 'Spam %s', 'eggs')
        # Let's check the contents of the file
        self.assertEqual(open(name).read(), 'Spam eggs')
        # If we try to write it again it will just ignore our request
        self.recipe.create_file(name, 'Spam spam spam %s', 'eggs')
        # The content of the file should therefore be the same
        self.assertEqual(open(name).read(), 'Spam eggs')
        # Now remove our temp file
        os.remove(name)

    def test_generate_secret(self):
        # To create a basic skeleton the recipe also generates a
        # random secret for the settings file. Since it should very
        # unlikely that it will generate the same key a few times in a
        # row we will test it with letting it generate a few keys.
        self.assert_(len(set(
                    [self.recipe.generate_secret() for i in xrange(10)])) > 1)

    def test_version_to_svn(self):
        # Version specification that lead to a svn repository can be
        # specified in different ways. Just specifying `trunk` should
        # be enough to get the full url to the Django trunk.
        self.assertEqual(self.recipe.version_to_svn('trunk'),
                         'http://code.djangoproject.com/svn/django/trunk/')
        # Any other specification should lead to the url it is given
        self.assertEqual(self.recipe.version_to_svn('svn://somehost/trunk'),
                         'svn://somehost/trunk')

    def test_version_to_download_suffic(self):
        # To create standard names for the download directory a method
        # is provided which converts a version to a dir suffix. A
        # simple pointer to trunk should return svn.
        self.assertEqual(self.recipe.version_to_download_suffix('trunk'), 
                         'svn')
        # Any other url should return the last path component. This
        # works out nicely for branches or version pinned url's.
        self.assertEqual(self.recipe.version_to_download_suffix(
                'http://monty/branches/python'), 'python')

    def test_make_protocol_scripts(self):
        # To ease deployment a WSGI script can be generated. The
        # script adds any paths from the `extra_paths` option to the
        # Python path.
        self.recipe.options['wsgi'] = 'true'
        self.recipe.options['fcgi'] = 'true'
        self.recipe.make_scripts([], [])
        # This should have created a script in the bin dir
        wsgi_script = os.path.join(self.bin_dir, 'django.wsgi')
        self.assert_(os.path.exists(wsgi_script))
        # The contents should list our paths
        contents = open(wsgi_script).read()
        # It should also have a reference to our settings module
        self.assert_('project.development' in contents)
        # and a line which set's up the WSGI app
        self.assert_("application = "
                     "djangorecipe.wsgi.main('project.development', logfile='')"
                     in contents)
        self.assert_("class logger(object)" not in contents)

        # Another deployment options is FCGI. The recipe supports an option to
        # automatically create the required script.
        fcgi_script = os.path.join(self.bin_dir, 'django.fcgi')
        self.assert_(os.path.exists(fcgi_script))
        # The contents should list our paths
        contents = open(fcgi_script).read()
        # It should also have a reference to our settings module
        self.assert_('project.development' in contents)
        # and a line which set's up the WSGI app
        self.assert_("djangorecipe.fcgi.main('project.development', logfile='')"
                     in contents)
        self.assert_("class logger(object)" not in contents)

        self.recipe.options['logfile'] = '/foo'
        self.recipe.make_scripts([], [])
        wsgi_script = os.path.join(self.bin_dir, 'django.wsgi')
        contents = open(wsgi_script).read()
        self.assert_("logfile='/foo'" in contents)

        self.recipe.options['logfile'] = '/foo'
        self.recipe.make_scripts([], [])
        fcgi_script = os.path.join(self.bin_dir, 'django.fcgi')
        contents = open(fcgi_script).read()
        self.assert_("logfile='/foo'" in contents)

    def test_create_project(self):
        # If a project does not exist already the recipe will create
        # one.
        project_dir = os.path.join(self.buildout_dir, 'project')
        self.recipe.create_project(project_dir)
        # This should have create a project directory
        self.assert_(os.path.exists(project_dir))
        # With this directory we should have __init__.py to make it a
        # package
        self.assert_(
            os.path.exists(os.path.join(project_dir, '__init__.py')))
        # There should also be a urls.py
        self.assert_(
            os.path.exists(os.path.join(project_dir, 'urls.py')))
        # To make it easier to start using this project both a media
        # and a templates folder are created
        self.assert_(
            os.path.exists(os.path.join(project_dir, 'media')))
        self.assert_(
            os.path.exists(os.path.join(project_dir, 'templates')))
        # The project is ready to go since the recipe has generated a
        # base settings, development and production file
        for f in ('settings.py', 'development.py', 'production.py'):
            self.assert_(
                os.path.exists(os.path.join(project_dir, f)))

    def test_create_test_runner(self):
        # An executable script can be generated which will make it
        # possible to execute the Django test runner. This options
        # only works if we specify one or apps to test.
        testrunner = os.path.join(self.bin_dir, 'test')

        # This first argument sets extra_paths, we will use this to
        # make sure the script can find this recipe
        recipe_dir = os.path.abspath(
            os.path.join(os.path.dirname(__file__), '..'))

        # First we will show it does nothing by default
        self.recipe.create_test_runner([recipe_dir], [])
        self.failIf(os.path.exists(testrunner))

        # When we specify an app to test it should create the the
        # testrunner
        self.recipe.options['test'] = 'knight'
        self.recipe.create_test_runner([recipe_dir], [])
        self.assert_(os.path.exists(testrunner))

    def test_create_manage_script(self):
        # This buildout recipe creates a alternative for the standard
        # manage.py script. It has all the same functionality as the
        # original one but it sits in the bin dir instead of within
        # the project.
        manage = os.path.join(self.bin_dir, 'django')
        self.recipe.create_manage_script([], [])
        self.assert_(os.path.exists(manage))

    def test_create_manage_script_projectegg(self):
        # When a projectegg is specified, then the egg specified
        # should get used as the project file.
        manage = os.path.join(self.bin_dir, 'django')
        self.recipe.options['projectegg'] = 'spameggs'
        self.recipe.create_manage_script([], [])
        self.assert_(os.path.exists(manage))
        # Check that we have 'spameggs' as the project
        self.assert_("djangorecipe.manage.main('spameggs.development')"
                     in open(manage).read())

    @mock.patch('shutil', 'rmtree')
    @mock.patch('os.path', 'exists')
    @mock.patch('urllib', 'urlretrieve')
    @mock.patch('shutil', 'copytree')
    @mock.patch(ZCRecipeEggScripts, 'working_set')
    @mock.patch('zc.buildout.easy_install', 'scripts')
    @mock.patch(Recipe, 'install_release')
    @mock.patch(Recipe, 'create_manage_script')
    @mock.patch(Recipe, 'create_test_runner')
    def test_extra_paths(self, rmtree, path_exists, urlretrieve,
                                   copytree, working_set, scripts,
                                   install_release, manage, testrunner):
        # The recipe allows extra-paths to be specified. It uses these to
        # extend the Python path within it's generated scripts.
        self.recipe.options['version'] = '1.0'
        self.recipe.options['extra-paths'] = 'somepackage\nanotherpackage'
        path_exists.return_value = True
        working_set.return_value = (None, [])
        self.recipe.install()
        self.assertEqual(manage.call_args[0][0][-2:],
                         ['somepackage', 'anotherpackage'])

    @mock.patch('shutil', 'rmtree')
    @mock.patch('os.path', 'exists')
    @mock.patch('urllib', 'urlretrieve')
    @mock.patch('shutil', 'copytree')
    @mock.patch(ZCRecipeEggScripts, 'working_set')
    @mock.patch('zc.buildout.easy_install', 'scripts')
    @mock.patch(Recipe, 'install_release')
    @mock.patch(Recipe, 'create_manage_script')
    @mock.patch(Recipe, 'create_test_runner')
    @mock.patch('site', 'addsitedir')
    def test_pth_files(self, rmtree, path_exists, urlretrieve,
                       copytree, working_set, scripts,
                       install_release, manage, testrunner, addsitedir):
        # When a pth-files option is set the recipe will use that to add more
        # paths to extra-paths.
        self.recipe.options['version'] = '1.0'
        path_exists.return_value = True
        working_set.return_value = (None, [])
        # The mock values needed to demonstrate the pth-files option.
        addsitedir.return_value = ['extra', 'dirs']
        self.recipe.options['pth-files'] = 'somedir'

        self.recipe.install()
        self.assertEqual(addsitedir.call_args, (('somedir', set([])), {}))
        # The extra-paths option has been extended.
        self.assertEqual(self.recipe.options['extra-paths'], '\nextra\ndirs')

    def test_create_wsgi_script_projectegg(self):
        # When a projectegg is specified, then the egg specified
        # should get used as the project in the wsgi script.
        wsgi = os.path.join(self.bin_dir, 'django.wsgi')
        recipe_dir = os.path.abspath(
            os.path.join(os.path.dirname(__file__), '..'))
        self.recipe.options['projectegg'] = 'spameggs'
        self.recipe.options['wsgi'] = 'true'
        self.recipe.make_scripts([recipe_dir], [])
        self.assert_(os.path.exists(wsgi))
        # Check that we have 'spameggs' as the project
        self.assert_('spameggs.development' in open(wsgi).read())

    def test_settings_option(self):
        # The settings option can be used to specify the settings file
        # for Django to use. By default it uses `development`.
        self.assertEqual(self.recipe.options['settings'], 'development')
        # When we change it an generate a manage script it will use
        # this var.
        self.recipe.options['settings'] = 'spameggs'
        self.recipe.create_manage_script([], [])
        manage = os.path.join(self.bin_dir, 'django')
        self.assert_("djangorecipe.manage.main('project.spameggs')"
                     in open(manage).read())

    @mock.patch('urllib2', 'urlopen')
    def test_get_release(self, mock):
        # The get_release method fecthes a release tarball and
        # extracts it. We have setup a mock so that it won't actually
        # download the release. Let's call the code.
        class FakeFile(object):
            def read(self):
                return 'Django tarball'
            def close(self):
                self.closed = True

        tmp = tempfile.mkdtemp()
        filename = os.path.join(tmp, 'django-0.96.2.tar.gz')
        mock.return_value = FakeFile()
        try:
            self.assertEqual(
                self.recipe.get_release('0.96.2', tmp),
                filename)
            # It tried to download the release through our mock
            mock.assert_called_with(
                'http://www.djangoproject.com/download/0.96.2/tarball/')
            # The file should have been filled with the contents from the
            # handle it got.
            self.assertEqual(open(filename).read(), 'Django tarball')
        finally:
            shutil.rmtree(tmp)

    @mock.patch('setuptools.archive_util', 'unpack_archive')
    @mock.patch('shutil', 'move')
    @mock.patch('shutil', 'rmtree')
    @mock.patch('os', 'listdir')
    def test_install_release(self, unpack, move, rmtree, listdir):
        # To install a release the recipe uses a specific method. We
        # have have mocked all the calls which interact with the
        # filesystem.
        listdir.return_value = ('Django-0.96-2',)
        self.recipe.install_release('0.96.2', 'downloads',
                                    'downloads/django-0.96.2.tar.gz',
                                    'parts/django')
        # Let's see what the mock's have been called with
        self.assertEqual(listdir.call_args, 
                         (('downloads/django-archive',), {}))
        self.assertEqual(unpack.call_args, 
                         (('downloads/django-0.96.2.tar.gz', 
                           'downloads/django-archive'), {}))
        self.assertEqual(move.call_args, 
                         (('downloads/django-archive/Django-0.96-2', 
                           'parts/django'), {}))
        self.assertEqual(rmtree.call_args,
                         (('downloads/django-archive',), {}))

    @mock.patch('shutil', 'copytree')
    @mock.patch(Recipe, 'command')
    def test_install_svn_version(self, copytree, command):
        # Installation from svn is handled by a method. We have mocked
        # the command method to avoid actual checkouts of Django.
        self.recipe.install_svn_version('trunk', 'downloads',
                                        'parts/django', False)
        # This should have tried to do a checkout of the Django trunk
        self.assertEqual(command.call_args,
                         (('svn co http://code.djangoproject.com/svn/django/trunk/ downloads/django-svn -q',), {}))
        # A copy command to the parts directory should also have been
        # issued
        self.assertEqual(copytree.call_args,
                         (('downloads/django-svn', 'parts/django'), {}))

    @mock.patch('shutil', 'copytree')
    @mock.patch('os.path', 'exists')
    @mock.patch(Recipe, 'command')
    def test_install_and_update_svn_version(self, copytree, exists, command):
        # When an checkout has been done of a svn based installation
        # is already done the recipe should just update it.
        exists.return_value = True

        self.recipe.install_svn_version('trunk', 'downloads',
                                        'parts/django', False)
        self.assertEqual(exists.call_args, (('downloads/django-svn',), {}))
        self.assertEqual(command.call_args,
                         (('svn up -q',), {'cwd': 'downloads/django-svn'}))

    @mock.patch(Recipe, 'command')
    def test_install_broken_svn(self, command):
        # When the checkout from svn fails during a svn build the
        # installation method raises an error. We will simulate this
        # failure by telling our mock what to do.
        command.return_value = 1
        # The line above should indicate a failure (non-zero exit
        # code)
        self.assertRaises(UserError, self.recipe.install_svn_version,
                          'trunk', 'downloads', 'parts/django', False)

    @mock.patch('shutil', 'copytree')
    @mock.patch(Recipe, 'command')
    def test_svn_install_from_cache(self, copytree, command):
        # If the buildout is told to install from cache it will not do
        # a checkout but instead an existing checkout
        self.recipe.buildout['buildout']['install-from-cache'] = 'true'
        # Now we can run the installation method
        self.recipe.install_svn_version('trunk', 'downloads', 
                                        'parts/django', True)
        # This should not have called the recipe's command method
        self.failIf(command.called)
        # A copy from the cache to the destination should have been
        # made
        self.assertEqual(copytree.call_args, 
                         (('downloads/django-svn', 'parts/django'), {}))

    @mock.patch(Recipe, 'command')
    def test_update_svn(self, command):
        # When the recipe is asked to do an update and the version is
        # a svn version it just does an update on the parts folder.
        self.recipe.update()
        self.assertEqual('svn up -q', command.call_args[0][0])
        # It changes the working directory so that the simple svn up
        # command will work.
        self.assertEqual(command.call_args[1].keys(), ['cwd'])

    @mock.patch('subprocess', 'call')
    def test_update_with_cache(self, call_process):
        # When the recipe is asked to do an update whilst in install
        # from cache mode it just ignores it
        self.recipe.install_from_cache = True
        self.recipe.update()
        self.failIf(call_process.called)
        
    @mock.patch('subprocess', 'call')
    def test_update_with_newest_false(self, call_process):
        # When the recipe is asked to do an update whilst in install
        # from cache mode it just ignores it
        self.recipe.buildout['buildout']['newest'] = 'false'
        self.recipe.update()
        self.assertFalse(call_process.called)

    @mock.patch('shutil', 'rmtree')
    @mock.patch('os.path', 'exists')
    @mock.patch('urllib', 'urlretrieve')
    @mock.patch('shutil', 'copytree')
    @mock.patch(ZCRecipeEggScripts, 'working_set')
    @mock.patch('zc.buildout.easy_install', 'scripts')
    @mock.patch(Recipe, 'install_release')
    def test_clear_existing_django(self, rmtree, path_exists, urlretrieve,
                                   copytree, working_set, scripts,
                                   install_release):
        # When the recipe is executed and Django is already installed
        # within parts it should remove it. We will mock the exists
        # check to make it let the recipe think it has an existing
        # Django install.
        self.recipe.options['version'] = '1.0'
        path_exists.return_value = True
        working_set.return_value = (None, [])
        self.recipe.install()
        # This should have called remove tree
        self.assert_(rmtree.called)
        # We will assert that the last two compontents of the path
        # passed to rmtree are the ones we wanted to delete.
        self.assertEqual(rmtree.call_args[0][0].split('/')[-2:], 
                         ['parts', 'django'])

    @mock.patch(Recipe, 'command')
    def test_update_pinned_svn_url(self, command):
        # Make sure that updating a pinned version is updated
        # accordingly. It must not switch to updating beyond it's
        # requested revision.
        # The recipe does this by checking for an @ sign in the url /
        # version.
        self.recipe.is_svn_url = lambda version: True

        self.recipe.options['version'] = 'http://testing/trunk@2531'
        self.recipe.update()
        self.assertEqual(command.call_args[0], ('svn up -r 2531 -q',))

    @mock.patch(Recipe, 'command')
    def test_update_username_in_svn_url(self, command):
        # Make sure that updating a version with a username
        # in the URL works
        self.recipe.is_svn_url = lambda version: True

        # First test with both a revision and a username in the url
        self.recipe.options['version'] = 'http://user@testing/trunk@2531'
        self.recipe.update()
        self.assertEqual(command.call_args[0], ('svn up -r 2531 -q',))

        # Now test with only the username
        self.recipe.options['version'] = 'http://user@testing/trunk'
        self.recipe.update()
        self.assertEqual(command.call_args[0], ('svn up -q',))

    def test_python_option(self):
        # The python option makes it possible to specify a specific Python 
        # executable which is to be used for the generated scripts.
        recipe = Recipe({'buildout': {'eggs-directory': self.eggs_dir,
                                      'develop-eggs-directory': self.develop_eggs_dir,
                                      'python': 'python-version',
                                      'bin-directory': self.bin_dir,
                                      'parts-directory': self.parts_dir,
                                      'directory': self.buildout_dir,
                                     },
                         'python-version': {'executable': '/python4k'}}, 
                        'django',
                        {'recipe': 'djangorecipe', 'version': 'trunk',
                         'wsgi': 'true'})
        recipe.make_scripts([], [])
        # This should have created a script in the bin dir
        wsgi_script = os.path.join(self.bin_dir, 'django.wsgi')
        self.assertEqual(open(wsgi_script).readlines()[0], '#!/python4k\n')
        # Changeing the option for only the part will change the used Python
        # version.
        recipe = Recipe({'buildout': {'eggs-directory': self.eggs_dir,
                                      'develop-eggs-directory': self.develop_eggs_dir,
                                      'python': 'python-version',
                                      'bin-directory': self.bin_dir,
                                      'parts-directory': self.parts_dir,
                                      'directory': self.buildout_dir,
                                     },
                         'python-version': {'executable': '/python4k'},
                         'py5k': {'executable': '/python5k'}}, 
                        'django',
                        {'recipe': 'djangorecipe', 'version': 'trunk',
                         'python': 'py5k', 'wsgi': 'true'})
        recipe.make_scripts([], [])
        self.assertEqual(open(wsgi_script).readlines()[0], '#!/python5k\n')        
Esempio n. 16
0
class TestRecipe(unittest.TestCase):

    def setUp(self):
        # Create a directory for our buildout files created by the recipe
        self.buildout_dir = tempfile.mkdtemp('djangorecipe')

        self.bin_dir = os.path.join(self.buildout_dir, 'bin')
        self.develop_eggs_dir = os.path.join(self.buildout_dir,
                                             'develop-eggs')
        self.eggs_dir = os.path.join(self.buildout_dir, 'eggs')
        self.parts_dir = os.path.join(self.buildout_dir, 'parts')

        # We need to create the bin dir since the recipe should be able to
        # expect it exists
        os.mkdir(self.bin_dir)

        self.recipe = Recipe({
                'buildout': {
                    'eggs-directory': self.eggs_dir,
                    'develop-eggs-directory': self.develop_eggs_dir,
                    'python': 'python-version',
                    'bin-directory': self.bin_dir,
                    'parts-directory': self.parts_dir,
                    'directory': self.buildout_dir,
                    'find-links': '',
                    'allow-hosts': '',
                    },
                'python-version': {'executable': sys.executable}},
                             'django',
                             {'recipe': 'djangorecipe'})

    def tearDown(self):
        # Remove our test dir
        shutil.rmtree(self.buildout_dir)

    def test_consistent_options(self):
        # Buildout is pretty clever in detecting changing options. If
        # the recipe modifies it's options during initialisation it
        # will store this to determine wheter it needs to update or do
        # a uninstall & install. We need to make sure that we normally
        # do not trigger this. That means running the recipe with the
        # same options should give us the same results.
        self.assertEqual(*[
                Recipe({'buildout': {
                            'eggs-directory': self.eggs_dir,
                            'develop-eggs-directory': self.develop_eggs_dir,
                            'python': 'python-version',
                            'bin-directory': self.bin_dir,
                            'parts-directory': self.parts_dir,
                            'directory': self.buildout_dir,
                            'find-links': '',
                            'allow-hosts':'',
                            },
                        'python-version': {'executable': sys.executable}},
                       'django',
                       {'recipe': 'djangorecipe'}).options.copy()
                for i in range(2)])

    def test_create_file(self):
        # The create file helper should create a file at a certain
        # location unless it already exists. We will need a
        # non-existing file first.
        f, name = tempfile.mkstemp()
        # To show the function in action we need to delete the file
        # before testing.
        os.remove(name)
        # The method accepts a template argument which it will use
        # with the options argument for string substitution.
        self.recipe.create_file(name, 'Spam %s', 'eggs')
        # Let's check the contents of the file
        self.assertEqual(open(name).read(), 'Spam eggs')
        # If we try to write it again it will just ignore our request
        self.recipe.create_file(name, 'Spam spam spam %s', 'eggs')
        # The content of the file should therefore be the same
        self.assertEqual(open(name).read(), 'Spam eggs')
        # Now remove our temp file
        os.remove(name)

    def test_generate_secret(self):
        # To create a basic skeleton the recipe also generates a
        # random secret for the settings file. Since it should very
        # unlikely that it will generate the same key a few times in a
        # row we will test it with letting it generate a few keys.
        self.assert_(len(set(
                    [self.recipe.generate_secret() for i in range(10)])) > 1)

    def test_make_protocol_scripts(self):
        # To ease deployment a WSGI script can be generated. The
        # script adds any paths from the `extra_paths` option to the
        # Python path.
        self.recipe.options['wsgi'] = 'true'
        self.recipe.options['fcgi'] = 'true'
        self.recipe.make_scripts([], [])
        # This should have created a script in the bin dir
        wsgi_script = os.path.join(self.bin_dir, 'django.wsgi')
        self.assert_(os.path.exists(wsgi_script))
        # The contents should list our paths
        contents = open(wsgi_script).read()
        # It should also have a reference to our settings module
        self.assert_('project.development' in contents)
        # and a line which set's up the WSGI app
        self.assert_("application = "
                     "djangorecipe.wsgi.main('project.development', "
                     "logfile='')"
                     in contents)
        self.assert_("class logger(object)" not in contents)

        # Another deployment options is FCGI. The recipe supports an option to
        # automatically create the required script.
        fcgi_script = os.path.join(self.bin_dir, 'django.fcgi')
        self.assert_(os.path.exists(fcgi_script))
        # The contents should list our paths
        contents = open(fcgi_script).read()
        # It should also have a reference to our settings module
        self.assert_('project.development' in contents)
        # and a line which set's up the WSGI app
        self.assert_("djangorecipe.fcgi.main('project.development', "
                     "logfile='')"
                     in contents)
        self.assert_("class logger(object)" not in contents)

        self.recipe.options['logfile'] = '/foo'
        self.recipe.make_scripts([], [])
        wsgi_script = os.path.join(self.bin_dir, 'django.wsgi')
        contents = open(wsgi_script).read()
        self.assert_("logfile='/foo'" in contents)

        self.recipe.options['logfile'] = '/foo'
        self.recipe.make_scripts([], [])
        fcgi_script = os.path.join(self.bin_dir, 'django.fcgi')
        contents = open(fcgi_script).read()
        self.assert_("logfile='/foo'" in contents)

    @mock.patch('zc.buildout.easy_install.scripts')
    def test_make_protocol_scripts_return_value(self, scripts):
        # The return value of make scripts lists the generated scripts.
        self.recipe.options['wsgi'] = 'true'
        self.recipe.options['fcgi'] = 'true'
        scripts.return_value = ['some-path']
        self.assertEqual(self.recipe.make_scripts([], []),
                         ['some-path', 'some-path'])

    def test_create_project(self):
        # If a project does not exist already the recipe will create
        # one.
        project_dir = os.path.join(self.buildout_dir, 'project')
        self.recipe.create_project(project_dir)
        # This should have create a project directory
        self.assert_(os.path.exists(project_dir))
        # With this directory we should have __init__.py to make it a
        # package
        self.assert_(
            os.path.exists(os.path.join(project_dir, '__init__.py')))
        # There should also be a urls.py
        self.assert_(
            os.path.exists(os.path.join(project_dir, 'urls.py')))
        # To make it easier to start using this project both a media
        # and a templates folder are created
        self.assert_(
            os.path.exists(os.path.join(project_dir, 'media')))
        self.assert_(
            os.path.exists(os.path.join(project_dir, 'templates')))
        # The project is ready to go since the recipe has generated a
        # base settings, development and production file
        for f in ('settings.py', 'development.py', 'production.py'):
            self.assert_(
                os.path.exists(os.path.join(project_dir, f)))

    def test_create_test_runner(self):
        # An executable script can be generated which will make it
        # possible to execute the Django test runner. This options
        # only works if we specify one or apps to test.
        testrunner = os.path.join(self.bin_dir, 'test')

        # This first argument sets extra_paths, we will use this to
        # make sure the script can find this recipe
        recipe_dir = os.path.abspath(
            os.path.join(os.path.dirname(__file__), '..'))

        # First we will show it does nothing by default
        self.recipe.create_test_runner([recipe_dir], [])
        self.failIf(os.path.exists(testrunner))

        # When we specify an app to test it should create the the
        # testrunner
        self.recipe.options['test'] = 'knight'
        self.recipe.create_test_runner([recipe_dir], [])
        self.assert_(os.path.exists(testrunner))

    def test_create_manage_script(self):
        # This buildout recipe creates a alternative for the standard
        # manage.py script. It has all the same functionality as the
        # original one but it sits in the bin dir instead of within
        # the project.
        manage = os.path.join(self.bin_dir, 'django')
        self.recipe.create_manage_script([], [])
        self.assert_(os.path.exists(manage))

    def test_create_manage_script_projectegg(self):
        # When a projectegg is specified, then the egg specified
        # should get used as the project file.
        manage = os.path.join(self.bin_dir, 'django')
        self.recipe.options['projectegg'] = 'spameggs'
        self.recipe.create_manage_script([], [])
        self.assert_(os.path.exists(manage))
        # Check that we have 'spameggs' as the project
        self.assert_("djangorecipe.manage.main('spameggs.development')"
                     in open(manage).read())

    @mock.patch('os.path.exists')
    @mock.patch('zc.recipe.egg.egg.Scripts.working_set')
    @mock.patch('zc.buildout.easy_install.scripts')
    @mock.patch('djangorecipe.recipe.Recipe.create_manage_script')
    @mock.patch('djangorecipe.recipe.Recipe.create_test_runner')
    def test_extra_paths(self, testrunner, manage, scripts,
                         working_set, path_exists):

        # The recipe allows extra-paths to be specified. It uses these to
        # extend the Python path within it's generated scripts.
        self.recipe.options['version'] = '1.0'
        self.recipe.options['extra-paths'] = 'somepackage\nanotherpackage'
        path_exists.return_value = True
        working_set.return_value = (None, [])
        manage.return_value = []
        scripts.return_value = []
        testrunner.return_value = []
        self.recipe.install()
        self.assertEqual(manage.call_args[0][0][-2:],
                         ['somepackage', 'anotherpackage'])

    @mock.patch('os.path.exists')
    @mock.patch('zc.recipe.egg.egg.Scripts.working_set')
    @mock.patch('zc.buildout.easy_install.scripts')
    @mock.patch('djangorecipe.recipe.Recipe.create_manage_script')
    @mock.patch('djangorecipe.recipe.Recipe.create_test_runner')
    @mock.patch('site.addsitedir')
    def test_pth_files(self, addsitedir, testrunner, manage,
                       scripts, working_set, path_exists,):

        # When a pth-files option is set the recipe will use that to add more
        # paths to extra-paths.
        self.recipe.options['version'] = '1.0'
        path_exists.return_value = True
        working_set.return_value = (None, [])
        scripts.return_value = []
        manage.return_value = []
        testrunner.return_value = []

        # The mock values needed to demonstrate the pth-files option.
        addsitedir.return_value = ['extra', 'dirs']
        self.recipe.options['pth-files'] = 'somedir'

        self.recipe.install()
        self.assertEqual(addsitedir.call_args, (('somedir', set([])), {}))
        # The extra-paths option has been extended.
        self.assertEqual(self.recipe.options['extra-paths'], '\nextra\ndirs')

    def test_create_wsgi_script_projectegg(self):
        # When a projectegg is specified, then the egg specified
        # should get used as the project in the wsgi script.
        wsgi = os.path.join(self.bin_dir, 'django.wsgi')
        recipe_dir = os.path.abspath(
            os.path.join(os.path.dirname(__file__), '..'))
        self.recipe.options['projectegg'] = 'spameggs'
        self.recipe.options['wsgi'] = 'true'
        self.recipe.make_scripts([recipe_dir], [])
        self.assert_(os.path.exists(wsgi))
        # Check that we have 'spameggs' as the project
        self.assert_('spameggs.development' in open(wsgi).read())

    def test_settings_option(self):
        # The settings option can be used to specify the settings file
        # for Django to use. By default it uses `development`.
        self.assertEqual(self.recipe.options['settings'], 'development')
        # When we change it an generate a manage script it will use
        # this var.
        self.recipe.options['settings'] = 'spameggs'
        self.recipe.create_manage_script([], [])
        manage = os.path.join(self.bin_dir, 'django')
        self.assert_("djangorecipe.manage.main('project.spameggs')"
                     in open(manage).read())

    def test_python_option(self):
        # The python option makes it possible to specify a specific Python
        # executable which is to be used for the generated scripts.
        recipe = Recipe({
                'buildout': {
                    'eggs-directory': self.eggs_dir,
                    'develop-eggs-directory': self.develop_eggs_dir,
                    'python': 'python-version',
                    'bin-directory': self.bin_dir,
                    'parts-directory': self.parts_dir,
                    'directory': self.buildout_dir,
                    'find-links': '',
                    'allow-hosts': '',
                    },
                'python-version': {'executable': '/python4k'}},
                        'django',
                        {'recipe': 'djangorecipe',
                         'wsgi': 'true'})
        recipe.make_scripts([], [])
        # This should have created a script in the bin dir
        wsgi_script = os.path.join(self.bin_dir, 'django.wsgi')
        self.assertEqual(open(wsgi_script).readlines()[0], '#!/python4k\n')
        # Changeing the option for only the part will change the used Python
        # version.
        recipe = Recipe({
                'buildout': {'eggs-directory': self.eggs_dir,
                             'develop-eggs-directory': self.develop_eggs_dir,
                             'python': 'python-version',
                             'bin-directory': self.bin_dir,
                             'parts-directory': self.parts_dir,
                             'directory': self.buildout_dir,
                             'find-links': '',
                             'allow-hosts': '',
                             },
                'python-version': {'executable': '/python4k'},
                'py5k': {'executable': '/python5k'}},
                        'django',
                        {'recipe': 'djangorecipe',
                         'python': 'py5k', 'wsgi': 'true'})
        recipe.make_scripts([], [])
        self.assertEqual(open(wsgi_script).readlines()[0], '#!/python5k\n')

    def test_boilerplate_newest(self):
        """Test the default boilerplate."""

        project_dir = os.path.join(self.buildout_dir, 'project')

        secret = '$55upfci7a#gi@&e9o1-hb*k+f$3+(&b$j=cn67h#22*0%-bj0'
        self.recipe.generate_secret = lambda: secret

        self.recipe.create_project(project_dir)
        settings = open(os.path.join(project_dir, 'settings.py')).read()
        settings_dict = {'project': self.recipe.options['project'],
                         'secret': secret,
                         'urlconf': self.recipe.options['urlconf'],
                         }
        from djangorecipe.boilerplate import versions
        self.assertEquals(versions['Newest']['settings'] % settings_dict,
                          settings)

    def test_boilerplate_1_2(self):
        """Test the boilerplate for django 1.2."""

        recipe = Recipe({
                'buildout': {'eggs-directory': self.eggs_dir,
                             'develop-eggs-directory': self.develop_eggs_dir,
                             'python': 'python-version',
                             'bin-directory': self.bin_dir,
                             'parts-directory': self.parts_dir,
                             'directory': self.buildout_dir,
                             'find-links': '',
                             'allow-hosts': '',
                             'versions': 'versions',
                             },
                'versions': {'django': '1.2.5'},
                'python-version': {'executable': '/python4k'},
                'py5k': {'executable': '/python5k'}},
                        'django',
                        {'recipe': 'djangorecipe',
                         'python': 'py5k', 'wsgi': 'true'})

        secret = '$55upfci7a#gi@&e9o1-hb*k+f$3+(&b$j=cn67h#22*0%-bj0'
        recipe.generate_secret = lambda: secret

        project_dir = os.path.join(self.buildout_dir, 'project')
        recipe.create_project(project_dir)
        settings = open(os.path.join(project_dir, 'settings.py')).read()
        settings_dict = {'project': self.recipe.options['project'],
                         'secret': secret,
                         'urlconf': self.recipe.options['urlconf'],
                         }
        from djangorecipe.boilerplate import versions
        self.assertEquals(versions['1.2']['settings'] % settings_dict,
                          settings)

    def test_versions_deprecation(self):
        from zc.buildout import UserError
        options = {'recipe': 'djangorecipe',
                   'version': 'trunk',
                   'python': 'py5k', 'wsgi': 'true'}
        self.assertRaises(UserError, Recipe, *('buildout', 'test', options))
Esempio n. 17
0
class TestRecipe(unittest.TestCase):

    def setUp(self):
        # Create a directory for our buildout files created by the recipe
        self.buildout_dir = tempfile.mkdtemp('djangorecipe')

        self.bin_dir = os.path.join(self.buildout_dir, 'bin')
        self.develop_eggs_dir = os.path.join(self.buildout_dir,
                                             'develop-eggs')
        self.eggs_dir = os.path.join(self.buildout_dir, 'eggs')
        self.parts_dir = os.path.join(self.buildout_dir, 'parts')

        # We need to create the bin dir since the recipe should be able to
        # expect it exists
        os.mkdir(self.bin_dir)

        self.recipe = Recipe({
                'buildout': {
                    'eggs-directory': self.eggs_dir,
                    'develop-eggs-directory': self.develop_eggs_dir,
                    'python': 'python-version',
                    'bin-directory': self.bin_dir,
                    'parts-directory': self.parts_dir,
                    'directory': self.buildout_dir,
                    'find-links': '',
                    'allow-hosts': '',
                    },
                'python-version': {'executable': sys.executable}},
                             'django',
                             {'recipe': 'djangorecipe'})

    def tearDown(self):
        # Remove our test dir
        shutil.rmtree(self.buildout_dir)

    def test_consistent_options(self):
        # Buildout is pretty clever in detecting changing options. If
        # the recipe modifies it's options during initialisation it
        # will store this to determine wheter it needs to update or do
        # a uninstall & install. We need to make sure that we normally
        # do not trigger this. That means running the recipe with the
        # same options should give us the same results.
        self.assertEqual(*[
                Recipe({'buildout': {
                            'eggs-directory': self.eggs_dir,
                            'develop-eggs-directory': self.develop_eggs_dir,
                            'python': 'python-version',
                            'bin-directory': self.bin_dir,
                            'parts-directory': self.parts_dir,
                            'directory': self.buildout_dir,
                            'find-links': '',
                            'allow-hosts':'',
                            },
                        'python-version': {'executable': sys.executable}},
                       'django',
                       {'recipe': 'djangorecipe'}).options.copy()
                for i in range(2)])

    def test_create_file(self):
        # The create file helper should create a file at a certain
        # location unless it already exists. We will need a
        # non-existing file first.
        f, name = tempfile.mkstemp()
        # To show the function in action we need to delete the file
        # before testing.
        os.remove(name)
        # The method accepts a template argument which it will use
        # with the options argument for string substitution.
        self.recipe.create_file(name, 'Spam %s', 'eggs')
        # Let's check the contents of the file
        self.assertEqual(open(name).read(), 'Spam eggs')
        # If we try to write it again it will just ignore our request
        self.recipe.create_file(name, 'Spam spam spam %s', 'eggs')
        # The content of the file should therefore be the same
        self.assertEqual(open(name).read(), 'Spam eggs')
        # Now remove our temp file
        os.remove(name)

    def test_generate_secret(self):
        # To create a basic skeleton the recipe also generates a
        # random secret for the settings file. Since it should very
        # unlikely that it will generate the same key a few times in a
        # row we will test it with letting it generate a few keys.
        self.assert_(len(set(
                    [self.recipe.generate_secret() for i in range(10)])) > 1)

    def test_make_protocol_scripts(self):
        # To ease deployment a WSGI script can be generated. The
        # script adds any paths from the `extra_paths` option to the
        # Python path.
        self.recipe.options['wsgi'] = 'true'
        self.recipe.options['fcgi'] = 'true'
        self.recipe.make_scripts([], [])
        # This should have created a script in the bin dir
        wsgi_script = os.path.join(self.bin_dir, 'django.wsgi')
        self.assert_(os.path.exists(wsgi_script))
        # The contents should list our paths
        contents = open(wsgi_script).read()
        # It should also have a reference to our settings module
        self.assert_('project.development' in contents)
        # and a line which set's up the WSGI app
        self.assert_("application = "
                     "djangorecipe.wsgi.main('project.development', "
                     "logfile='')"
                     in contents)
        self.assert_("class logger(object)" not in contents)

        # Another deployment options is FCGI. The recipe supports an option to
        # automatically create the required script.
        fcgi_script = os.path.join(self.bin_dir, 'django.fcgi')
        self.assert_(os.path.exists(fcgi_script))
        # The contents should list our paths
        contents = open(fcgi_script).read()
        # It should also have a reference to our settings module
        self.assert_('project.development' in contents)
        # and a line which set's up the WSGI app
        self.assert_("djangorecipe.fcgi.main('project.development', "
                     "logfile='')"
                     in contents)
        self.assert_("class logger(object)" not in contents)

        self.recipe.options['logfile'] = '/foo'
        self.recipe.make_scripts([], [])
        wsgi_script = os.path.join(self.bin_dir, 'django.wsgi')
        contents = open(wsgi_script).read()
        self.assert_("logfile='/foo'" in contents)

        self.recipe.options['logfile'] = '/foo'
        self.recipe.make_scripts([], [])
        fcgi_script = os.path.join(self.bin_dir, 'django.fcgi')
        contents = open(fcgi_script).read()
        self.assert_("logfile='/foo'" in contents)

    @mock.patch('zc.buildout.easy_install.scripts')
    def test_make_protocol_scripts_return_value(self, scripts):
        # The return value of make scripts lists the generated scripts.
        self.recipe.options['wsgi'] = 'true'
        self.recipe.options['fcgi'] = 'true'
        scripts.return_value = ['some-path']
        self.assertEqual(self.recipe.make_scripts([], []),
                         ['some-path', 'some-path'])

    def test_create_project(self):
        # If a project does not exist already the recipe will create
        # one.
        project_dir = os.path.join(self.buildout_dir, 'project')
        self.recipe.create_project(project_dir)
        # This should have create a project directory
        self.assert_(os.path.exists(project_dir))
        # With this directory we should have __init__.py to make it a
        # package
        self.assert_(
            os.path.exists(os.path.join(project_dir, '__init__.py')))
        # There should also be a urls.py
        self.assert_(
            os.path.exists(os.path.join(project_dir, 'urls.py')))
        # To make it easier to start using this project both a media
        # and a templates folder are created
        self.assert_(
            os.path.exists(os.path.join(project_dir, 'media')))
        self.assert_(
            os.path.exists(os.path.join(project_dir, 'templates')))
        # The project is ready to go since the recipe has generated a
        # base settings, development and production file
        for f in ('settings.py', 'development.py', 'production.py'):
            self.assert_(
                os.path.exists(os.path.join(project_dir, f)))

    def test_create_test_runner(self):
        # An executable script can be generated which will make it
        # possible to execute the Django test runner. This options
        # only works if we specify one or apps to test.
        testrunner = os.path.join(self.bin_dir, 'test')

        # This first argument sets extra_paths, we will use this to
        # make sure the script can find this recipe
        recipe_dir = os.path.abspath(
            os.path.join(os.path.dirname(__file__), '..'))

        # First we will show it does nothing by default
        self.recipe.create_test_runner([recipe_dir], [])
        self.failIf(os.path.exists(testrunner))

        # When we specify an app to test it should create the the
        # testrunner
        self.recipe.options['test'] = 'knight'
        self.recipe.create_test_runner([recipe_dir], [])
        self.assert_(os.path.exists(testrunner))

    def test_create_manage_script(self):
        # This buildout recipe creates a alternative for the standard
        # manage.py script. It has all the same functionality as the
        # original one but it sits in the bin dir instead of within
        # the project.
        manage = os.path.join(self.bin_dir, 'django')
        self.recipe.create_manage_script([], [])
        self.assert_(os.path.exists(manage))

    def test_create_manage_script_projectegg(self):
        # When a projectegg is specified, then the egg specified
        # should get used as the project file.
        manage = os.path.join(self.bin_dir, 'django')
        self.recipe.options['projectegg'] = 'spameggs'
        self.recipe.create_manage_script([], [])
        self.assert_(os.path.exists(manage))
        # Check that we have 'spameggs' as the project
        self.assert_("djangorecipe.manage.main('spameggs.development')"
                     in open(manage).read())

    @mock.patch('os.path.exists')
    @mock.patch('zc.recipe.egg.egg.Scripts.working_set')
    @mock.patch('zc.buildout.easy_install.scripts')
    @mock.patch('djangorecipe.recipe.Recipe.create_manage_script')
    @mock.patch('djangorecipe.recipe.Recipe.create_test_runner')
    def test_extra_paths(self, testrunner, manage, scripts,
                         working_set, path_exists):

        # The recipe allows extra-paths to be specified. It uses these to
        # extend the Python path within it's generated scripts.
        self.recipe.options['version'] = '1.0'
        self.recipe.options['extra-paths'] = 'somepackage\nanotherpackage'
        path_exists.return_value = True
        working_set.return_value = (None, [])
        manage.return_value = []
        scripts.return_value = []
        testrunner.return_value = []
        self.recipe.install()
        self.assertEqual(manage.call_args[0][0][-2:],
                         ['somepackage', 'anotherpackage'])

    @mock.patch('os.path.exists')
    @mock.patch('zc.recipe.egg.egg.Scripts.working_set')
    @mock.patch('zc.buildout.easy_install.scripts')
    @mock.patch('djangorecipe.recipe.Recipe.create_manage_script')
    @mock.patch('djangorecipe.recipe.Recipe.create_test_runner')
    @mock.patch('site.addsitedir')
    def test_pth_files(self, addsitedir, testrunner, manage,
                       scripts, working_set, path_exists,):

        # When a pth-files option is set the recipe will use that to add more
        # paths to extra-paths.
        self.recipe.options['version'] = '1.0'
        path_exists.return_value = True
        working_set.return_value = (None, [])
        scripts.return_value = []
        manage.return_value = []
        testrunner.return_value = []

        # The mock values needed to demonstrate the pth-files option.
        addsitedir.return_value = ['extra', 'dirs']
        self.recipe.options['pth-files'] = 'somedir'

        self.recipe.install()
        self.assertEqual(addsitedir.call_args, (('somedir', set([])), {}))
        # The extra-paths option has been extended.
        self.assertEqual(self.recipe.options['extra-paths'], '\nextra\ndirs')

    def test_create_wsgi_script_projectegg(self):
        # When a projectegg is specified, then the egg specified
        # should get used as the project in the wsgi script.
        wsgi = os.path.join(self.bin_dir, 'django.wsgi')
        recipe_dir = os.path.abspath(
            os.path.join(os.path.dirname(__file__), '..'))
        self.recipe.options['projectegg'] = 'spameggs'
        self.recipe.options['wsgi'] = 'true'
        self.recipe.make_scripts([recipe_dir], [])
        self.assert_(os.path.exists(wsgi))
        # Check that we have 'spameggs' as the project
        self.assert_('spameggs.development' in open(wsgi).read())

    def test_settings_option(self):
        # The settings option can be used to specify the settings file
        # for Django to use. By default it uses `development`.
        self.assertEqual(self.recipe.options['settings'], 'development')
        # When we change it an generate a manage script it will use
        # this var.
        self.recipe.options['settings'] = 'spameggs'
        self.recipe.create_manage_script([], [])
        manage = os.path.join(self.bin_dir, 'django')
        self.assert_("djangorecipe.manage.main('project.spameggs')"
                     in open(manage).read())

    def test_python_option(self):
        # The python option makes it possible to specify a specific Python
        # executable which is to be used for the generated scripts.
        recipe = Recipe({
                'buildout': {
                    'eggs-directory': self.eggs_dir,
                    'develop-eggs-directory': self.develop_eggs_dir,
                    'python': 'python-version',
                    'bin-directory': self.bin_dir,
                    'parts-directory': self.parts_dir,
                    'directory': self.buildout_dir,
                    'find-links': '',
                    'allow-hosts': '',
                    },
                'python-version': {'executable': '/python4k'}},
                        'django',
                        {'recipe': 'djangorecipe',
                         'wsgi': 'true'})
        recipe.make_scripts([], [])
        # This should have created a script in the bin dir
        wsgi_script = os.path.join(self.bin_dir, 'django.wsgi')
        self.assertEqual(open(wsgi_script).readlines()[0], '#!/python4k\n')
        # Changeing the option for only the part will change the used Python
        # version.
        recipe = Recipe({
                'buildout': {'eggs-directory': self.eggs_dir,
                             'develop-eggs-directory': self.develop_eggs_dir,
                             'python': 'python-version',
                             'bin-directory': self.bin_dir,
                             'parts-directory': self.parts_dir,
                             'directory': self.buildout_dir,
                             'find-links': '',
                             'allow-hosts': '',
                             },
                'python-version': {'executable': '/python4k'},
                'py5k': {'executable': '/python5k'}},
                        'django',
                        {'recipe': 'djangorecipe',
                         'python': 'py5k', 'wsgi': 'true'})
        recipe.make_scripts([], [])
        self.assertEqual(open(wsgi_script).readlines()[0], '#!/python5k\n')

    def test_boilerplate_newest(self):
        """Test the default boilerplate."""

        project_dir = os.path.join(self.buildout_dir, 'project')

        secret = '$55upfci7a#gi@&e9o1-hb*k+f$3+(&b$j=cn67h#22*0%-bj0'
        self.recipe.generate_secret = lambda: secret

        self.recipe.create_project(project_dir)
        settings = open(os.path.join(project_dir, 'settings.py')).read()
        settings_dict = {'project': self.recipe.options['project'],
                         'secret': secret,
                         'urlconf': self.recipe.options['urlconf'],
                         }
        from djangorecipe.boilerplate import versions
        self.assertEquals(versions['Newest']['settings'] % settings_dict,
                          settings)

    def test_boilerplate_1_2(self):
        """Test the boilerplate for django 1.2."""

        recipe = Recipe({
                'buildout': {'eggs-directory': self.eggs_dir,
                             'develop-eggs-directory': self.develop_eggs_dir,
                             'python': 'python-version',
                             'bin-directory': self.bin_dir,
                             'parts-directory': self.parts_dir,
                             'directory': self.buildout_dir,
                             'find-links': '',
                             'allow-hosts': '',
                             'versions': 'versions',
                             },
                'versions': {'django': '1.2.5'},
                'python-version': {'executable': '/python4k'},
                'py5k': {'executable': '/python5k'}},
                        'django',
                        {'recipe': 'djangorecipe',
                         'python': 'py5k', 'wsgi': 'true'})

        secret = '$55upfci7a#gi@&e9o1-hb*k+f$3+(&b$j=cn67h#22*0%-bj0'
        recipe.generate_secret = lambda: secret

        project_dir = os.path.join(self.buildout_dir, 'project')
        recipe.create_project(project_dir)
        settings = open(os.path.join(project_dir, 'settings.py')).read()
        settings_dict = {'project': self.recipe.options['project'],
                         'secret': secret,
                         'urlconf': self.recipe.options['urlconf'],
                         }
        from djangorecipe.boilerplate import versions
        self.assertEquals(versions['1.2']['settings'] % settings_dict,
                          settings)

    def test_versions_deprecation(self):
        from zc.buildout import UserError
        options = {'recipe': 'djangorecipe',
                   'version': 'trunk',
                   'python': 'py5k', 'wsgi': 'true'}
        self.assertRaises(UserError, Recipe, *('buildout', 'test', options))