github.com/misfo/deis@v1.0.1-0.20141111224634-e0eee0392b8a/controller/api/tests/test_config.py (about) 1 # -*- coding: utf-8 -*- 2 """ 3 Unit tests for the Deis api app. 4 5 Run the tests with "./manage.py test api" 6 """ 7 8 from __future__ import unicode_literals 9 10 import json 11 import mock 12 import requests 13 14 from django.contrib.auth.models import User 15 from django.test import TransactionTestCase 16 from rest_framework.authtoken.models import Token 17 18 from api.models import Config 19 20 21 def mock_import_repository_task(*args, **kwargs): 22 resp = requests.Response() 23 resp.status_code = 200 24 resp._content_consumed = True 25 return resp 26 27 28 class ConfigTest(TransactionTestCase): 29 30 """Tests setting and updating config values""" 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 38 @mock.patch('requests.post', mock_import_repository_task) 39 def test_config(self): 40 """ 41 Test that config is auto-created for a new app and that 42 config can be updated using a PATCH 43 """ 44 url = '/v1/apps' 45 response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 46 self.assertEqual(response.status_code, 201) 47 app_id = response.data['id'] 48 # check to see that an initial/empty config was created 49 url = "/v1/apps/{app_id}/config".format(**locals()) 50 response = self.client.get(url, 51 HTTP_AUTHORIZATION='token {}'.format(self.token)) 52 self.assertEqual(response.status_code, 200) 53 self.assertIn('values', response.data) 54 self.assertEqual(response.data['values'], {}) 55 config1 = response.data 56 # set an initial config value 57 body = {'values': json.dumps({'NEW_URL1': 'http://localhost:8080/'})} 58 response = self.client.post(url, json.dumps(body), content_type='application/json', 59 HTTP_AUTHORIZATION='token {}'.format(self.token)) 60 self.assertEqual(response.status_code, 201) 61 config2 = response.data 62 self.assertNotEqual(config1['uuid'], config2['uuid']) 63 self.assertIn('NEW_URL1', response.data['values']) 64 # read the config 65 response = self.client.get(url, 66 HTTP_AUTHORIZATION='token {}'.format(self.token)) 67 self.assertEqual(response.status_code, 200) 68 config3 = response.data 69 self.assertEqual(config2, config3) 70 self.assertIn('NEW_URL1', response.data['values']) 71 # set an additional config value 72 body = {'values': json.dumps({'NEW_URL2': 'http://localhost:8080/'})} 73 response = self.client.post(url, json.dumps(body), content_type='application/json', 74 HTTP_AUTHORIZATION='token {}'.format(self.token)) 75 self.assertEqual(response.status_code, 201) 76 config3 = response.data 77 self.assertNotEqual(config2['uuid'], config3['uuid']) 78 self.assertIn('NEW_URL1', response.data['values']) 79 self.assertIn('NEW_URL2', response.data['values']) 80 # read the config again 81 response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 82 self.assertEqual(response.status_code, 200) 83 config4 = response.data 84 self.assertEqual(config3, config4) 85 self.assertIn('NEW_URL1', response.data['values']) 86 self.assertIn('NEW_URL2', response.data['values']) 87 # unset a config value 88 body = {'values': json.dumps({'NEW_URL2': None})} 89 response = self.client.post(url, json.dumps(body), content_type='application/json', 90 HTTP_AUTHORIZATION='token {}'.format(self.token)) 91 self.assertEqual(response.status_code, 201) 92 config5 = response.data 93 self.assertNotEqual(config4['uuid'], config5['uuid']) 94 self.assertNotIn('NEW_URL2', json.dumps(response.data['values'])) 95 # unset all config values 96 body = {'values': json.dumps({'NEW_URL1': None})} 97 response = self.client.post(url, json.dumps(body), content_type='application/json', 98 HTTP_AUTHORIZATION='token {}'.format(self.token)) 99 self.assertEqual(response.status_code, 201) 100 self.assertNotIn('NEW_URL1', json.dumps(response.data['values'])) 101 # disallow put/patch/delete 102 response = self.client.put(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 103 self.assertEqual(response.status_code, 405) 104 response = self.client.patch(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 105 self.assertEqual(response.status_code, 405) 106 response = self.client.delete(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 107 self.assertEqual(response.status_code, 405) 108 return config5 109 110 @mock.patch('requests.post', mock_import_repository_task) 111 def test_config_set_same_key(self): 112 """ 113 Test that config sets on the same key function properly 114 """ 115 url = '/v1/apps' 116 response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 117 self.assertEqual(response.status_code, 201) 118 app_id = response.data['id'] 119 url = "/v1/apps/{app_id}/config".format(**locals()) 120 # set an initial config value 121 body = {'values': json.dumps({'PORT': '5000'})} 122 response = self.client.post(url, json.dumps(body), content_type='application/json', 123 HTTP_AUTHORIZATION='token {}'.format(self.token)) 124 self.assertEqual(response.status_code, 201) 125 self.assertIn('PORT', response.data['values']) 126 # reset same config value 127 body = {'values': json.dumps({'PORT': '5001'})} 128 response = self.client.post(url, json.dumps(body), content_type='application/json', 129 HTTP_AUTHORIZATION='token {}'.format(self.token)) 130 self.assertEqual(response.status_code, 201) 131 self.assertIn('PORT', response.data['values']) 132 self.assertEqual(response.data['values']['PORT'], '5001') 133 134 @mock.patch('requests.post', mock_import_repository_task) 135 def test_config_set_unicode(self): 136 """ 137 Test that config sets with unicode values are accepted. 138 """ 139 url = '/v1/apps' 140 response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 141 self.assertEqual(response.status_code, 201) 142 app_id = response.data['id'] 143 url = "/v1/apps/{app_id}/config".format(**locals()) 144 # set an initial config value 145 body = {'values': json.dumps({'POWERED_BY': 'Деис'})} 146 response = self.client.post(url, json.dumps(body), content_type='application/json', 147 HTTP_AUTHORIZATION='token {}'.format(self.token)) 148 self.assertEqual(response.status_code, 201) 149 self.assertIn('POWERED_BY', response.data['values']) 150 # reset same config value 151 body = {'values': json.dumps({'POWERED_BY': 'Кроликов'})} 152 response = self.client.post(url, json.dumps(body), content_type='application/json', 153 HTTP_AUTHORIZATION='token {}'.format(self.token)) 154 self.assertEqual(response.status_code, 201) 155 self.assertIn('POWERED_BY', response.data['values']) 156 self.assertEqual(response.data['values']['POWERED_BY'], 'Кроликов') 157 # set an integer to test unicode regression 158 body = {'values': json.dumps({'INTEGER': 1})} 159 response = self.client.post(url, json.dumps(body), content_type='application/json', 160 HTTP_AUTHORIZATION='token {}'.format(self.token)) 161 self.assertEqual(response.status_code, 201) 162 self.assertIn('INTEGER', response.data['values']) 163 self.assertEqual(response.data['values']['INTEGER'], 1) 164 165 @mock.patch('requests.post', mock_import_repository_task) 166 def test_config_str(self): 167 """Test the text representation of a node.""" 168 config5 = self.test_config() 169 config = Config.objects.get(uuid=config5['uuid']) 170 self.assertEqual(str(config), "{}-{}".format(config5['app'], config5['uuid'][:7])) 171 172 @mock.patch('requests.post', mock_import_repository_task) 173 def test_admin_can_create_config_on_other_apps(self): 174 """If a non-admin creates an app, an administrator should be able to set config 175 values for that app. 176 """ 177 user = User.objects.get(username='autotest2') 178 token = Token.objects.get(user=user).key 179 url = '/v1/apps' 180 response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(token)) 181 self.assertEqual(response.status_code, 201) 182 app_id = response.data['id'] 183 url = "/v1/apps/{app_id}/config".format(**locals()) 184 # set an initial config value 185 body = {'values': json.dumps({'PORT': '5000'})} 186 response = self.client.post(url, json.dumps(body), content_type='application/json', 187 HTTP_AUTHORIZATION='token {}'.format(self.token)) 188 self.assertEqual(response.status_code, 201) 189 self.assertIn('PORT', response.data['values']) 190 191 @mock.patch('requests.post', mock_import_repository_task) 192 def test_limit_memory(self): 193 """ 194 Test that limit is auto-created for a new app and that 195 limits can be updated using a PATCH 196 """ 197 url = '/v1/apps' 198 response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 199 self.assertEqual(response.status_code, 201) 200 app_id = response.data['id'] 201 url = '/v1/apps/{app_id}/config'.format(**locals()) 202 # check default limit 203 response = self.client.get(url, content_type='application/json', 204 HTTP_AUTHORIZATION='token {}'.format(self.token)) 205 self.assertEqual(response.status_code, 200) 206 self.assertIn('memory', response.data) 207 self.assertEqual(response.data['memory'], {}) 208 # regression test for https://github.com/deis/deis/issues/1563 209 self.assertNotIn('"', response.data['memory']) 210 # set an initial limit 211 mem = {'web': '1G'} 212 body = {'memory': json.dumps(mem)} 213 response = self.client.post(url, json.dumps(body), content_type='application/json', 214 HTTP_AUTHORIZATION='token {}'.format(self.token)) 215 self.assertEqual(response.status_code, 201) 216 limit1 = response.data 217 # check memory limits 218 response = self.client.get(url, content_type='application/json', 219 HTTP_AUTHORIZATION='token {}'.format(self.token)) 220 self.assertEqual(response.status_code, 200) 221 self.assertIn('memory', response.data) 222 memory = response.data['memory'] 223 self.assertIn('web', memory) 224 self.assertEqual(memory['web'], '1G') 225 # set an additional value 226 body = {'memory': json.dumps({'worker': '512M'})} 227 response = self.client.post(url, json.dumps(body), content_type='application/json', 228 HTTP_AUTHORIZATION='token {}'.format(self.token)) 229 self.assertEqual(response.status_code, 201) 230 limit2 = response.data 231 self.assertNotEqual(limit1['uuid'], limit2['uuid']) 232 memory = response.data['memory'] 233 self.assertIn('worker', memory) 234 self.assertEqual(memory['worker'], '512M') 235 self.assertIn('web', memory) 236 self.assertEqual(memory['web'], '1G') 237 # read the limit again 238 response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 239 self.assertEqual(response.status_code, 200) 240 limit3 = response.data 241 self.assertEqual(limit2, limit3) 242 memory = response.data['memory'] 243 self.assertIn('worker', memory) 244 self.assertEqual(memory['worker'], '512M') 245 self.assertIn('web', memory) 246 self.assertEqual(memory['web'], '1G') 247 # regression test for https://github.com/deis/deis/issues/1613 248 # ensure that config:set doesn't wipe out previous limits 249 body = {'values': json.dumps({'NEW_URL2': 'http://localhost:8080/'})} 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, 201) 253 self.assertIn('NEW_URL2', response.data['values']) 254 # read the limit again 255 response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 256 self.assertEqual(response.status_code, 200) 257 memory = response.data['memory'] 258 self.assertIn('worker', memory) 259 self.assertEqual(memory['worker'], '512M') 260 self.assertIn('web', memory) 261 self.assertEqual(memory['web'], '1G') 262 # unset a value 263 body = {'memory': json.dumps({'worker': None})} 264 response = self.client.post(url, json.dumps(body), content_type='application/json', 265 HTTP_AUTHORIZATION='token {}'.format(self.token)) 266 self.assertEqual(response.status_code, 201) 267 limit4 = response.data 268 self.assertNotEqual(limit3['uuid'], limit4['uuid']) 269 self.assertNotIn('worker', json.dumps(response.data['memory'])) 270 # disallow put/patch/delete 271 response = self.client.put(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 272 self.assertEqual(response.status_code, 405) 273 response = self.client.patch(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 274 self.assertEqual(response.status_code, 405) 275 response = self.client.delete(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 276 self.assertEqual(response.status_code, 405) 277 return limit4 278 279 @mock.patch('requests.post', mock_import_repository_task) 280 def test_limit_cpu(self): 281 """ 282 Test that CPU limits can be set 283 """ 284 url = '/v1/apps' 285 response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 286 self.assertEqual(response.status_code, 201) 287 app_id = response.data['id'] 288 url = '/v1/apps/{app_id}/config'.format(**locals()) 289 # check default limit 290 response = self.client.get(url, content_type='application/json', 291 HTTP_AUTHORIZATION='token {}'.format(self.token)) 292 self.assertEqual(response.status_code, 200) 293 self.assertIn('cpu', response.data) 294 self.assertEqual(response.data['cpu'], {}) 295 # regression test for https://github.com/deis/deis/issues/1563 296 self.assertNotIn('"', response.data['cpu']) 297 # set an initial limit 298 body = {'cpu': json.dumps({'web': '1024'})} 299 response = self.client.post(url, json.dumps(body), content_type='application/json', 300 HTTP_AUTHORIZATION='token {}'.format(self.token)) 301 self.assertEqual(response.status_code, 201) 302 limit1 = response.data 303 # check memory limits 304 response = self.client.get(url, content_type='application/json', 305 HTTP_AUTHORIZATION='token {}'.format(self.token)) 306 self.assertEqual(response.status_code, 200) 307 self.assertIn('cpu', response.data) 308 cpu = response.data['cpu'] 309 self.assertIn('web', cpu) 310 self.assertEqual(cpu['web'], '1024') 311 # set an additional value 312 body = {'cpu': json.dumps({'worker': '512'})} 313 response = self.client.post(url, json.dumps(body), content_type='application/json', 314 HTTP_AUTHORIZATION='token {}'.format(self.token)) 315 self.assertEqual(response.status_code, 201) 316 limit2 = response.data 317 self.assertNotEqual(limit1['uuid'], limit2['uuid']) 318 cpu = response.data['cpu'] 319 self.assertIn('worker', cpu) 320 self.assertEqual(cpu['worker'], '512') 321 self.assertIn('web', cpu) 322 self.assertEqual(cpu['web'], '1024') 323 # read the limit again 324 response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 325 self.assertEqual(response.status_code, 200) 326 limit3 = response.data 327 self.assertEqual(limit2, limit3) 328 cpu = response.data['cpu'] 329 self.assertIn('worker', cpu) 330 self.assertEqual(cpu['worker'], '512') 331 self.assertIn('web', cpu) 332 self.assertEqual(cpu['web'], '1024') 333 # unset a value 334 body = {'memory': json.dumps({'worker': None})} 335 response = self.client.post(url, json.dumps(body), content_type='application/json', 336 HTTP_AUTHORIZATION='token {}'.format(self.token)) 337 self.assertEqual(response.status_code, 201) 338 limit4 = response.data 339 self.assertNotEqual(limit3['uuid'], limit4['uuid']) 340 self.assertNotIn('worker', json.dumps(response.data['memory'])) 341 # disallow put/patch/delete 342 response = self.client.put(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 343 self.assertEqual(response.status_code, 405) 344 response = self.client.patch(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 345 self.assertEqual(response.status_code, 405) 346 response = self.client.delete(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 347 self.assertEqual(response.status_code, 405) 348 return limit4 349 350 @mock.patch('requests.post', mock_import_repository_task) 351 def test_tags(self): 352 """ 353 Test that tags can be set on an application 354 """ 355 url = '/v1/apps' 356 response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 357 self.assertEqual(response.status_code, 201) 358 app_id = response.data['id'] 359 url = '/v1/apps/{app_id}/config'.format(**locals()) 360 # check default 361 response = self.client.get(url, content_type='application/json', 362 HTTP_AUTHORIZATION='token {}'.format(self.token)) 363 self.assertEqual(response.status_code, 200) 364 self.assertIn('tags', response.data) 365 self.assertEqual(response.data['tags'], {}) 366 # set some tags 367 body = {'tags': json.dumps({'environ': 'dev'})} 368 response = self.client.post(url, json.dumps(body), content_type='application/json', 369 HTTP_AUTHORIZATION='token {}'.format(self.token)) 370 self.assertEqual(response.status_code, 201) 371 tags1 = response.data 372 # check tags again 373 response = self.client.get(url, content_type='application/json', 374 HTTP_AUTHORIZATION='token {}'.format(self.token)) 375 self.assertEqual(response.status_code, 200) 376 self.assertIn('tags', response.data) 377 tags = response.data['tags'] 378 self.assertIn('environ', tags) 379 self.assertEqual(tags['environ'], 'dev') 380 # set an additional value 381 body = {'tags': json.dumps({'rack': '1'})} 382 response = self.client.post(url, json.dumps(body), content_type='application/json', 383 HTTP_AUTHORIZATION='token {}'.format(self.token)) 384 self.assertEqual(response.status_code, 201) 385 tags2 = response.data 386 self.assertNotEqual(tags1['uuid'], tags2['uuid']) 387 tags = response.data['tags'] 388 self.assertIn('rack', tags) 389 self.assertEqual(tags['rack'], '1') 390 self.assertIn('environ', tags) 391 self.assertEqual(tags['environ'], 'dev') 392 # read the limit again 393 response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 394 self.assertEqual(response.status_code, 200) 395 tags3 = response.data 396 self.assertEqual(tags2, tags3) 397 tags = response.data['tags'] 398 self.assertIn('rack', tags) 399 self.assertEqual(tags['rack'], '1') 400 self.assertIn('environ', tags) 401 self.assertEqual(tags['environ'], 'dev') 402 # unset a value 403 body = {'tags': json.dumps({'rack': None})} 404 response = self.client.post(url, json.dumps(body), content_type='application/json', 405 HTTP_AUTHORIZATION='token {}'.format(self.token)) 406 self.assertEqual(response.status_code, 201) 407 tags4 = response.data 408 self.assertNotEqual(tags3['uuid'], tags4['uuid']) 409 self.assertNotIn('rack', json.dumps(response.data['tags'])) 410 # set invalid values 411 body = {'tags': json.dumps({'valid': 'in\nvalid'})} 412 response = self.client.post(url, json.dumps(body), content_type='application/json', 413 HTTP_AUTHORIZATION='token {}'.format(self.token)) 414 self.assertEqual(response.status_code, 400) 415 body = {'tags': json.dumps({'in.valid': 'valid'})} 416 response = self.client.post(url, json.dumps(body), content_type='application/json', 417 HTTP_AUTHORIZATION='token {}'.format(self.token)) 418 self.assertEqual(response.status_code, 400) 419 # disallow put/patch/delete 420 response = self.client.put(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 421 self.assertEqual(response.status_code, 405) 422 response = self.client.patch(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 423 self.assertEqual(response.status_code, 405) 424 response = self.client.delete(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 425 self.assertEqual(response.status_code, 405)