github.com/tmlbl/deis@v1.0.2/controller/api/tests/test_app.py (about)

     1  """
     2  Unit tests for the Deis api app.
     3  
     4  Run the tests with "./manage.py test api"
     5  """
     6  
     7  from __future__ import unicode_literals
     8  
     9  import json
    10  import mock
    11  import os.path
    12  import requests
    13  
    14  from django.conf import settings
    15  from django.contrib.auth.models import User
    16  from django.test import TestCase
    17  from rest_framework.authtoken.models import Token
    18  
    19  from api.models import App
    20  
    21  
    22  def mock_import_repository_task(*args, **kwargs):
    23      resp = requests.Response()
    24      resp.status_code = 200
    25      resp._content_consumed = True
    26      return resp
    27  
    28  
    29  class AppTest(TestCase):
    30      """Tests creation of applications"""
    31  
    32      fixtures = ['tests.json']
    33  
    34      def setUp(self):
    35          self.user = User.objects.get(username='autotest')
    36          self.token = Token.objects.get(user=self.user).key
    37          # provide mock authentication used for run commands
    38          settings.SSH_PRIVATE_KEY = '<some-ssh-private-key>'
    39  
    40      def tearDown(self):
    41          # reset global vars for other tests
    42          settings.SSH_PRIVATE_KEY = ''
    43  
    44      def test_app(self):
    45          """
    46          Test that a user can create, read, update and delete an application
    47          """
    48          url = '/v1/apps'
    49          response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
    50          self.assertEqual(response.status_code, 201)
    51          app_id = response.data['id']  # noqa
    52          self.assertIn('id', response.data)
    53          self.assertIn('url', response.data)
    54          self.assertEqual(response.data['url'], '{app_id}.deisapp.local'.format(**locals()))
    55          response = self.client.get('/v1/apps',
    56                                     HTTP_AUTHORIZATION='token {}'.format(self.token))
    57          self.assertEqual(response.status_code, 200)
    58          self.assertEqual(len(response.data['results']), 1)
    59          url = '/v1/apps/{app_id}'.format(**locals())
    60          response = self.client.get(url,
    61                                     HTTP_AUTHORIZATION='token {}'.format(self.token))
    62          self.assertEqual(response.status_code, 200)
    63          body = {'id': 'new'}
    64          response = self.client.patch(url, json.dumps(body), content_type='application/json',
    65                                       HTTP_AUTHORIZATION='token {}'.format(self.token))
    66          self.assertEqual(response.status_code, 405)
    67          response = self.client.delete(url,
    68                                        HTTP_AUTHORIZATION='token {}'.format(self.token))
    69          self.assertEqual(response.status_code, 204)
    70  
    71      def test_app_override_id(self):
    72          body = {'id': 'myid'}
    73          response = self.client.post('/v1/apps', json.dumps(body),
    74                                      content_type='application/json',
    75                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
    76          self.assertEqual(response.status_code, 201)
    77          body = {'id': response.data['id']}
    78          response = self.client.post('/v1/apps', json.dumps(body),
    79                                      content_type='application/json',
    80                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
    81          self.assertContains(response, 'App with this Id already exists.', status_code=400)
    82          return response
    83  
    84      def test_app_actions(self):
    85          url = '/v1/apps'
    86          body = {'id': 'autotest'}
    87          response = self.client.post(url, json.dumps(body), content_type='application/json',
    88                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
    89          self.assertEqual(response.status_code, 201)
    90          app_id = response.data['id']  # noqa
    91          # test logs
    92          if not os.path.exists(settings.DEIS_LOG_DIR):
    93              os.mkdir(settings.DEIS_LOG_DIR)
    94          path = os.path.join(settings.DEIS_LOG_DIR, app_id + '.log')
    95          # HACK: remove app lifecycle logs
    96          if os.path.exists(path):
    97              os.remove(path)
    98          url = '/v1/apps/{app_id}/logs'.format(**locals())
    99          response = self.client.get(url,
   100                                     HTTP_AUTHORIZATION='token {}'.format(self.token))
   101          self.assertEqual(response.status_code, 204)
   102          self.assertEqual(response.data, 'No logs for {}'.format(app_id))
   103          # write out some fake log data and try again
   104          with open(path, 'a') as f:
   105              f.write(FAKE_LOG_DATA)
   106          response = self.client.get(url,
   107                                     HTTP_AUTHORIZATION='token {}'.format(self.token))
   108          self.assertEqual(response.status_code, 200)
   109          self.assertEqual(response.data, FAKE_LOG_DATA)
   110          os.remove(path)
   111          # TODO: test run needs an initial build
   112  
   113      def test_app_release_notes_in_logs(self):
   114          """Verifies that an app's release summary is dumped into the logs."""
   115          url = '/v1/apps'
   116          body = {'id': 'autotest'}
   117          response = self.client.post(url, json.dumps(body), content_type='application/json',
   118                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   119          self.assertEqual(response.status_code, 201)
   120          app_id = response.data['id']  # noqa
   121          path = os.path.join(settings.DEIS_LOG_DIR, app_id + '.log')
   122          url = '/v1/apps/{app_id}/logs'.format(**locals())
   123          response = self.client.get(url,
   124                                     HTTP_AUTHORIZATION='token {}'.format(self.token))
   125          self.assertIn('autotest created initial release', response.data)
   126          self.assertEqual(response.status_code, 200)
   127          # delete file for future runs
   128          os.remove(path)
   129  
   130      def test_app_errors(self):
   131          app_id = 'autotest-errors'
   132          url = '/v1/apps'
   133          body = {'id': 'camelCase'}
   134          response = self.client.post(url, json.dumps(body), content_type='application/json',
   135                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   136          self.assertContains(response, 'App IDs can only contain [a-z0-9-]', status_code=400)
   137          url = '/v1/apps'
   138          body = {'id': 'deis'}
   139          response = self.client.post(url, json.dumps(body), content_type='application/json',
   140                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   141          self.assertContains(response, "App IDs cannot be 'deis'", status_code=400)
   142          body = {'id': app_id}
   143          response = self.client.post(url, json.dumps(body), content_type='application/json',
   144                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   145          self.assertEqual(response.status_code, 201)
   146          app_id = response.data['id']  # noqa
   147          url = '/v1/apps/{app_id}'.format(**locals())
   148          response = self.client.delete(url,
   149                                        HTTP_AUTHORIZATION='token {}'.format(self.token))
   150          self.assertEquals(response.status_code, 204)
   151          for endpoint in ('containers', 'config', 'releases', 'builds'):
   152              url = '/v1/apps/{app_id}/{endpoint}'.format(**locals())
   153              response = self.client.get(url,
   154                                         HTTP_AUTHORIZATION='token {}'.format(self.token))
   155              self.assertEquals(response.status_code, 404)
   156  
   157      def test_app_structure_is_valid_json(self):
   158          """Application structures should be valid JSON objects."""
   159          url = '/v1/apps'
   160          response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
   161          self.assertEqual(response.status_code, 201)
   162          app_id = response.data['id']
   163          self.assertIn('structure', response.data)
   164          self.assertEqual(response.data['structure'], {})
   165          app = App.objects.get(id=app_id)
   166          app.structure = {'web': 1}
   167          app.save()
   168          url = '/v1/apps/{}'.format(app_id)
   169          response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
   170          self.assertIn('structure', response.data)
   171          self.assertEqual(response.data['structure'], {"web": 1})
   172  
   173      @mock.patch('requests.post', mock_import_repository_task)
   174      def test_admin_can_manage_other_apps(self):
   175          """Administrators of Deis should be able to manage all applications.
   176          """
   177          # log in as non-admin user and create an app
   178          user = User.objects.get(username='autotest2')
   179          token = Token.objects.get(user=user)
   180          app_id = 'autotest'
   181          url = '/v1/apps'
   182          body = {'id': app_id}
   183          response = self.client.post(url, json.dumps(body), content_type='application/json',
   184                                      HTTP_AUTHORIZATION='token {}'.format(token))
   185          app = App.objects.get(id=app_id)
   186          # log in as admin, check to see if they have access
   187          url = '/v1/apps/{}'.format(app_id)
   188          response = self.client.get(url,
   189                                     HTTP_AUTHORIZATION='token {}'.format(self.token))
   190          self.assertEqual(response.status_code, 200)
   191          # check app logs
   192          url = '/v1/apps/{app_id}/logs'.format(**locals())
   193          response = self.client.get(url,
   194                                     HTTP_AUTHORIZATION='token {}'.format(self.token))
   195          self.assertEqual(response.status_code, 200)
   196          self.assertIn('autotest2 created initial release', response.data)
   197          # TODO: test run needs an initial build
   198          # delete the app
   199          url = '/v1/apps/{}'.format(app_id)
   200          response = self.client.delete(url,
   201                                        HTTP_AUTHORIZATION='token {}'.format(self.token))
   202          self.assertEqual(response.status_code, 204)
   203  
   204      def test_admin_can_see_other_apps(self):
   205          """If a user creates an application, the administrator should be able
   206          to see it.
   207          """
   208          # log in as non-admin user and create an app
   209          user = User.objects.get(username='autotest2')
   210          token = Token.objects.get(user=user)
   211          app_id = 'autotest'
   212          url = '/v1/apps'
   213          body = {'id': app_id}
   214          response = self.client.post(url, json.dumps(body), content_type='application/json',
   215                                      HTTP_AUTHORIZATION='token {}'.format(token))
   216          # log in as admin
   217          response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
   218          self.assertEqual(response.data['count'], 1)
   219  
   220      def test_run_without_auth(self):
   221          """If the administrator has not provided SSH private key for run commands,
   222          make sure a friendly error message is provided on run"""
   223          settings.SSH_PRIVATE_KEY = ''
   224          url = '/v1/apps'
   225          body = {'id': 'autotest'}
   226          response = self.client.post(url, json.dumps(body), content_type='application/json',
   227                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   228          self.assertEqual(response.status_code, 201)
   229          app_id = response.data['id']  # noqa
   230          # test run
   231          url = '/v1/apps/{app_id}/run'.format(**locals())
   232          body = {'command': 'ls -al'}
   233          response = self.client.post(url, json.dumps(body), content_type='application/json',
   234                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   235          self.assertEquals(response.status_code, 400)
   236          self.assertEquals(response.data, 'Support for admin commands is not configured')
   237  
   238      def test_run_without_release_should_error(self):
   239          """
   240          A user should not be able to run a one-off command unless a release
   241          is present.
   242          """
   243          app_id = 'autotest'
   244          url = '/v1/apps'
   245          body = {'id': app_id}
   246          response = self.client.post(url, json.dumps(body), content_type='application/json',
   247                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   248          url = '/v1/apps/{}/run'.format(app_id)
   249          body = {'command': 'ls -al'}
   250          response = self.client.post(url, json.dumps(body), content_type='application/json',
   251                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   252          self.assertEqual(response.status_code, 400)
   253          self.assertEqual(response.data, "No build associated with this release "
   254                                          "to run this command")
   255  
   256  
   257  FAKE_LOG_DATA = """
   258  2013-08-15 12:41:25 [33454] [INFO] Starting gunicorn 17.5
   259  2013-08-15 12:41:25 [33454] [INFO] Listening at: http://0.0.0.0:5000 (33454)
   260  2013-08-15 12:41:25 [33454] [INFO] Using worker: sync
   261  2013-08-15 12:41:25 [33457] [INFO] Booting worker with pid 33457
   262  """