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)