github.com/amrnt/deis@v1.3.1/controller/api/tests/test_release.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 requests 12 13 from django.contrib.auth.models import User 14 from django.test import TransactionTestCase 15 from rest_framework.authtoken.models import Token 16 17 from api.models import Release 18 19 20 def mock_import_repository_task(*args, **kwargs): 21 resp = requests.Response() 22 resp.status_code = 200 23 resp._content_consumed = True 24 return resp 25 26 27 class ReleaseTest(TransactionTestCase): 28 29 """Tests push notification from build system""" 30 31 fixtures = ['tests.json'] 32 33 def setUp(self): 34 self.user = User.objects.get(username='autotest') 35 self.token = Token.objects.get(user=self.user).key 36 37 @mock.patch('requests.post', mock_import_repository_task) 38 def test_release(self): 39 """ 40 Test that a release is created when an app is created, and 41 that updating config or build or triggers a new release 42 """ 43 url = '/v1/apps' 44 response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 45 self.assertEqual(response.status_code, 201) 46 app_id = response.data['id'] 47 # check that updating config rolls a new release 48 url = '/v1/apps/{app_id}/config'.format(**locals()) 49 body = {'values': json.dumps({'NEW_URL1': 'http://localhost:8080/'})} 50 response = self.client.post( 51 url, json.dumps(body), content_type='application/json', 52 HTTP_AUTHORIZATION='token {}'.format(self.token)) 53 self.assertEqual(response.status_code, 201) 54 self.assertIn('NEW_URL1', response.data['values']) 55 # check to see that an initial release was created 56 url = '/v1/apps/{app_id}/releases'.format(**locals()) 57 response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 58 self.assertEqual(response.status_code, 200) 59 # account for the config release as well 60 self.assertEqual(response.data['count'], 2) 61 url = '/v1/apps/{app_id}/releases/v1'.format(**locals()) 62 response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 63 self.assertEqual(response.status_code, 200) 64 release1 = response.data 65 self.assertIn('config', response.data) 66 self.assertIn('build', response.data) 67 self.assertEquals(release1['version'], 1) 68 # check to see that a new release was created 69 url = '/v1/apps/{app_id}/releases/v2'.format(**locals()) 70 response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 71 self.assertEqual(response.status_code, 200) 72 release2 = response.data 73 self.assertNotEqual(release1['uuid'], release2['uuid']) 74 self.assertNotEqual(release1['config'], release2['config']) 75 self.assertEqual(release1['build'], release2['build']) 76 self.assertEquals(release2['version'], 2) 77 # check that updating the build rolls a new release 78 url = '/v1/apps/{app_id}/builds'.format(**locals()) 79 build_config = json.dumps({'PATH': 'bin:/usr/local/bin:/usr/bin:/bin'}) 80 body = {'image': 'autotest/example'} 81 response = self.client.post( 82 url, json.dumps(body), content_type='application/json', 83 HTTP_AUTHORIZATION='token {}'.format(self.token)) 84 self.assertEqual(response.status_code, 201) 85 self.assertEqual(response.data['image'], body['image']) 86 # check to see that a new release was created 87 url = '/v1/apps/{app_id}/releases/v3'.format(**locals()) 88 response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 89 self.assertEqual(response.status_code, 200) 90 release3 = response.data 91 self.assertNotEqual(release2['uuid'], release3['uuid']) 92 self.assertNotEqual(release2['build'], release3['build']) 93 self.assertEquals(release3['version'], 3) 94 # check that we can fetch a previous release 95 url = '/v1/apps/{app_id}/releases/v2'.format(**locals()) 96 response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 97 self.assertEqual(response.status_code, 200) 98 release2 = response.data 99 self.assertNotEqual(release2['uuid'], release3['uuid']) 100 self.assertNotEqual(release2['build'], release3['build']) 101 self.assertEquals(release2['version'], 2) 102 # disallow post/put/patch/delete 103 url = '/v1/apps/{app_id}/releases'.format(**locals()) 104 response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 105 self.assertEqual(response.status_code, 405) 106 response = self.client.put(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 107 self.assertEqual(response.status_code, 405) 108 response = self.client.patch(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 109 self.assertEqual(response.status_code, 405) 110 response = self.client.delete(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 111 self.assertEqual(response.status_code, 405) 112 return release3 113 114 @mock.patch('requests.post', mock_import_repository_task) 115 def test_response_data(self): 116 body = {'id': 'test'} 117 response = self.client.post('/v1/apps', json.dumps(body), 118 content_type='application/json', 119 HTTP_AUTHORIZATION='token {}'.format(self.token)) 120 body = {'values': json.dumps({'NEW_URL': 'http://localhost:8080/'})} 121 config_response = self.client.post('/v1/apps/test/config', json.dumps(body), 122 content_type='application/json', 123 HTTP_AUTHORIZATION='token {}'.format(self.token)) 124 url = '/v1/apps/test/releases/v2' 125 response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 126 for key in response.data.keys(): 127 self.assertIn(key, ['uuid', 'owner', 'created', 'updated', 'app', 'build', 'config', 128 'summary', 'version']) 129 expected = { 130 'owner': self.user.username, 131 'app': 'test', 132 'build': None, 133 'config': config_response.data['uuid'], 134 'summary': '{} added NEW_URL'.format(self.user.username), 135 'version': 2 136 } 137 self.assertDictContainsSubset(expected, response.data) 138 139 @mock.patch('requests.post', mock_import_repository_task) 140 def test_release_rollback(self): 141 url = '/v1/apps' 142 response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 143 self.assertEqual(response.status_code, 201) 144 app_id = response.data['id'] 145 # try to rollback with only 1 release extant, expecting 400 146 url = "/v1/apps/{app_id}/releases/rollback/".format(**locals()) 147 response = self.client.post(url, content_type='application/json', 148 HTTP_AUTHORIZATION='token {}'.format(self.token)) 149 self.assertEqual(response.status_code, 400) 150 self.assertEqual(response.data, {'detail': 'version cannot be below 0'}) 151 self.assertEqual(response.get('content-type'), 'application/json') 152 # update config to roll a new release 153 url = '/v1/apps/{app_id}/config'.format(**locals()) 154 body = {'values': json.dumps({'NEW_URL1': 'http://localhost:8080/'})} 155 response = self.client.post( 156 url, json.dumps(body), content_type='application/json', 157 HTTP_AUTHORIZATION='token {}'.format(self.token)) 158 self.assertEqual(response.status_code, 201) 159 # update the build to roll a new release 160 url = '/v1/apps/{app_id}/builds'.format(**locals()) 161 body = {'image': 'autotest/example'} 162 response = self.client.post( 163 url, json.dumps(body), content_type='application/json', 164 HTTP_AUTHORIZATION='token {}'.format(self.token)) 165 self.assertEqual(response.status_code, 201) 166 # rollback and check to see that a 4th release was created 167 # with the build and config of release #2 168 url = "/v1/apps/{app_id}/releases/rollback/".format(**locals()) 169 response = self.client.post(url, content_type='application/json', 170 HTTP_AUTHORIZATION='token {}'.format(self.token)) 171 self.assertEqual(response.status_code, 201) 172 url = '/v1/apps/{app_id}/releases'.format(**locals()) 173 response = self.client.get(url, content_type='application/json', 174 HTTP_AUTHORIZATION='token {}'.format(self.token)) 175 self.assertEqual(response.status_code, 200) 176 self.assertEqual(response.data['count'], 4) 177 url = '/v1/apps/{app_id}/releases/v2'.format(**locals()) 178 response = self.client.get(url, content_type='application/json', 179 HTTP_AUTHORIZATION='token {}'.format(self.token)) 180 self.assertEqual(response.status_code, 200) 181 release2 = response.data 182 self.assertEquals(release2['version'], 2) 183 url = '/v1/apps/{app_id}/releases/v4'.format(**locals()) 184 response = self.client.get(url, content_type='application/json', 185 HTTP_AUTHORIZATION='token {}'.format(self.token)) 186 self.assertEqual(response.status_code, 200) 187 release4 = response.data 188 self.assertEquals(release4['version'], 4) 189 self.assertNotEqual(release2['uuid'], release4['uuid']) 190 self.assertEqual(release2['build'], release4['build']) 191 self.assertEqual(release2['config'], release4['config']) 192 # rollback explicitly to release #1 and check that a 5th release 193 # was created with the build and config of release #1 194 url = "/v1/apps/{app_id}/releases/rollback/".format(**locals()) 195 body = {'version': 1} 196 response = self.client.post( 197 url, json.dumps(body), content_type='application/json', 198 HTTP_AUTHORIZATION='token {}'.format(self.token)) 199 self.assertEqual(response.status_code, 201) 200 url = '/v1/apps/{app_id}/releases'.format(**locals()) 201 response = self.client.get(url, content_type='application/json', 202 HTTP_AUTHORIZATION='token {}'.format(self.token)) 203 self.assertEqual(response.status_code, 200) 204 self.assertEqual(response.data['count'], 5) 205 url = '/v1/apps/{app_id}/releases/v1'.format(**locals()) 206 response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 207 self.assertEqual(response.status_code, 200) 208 release1 = response.data 209 url = '/v1/apps/{app_id}/releases/v5'.format(**locals()) 210 response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 211 self.assertEqual(response.status_code, 200) 212 release5 = response.data 213 self.assertEqual(release5['version'], 5) 214 self.assertNotEqual(release1['uuid'], release5['uuid']) 215 self.assertEqual(release1['build'], release5['build']) 216 self.assertEqual(release1['config'], release5['config']) 217 # check to see that the current config is actually the initial one 218 url = "/v1/apps/{app_id}/config".format(**locals()) 219 response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 220 self.assertEqual(response.status_code, 200) 221 self.assertEqual(response.data['values'], {}) 222 # rollback to #3 and see that it has the correct config 223 url = "/v1/apps/{app_id}/releases/rollback/".format(**locals()) 224 body = {'version': 3} 225 response = self.client.post( 226 url, json.dumps(body), content_type='application/json', 227 HTTP_AUTHORIZATION='token {}'.format(self.token)) 228 self.assertEqual(response.status_code, 201) 229 url = "/v1/apps/{app_id}/config".format(**locals()) 230 response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 231 self.assertEqual(response.status_code, 200) 232 values = response.data['values'] 233 self.assertIn('NEW_URL1', values) 234 self.assertEqual('http://localhost:8080/', values['NEW_URL1']) 235 236 @mock.patch('requests.post', mock_import_repository_task) 237 def test_release_str(self): 238 """Test the text representation of a release.""" 239 release3 = self.test_release() 240 release = Release.objects.get(uuid=release3['uuid']) 241 self.assertEqual(str(release), "{}-v3".format(release3['app'])) 242 243 @mock.patch('requests.post', mock_import_repository_task) 244 def test_release_summary(self): 245 """Test the text summary of a release.""" 246 release3 = self.test_release() 247 release = Release.objects.get(uuid=release3['uuid']) 248 # check that the release has push and env change messages 249 self.assertIn('autotest deployed ', release.summary) 250 251 @mock.patch('requests.post', mock_import_repository_task) 252 def test_admin_can_create_release(self): 253 """If a non-user creates an app, an admin should be able to create releases.""" 254 user = User.objects.get(username='autotest2') 255 token = Token.objects.get(user=user).key 256 url = '/v1/apps' 257 response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(token)) 258 self.assertEqual(response.status_code, 201) 259 app_id = response.data['id'] 260 # check that updating config rolls a new release 261 url = '/v1/apps/{app_id}/config'.format(**locals()) 262 body = {'values': json.dumps({'NEW_URL1': 'http://localhost:8080/'})} 263 response = self.client.post( 264 url, json.dumps(body), content_type='application/json', 265 HTTP_AUTHORIZATION='token {}'.format(self.token)) 266 self.assertEqual(response.status_code, 201) 267 self.assertIn('NEW_URL1', response.data['values']) 268 # check to see that an initial release was created 269 url = '/v1/apps/{app_id}/releases'.format(**locals()) 270 response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 271 self.assertEqual(response.status_code, 200) 272 # account for the config release as well 273 self.assertEqual(response.data['count'], 2) 274 275 @mock.patch('requests.post', mock_import_repository_task) 276 def test_unauthorized_user_cannot_modify_release(self): 277 """ 278 An unauthorized user should not be able to modify other releases. 279 280 Since an unauthorized user should not know about the application at all, these 281 requests should return a 404. 282 """ 283 app_id = 'autotest' 284 base_url = '/v1/apps' 285 body = {'id': app_id} 286 response = self.client.post(base_url, json.dumps(body), content_type='application/json', 287 HTTP_AUTHORIZATION='token {}'.format(self.token)) 288 # push a new build 289 url = '{base_url}/{app_id}/builds'.format(**locals()) 290 body = {'image': 'test'} 291 response = self.client.post( 292 url, json.dumps(body), content_type='application/json', 293 HTTP_AUTHORIZATION='token {}'.format(self.token)) 294 # update config to roll a new release 295 url = '{base_url}/{app_id}/config'.format(**locals()) 296 body = {'values': json.dumps({'NEW_URL1': 'http://localhost:8080/'})} 297 response = self.client.post( 298 url, json.dumps(body), content_type='application/json', 299 HTTP_AUTHORIZATION='token {}'.format(self.token)) 300 unauthorized_user = User.objects.get(username='autotest2') 301 unauthorized_token = Token.objects.get(user=unauthorized_user).key 302 # try to rollback 303 url = '{base_url}/{app_id}/releases/rollback/'.format(**locals()) 304 response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(unauthorized_token)) 305 self.assertEqual(response.status_code, 403)