github.com/jiasir/deis@v1.12.2/controller/api/tests/test_build.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  
    11  from django.contrib.auth.models import User
    12  from django.test import TransactionTestCase
    13  import mock
    14  from rest_framework.authtoken.models import Token
    15  
    16  from api.models import Build
    17  from . import mock_status_ok
    18  
    19  
    20  @mock.patch('api.models.publish_release', lambda *args: None)
    21  class BuildTest(TransactionTestCase):
    22  
    23      """Tests build notification from build system"""
    24  
    25      fixtures = ['tests.json']
    26  
    27      def setUp(self):
    28          self.user = User.objects.get(username='autotest')
    29          self.token = Token.objects.get(user=self.user).key
    30  
    31      @mock.patch('requests.post', mock_status_ok)
    32      def test_build(self):
    33          """
    34          Test that a null build is created and that users can post new builds
    35          """
    36          url = '/v1/apps'
    37          response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
    38          self.assertEqual(response.status_code, 201)
    39          app_id = response.data['id']
    40          # check to see that no initial build was created
    41          url = "/v1/apps/{app_id}/builds".format(**locals())
    42          response = self.client.get(url,
    43                                     HTTP_AUTHORIZATION='token {}'.format(self.token))
    44          self.assertEqual(response.status_code, 200)
    45          self.assertEqual(response.data['count'], 0)
    46          # post a new build
    47          body = {'image': 'autotest/example'}
    48          response = self.client.post(url, json.dumps(body), content_type='application/json',
    49                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
    50          self.assertEqual(response.status_code, 201)
    51          build_id = response.data['uuid']
    52          build1 = response.data
    53          self.assertEqual(response.data['image'], body['image'])
    54          # read the build
    55          url = "/v1/apps/{app_id}/builds/{build_id}".format(**locals())
    56          response = self.client.get(url,
    57                                     HTTP_AUTHORIZATION='token {}'.format(self.token))
    58          self.assertEqual(response.status_code, 200)
    59          build2 = response.data
    60          self.assertEqual(build1, build2)
    61          # post a new build
    62          url = "/v1/apps/{app_id}/builds".format(**locals())
    63          body = {'image': 'autotest/example'}
    64          response = self.client.post(url, json.dumps(body), content_type='application/json',
    65                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
    66          self.assertEqual(response.status_code, 201)
    67          self.assertIn('x-deis-release', response._headers)
    68          build3 = response.data
    69          self.assertEqual(response.data['image'], body['image'])
    70          self.assertNotEqual(build2['uuid'], build3['uuid'])
    71          # disallow put/patch/delete
    72          response = self.client.put(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
    73          self.assertEqual(response.status_code, 405)
    74          response = self.client.patch(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
    75          self.assertEqual(response.status_code, 405)
    76          response = self.client.delete(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
    77          self.assertEqual(response.status_code, 405)
    78  
    79      @mock.patch('requests.post', mock_status_ok)
    80      def test_response_data(self):
    81          """Test that the serialized response contains only relevant data."""
    82          body = {'id': 'test'}
    83          url = '/v1/apps'
    84          response = self.client.post(url, json.dumps(body),
    85                                      content_type='application/json',
    86                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
    87          # post an image as a build
    88          url = "/v1/apps/test/builds".format(**locals())
    89          body = {'image': 'autotest/example'}
    90          response = self.client.post(url, json.dumps(body), content_type='application/json',
    91                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
    92          for key in response.data:
    93              self.assertIn(key, ['uuid', 'owner', 'created', 'updated', 'app', 'dockerfile',
    94                                  'image', 'procfile', 'sha'])
    95          expected = {
    96              'owner': self.user.username,
    97              'app': 'test',
    98              'dockerfile': '',
    99              'image': 'autotest/example',
   100              'procfile': {},
   101              'sha': ''
   102          }
   103          self.assertDictContainsSubset(expected, response.data)
   104  
   105      @mock.patch('requests.post', mock_status_ok)
   106      def test_build_default_containers(self):
   107          url = '/v1/apps'
   108          response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
   109          self.assertEqual(response.status_code, 201)
   110          app_id = response.data['id']
   111          # post an image as a build
   112          url = "/v1/apps/{app_id}/builds".format(**locals())
   113          body = {'image': 'autotest/example'}
   114          response = self.client.post(url, json.dumps(body), content_type='application/json',
   115                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   116          self.assertEqual(response.status_code, 201)
   117          url = "/v1/apps/{app_id}/containers/cmd".format(**locals())
   118          response = self.client.get(url,
   119                                     HTTP_AUTHORIZATION='token {}'.format(self.token))
   120          self.assertEqual(response.status_code, 200)
   121          self.assertEqual(len(response.data['results']), 1)
   122          container = response.data['results'][0]
   123          self.assertEqual(container['type'], 'cmd')
   124          self.assertEqual(container['num'], 1)
   125          # start with a new app
   126          url = '/v1/apps'
   127          response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
   128          self.assertEqual(response.status_code, 201)
   129          app_id = response.data['id']
   130          # post a new build with procfile
   131          url = "/v1/apps/{app_id}/builds".format(**locals())
   132          body = {'image': 'autotest/example',
   133                  'sha': 'a'*40,
   134                  'dockerfile': "FROM scratch"}
   135          response = self.client.post(url, json.dumps(body), content_type='application/json',
   136                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   137          self.assertEqual(response.status_code, 201)
   138          url = "/v1/apps/{app_id}/containers/cmd".format(**locals())
   139          response = self.client.get(url,
   140                                     HTTP_AUTHORIZATION='token {}'.format(self.token))
   141          self.assertEqual(response.status_code, 200)
   142          self.assertEqual(len(response.data['results']), 1)
   143          container = response.data['results'][0]
   144          self.assertEqual(container['type'], 'cmd')
   145          self.assertEqual(container['num'], 1)
   146          # start with a new app
   147          url = '/v1/apps'
   148          response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
   149          self.assertEqual(response.status_code, 201)
   150          app_id = response.data['id']
   151          # post a new build with procfile
   152          url = "/v1/apps/{app_id}/builds".format(**locals())
   153          body = {'image': 'autotest/example',
   154                  'sha': 'a'*40,
   155                  'dockerfile': "FROM scratch",
   156                  'procfile': {'worker': 'node worker.js'}}
   157          response = self.client.post(url, json.dumps(body), content_type='application/json',
   158                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   159          self.assertEqual(response.status_code, 201)
   160          url = "/v1/apps/{app_id}/containers/cmd".format(**locals())
   161          response = self.client.get(url,
   162                                     HTTP_AUTHORIZATION='token {}'.format(self.token))
   163          self.assertEqual(response.status_code, 200)
   164          self.assertEqual(len(response.data['results']), 1)
   165          container = response.data['results'][0]
   166          self.assertEqual(container['type'], 'cmd')
   167          self.assertEqual(container['num'], 1)
   168          # start with a new app
   169          url = '/v1/apps'
   170          response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
   171          self.assertEqual(response.status_code, 201)
   172          app_id = response.data['id']
   173          # post a new build with procfile
   174          url = "/v1/apps/{app_id}/builds".format(**locals())
   175          body = {'image': 'autotest/example',
   176                  'sha': 'a'*40,
   177                  'procfile': json.dumps({'web': 'node server.js',
   178                                          'worker': 'node worker.js'})}
   179          response = self.client.post(url, json.dumps(body), content_type='application/json',
   180                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   181          self.assertEqual(response.status_code, 201)
   182          url = "/v1/apps/{app_id}/containers/web".format(**locals())
   183          response = self.client.get(url,
   184                                     HTTP_AUTHORIZATION='token {}'.format(self.token))
   185          self.assertEqual(response.status_code, 200)
   186          self.assertEqual(len(response.data['results']), 1)
   187          container = response.data['results'][0]
   188          self.assertEqual(container['type'], 'web')
   189          self.assertEqual(container['num'], 1)
   190  
   191      @mock.patch('requests.post', mock_status_ok)
   192      def test_build_str(self):
   193          """Test the text representation of a build."""
   194          url = '/v1/apps'
   195          response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
   196          self.assertEqual(response.status_code, 201)
   197          app_id = response.data['id']
   198          # post a new build
   199          url = "/v1/apps/{app_id}/builds".format(**locals())
   200          body = {'image': 'autotest/example'}
   201          response = self.client.post(url, json.dumps(body), content_type='application/json',
   202                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   203          self.assertEqual(response.status_code, 201)
   204          build = Build.objects.get(uuid=response.data['uuid'])
   205          self.assertEqual(str(build), "{}-{}".format(
   206                           response.data['app'], response.data['uuid'][:7]))
   207  
   208      @mock.patch('requests.post', mock_status_ok)
   209      def test_admin_can_create_builds_on_other_apps(self):
   210          """If a user creates an application, an administrator should be able
   211          to push builds.
   212          """
   213          # create app as non-admin
   214          user = User.objects.get(username='autotest2')
   215          token = Token.objects.get(user=user).key
   216          url = '/v1/apps'
   217          response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(token))
   218          self.assertEqual(response.status_code, 201)
   219          app_id = response.data['id']
   220          # post a new build as admin
   221          url = "/v1/apps/{app_id}/builds".format(**locals())
   222          body = {'image': 'autotest/example'}
   223          response = self.client.post(url, json.dumps(body), content_type='application/json',
   224                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   225          self.assertEqual(response.status_code, 201)
   226          build = Build.objects.get(uuid=response.data['uuid'])
   227          self.assertEqual(str(build), "{}-{}".format(
   228                           response.data['app'], response.data['uuid'][:7]))
   229  
   230      @mock.patch('requests.post', mock_status_ok)
   231      def test_unauthorized_user_cannot_modify_build(self):
   232          """
   233          An unauthorized user should not be able to modify other builds.
   234  
   235          Since an unauthorized user can't access the application, these
   236          requests should return a 403.
   237          """
   238          app_id = 'autotest'
   239          url = '/v1/apps'
   240          body = {'id': app_id}
   241          response = self.client.post(url, json.dumps(body), content_type='application/json',
   242                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   243          unauthorized_user = User.objects.get(username='autotest2')
   244          unauthorized_token = Token.objects.get(user=unauthorized_user).key
   245          url = '{}/{}/builds'.format(url, app_id)
   246          body = {'image': 'foo'}
   247          response = self.client.post(url, json.dumps(body), content_type='application/json',
   248                                      HTTP_AUTHORIZATION='token {}'.format(unauthorized_token))
   249          self.assertEqual(response.status_code, 403)
   250  
   251      @mock.patch('requests.post', mock_status_ok)
   252      def test_new_build_does_not_scale_up_automatically(self):
   253          """
   254          After the first initial deploy, if the containers are scaled down to zero,
   255          they should stay that way on a new release.
   256          """
   257          url = '/v1/apps'
   258          response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
   259          self.assertEqual(response.status_code, 201)
   260          app_id = response.data['id']
   261          # post a new build
   262          url = "/v1/apps/{app_id}/builds".format(**locals())
   263          body = {'image': 'autotest/example',
   264                  'sha': 'a'*40,
   265                  'procfile': json.dumps({'web': 'node server.js',
   266                                          'worker': 'node worker.js'})}
   267          response = self.client.post(url, json.dumps(body), content_type='application/json',
   268                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   269          self.assertEqual(response.status_code, 201)
   270          url = "/v1/apps/{app_id}/containers/web".format(**locals())
   271          response = self.client.get(url,
   272                                     HTTP_AUTHORIZATION='token {}'.format(self.token))
   273          self.assertEqual(response.status_code, 200)
   274          self.assertEqual(len(response.data['results']), 1)
   275          # scale to zero
   276          url = "/v1/apps/{app_id}/scale".format(**locals())
   277          body = {'web': 0}
   278          response = self.client.post(url, json.dumps(body), content_type='application/json',
   279                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   280          self.assertEqual(response.status_code, 204)
   281          # post another build
   282          url = "/v1/apps/{app_id}/builds".format(**locals())
   283          body = {'image': 'autotest/example',
   284                  'sha': 'a'*40,
   285                  'procfile': json.dumps({'web': 'node server.js',
   286                                          'worker': 'node worker.js'})}
   287          response = self.client.post(url, json.dumps(body), content_type='application/json',
   288                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   289          self.assertEqual(response.status_code, 201)
   290          url = "/v1/apps/{app_id}/containers/web".format(**locals())
   291          response = self.client.get(url,
   292                                     HTTP_AUTHORIZATION='token {}'.format(self.token))
   293          self.assertEqual(response.status_code, 200)
   294          self.assertEqual(len(response.data['results']), 0)