~bac/charmworld/tag-constraints

« back to all changes in this revision

Viewing changes to charmworld/views/tests/test_proof.py

  • Committer: Matthew Scott
  • Date: 2014-03-06 20:32:51 UTC
  • mfrom: (489 trunk)
  • mto: This revision was merged to the branch mainline in revision 490.
  • Revision ID: matthew.scott@canonical.com-20140306203251-ve388tpu1av0coqz
Merge with trunk to resolve conflicts.

Show diffs side-by-side

added added

removed removed

Lines of Context:
11
11
class TestBundleProof:
12
12
    """Verify that a bundle can be proofed in charmworld."""
13
13
 
 
14
    def setup_charm(self, description=''):
 
15
        # Create a charm for use in the test.  Return it as a Charm object.
 
16
        _id, charm = factory.makeCharm(
 
17
            self.db,
 
18
            description='',
 
19
        )
 
20
        return Charm(charm)
 
21
 
14
22
    def setup_relatable_charms(self, second_requires, second_provides):
15
23
        # Unless otherwise specified, the generated charm provides
16
24
        # website:https and requires database:mongodb.
39
47
        request = self.make_request('bundle', remainder='/proof')
40
48
        response = self.api_class(request)()
41
49
        self.assertEqual(400, response.status_code)
 
50
        results = response.json_body
 
51
        self.assertEqual(0, len(results['warning_messages']))
42
52
        self.assertEqual(
43
 
            'No deployer file data received.',
44
 
            response.json_body['error_messages'][0])
 
53
            ['No deployer file data received.'],
 
54
            results['error_messages'])
45
55
 
46
56
    def test_bundle_proof_invalid_deployer_file(self):
47
57
        request = self.make_request('bundle', remainder='/proof')
48
58
        request.params = {
49
 
            'deployer_file': '{'
 
59
            'deployer_file': '{',
50
60
        }
51
61
        response = self.api_class(request)()
52
62
        self.assertEqual(400, response.status_code)
 
63
        results = response.json_body
 
64
        self.assertEqual(1, len(results['error_messages']))
 
65
        self.assertEqual(0, len(results['warning_messages']))
53
66
        self.assertIn(
54
 
            'Could not parse',
55
 
            response.json_body['error_messages'][0])
 
67
            'Could not parse', results['error_messages'][0])
56
68
 
57
69
    def test_bundle_proof_charm_exists(self):
58
70
        deployer_file = load_data_file('sample_bundle/bundles.yaml')
59
71
 
60
72
        request = self.make_request('bundle', remainder='/proof')
61
73
        request.params = {
62
 
            'deployer_file': deployer_file
 
74
            'deployer_file': deployer_file,
63
75
        }
64
76
 
65
77
        response = self.api_class(request)()
66
78
        self.assertEqual(200, response.status_code)
67
79
        results = response.json_body
68
80
        self.assertEqual(1, len(results['errors']))
 
81
        self.assertEqual(0, len(results['warning_messages']))
69
82
        self.assertEqual(4, len(results['errors'][0]['services'].keys()))
70
83
        self.assertIn(
71
84
            'Could not find ',
77
90
            results['errors'][0]['services']['db'][0].keys())
78
91
 
79
92
    def test_bundle_proof_valid(self):
80
 
        # We have to create the charm so that we can verify it exists.
81
 
        _id, charm = factory.makeCharm(
82
 
            self.db,
83
 
            description=''
84
 
        )
85
 
 
86
 
        # and we'll cheat and just find it straight by the store url.
87
 
        charm = Charm(charm)
88
 
        store_url = charm.store_url
 
93
        charm = self.setup_charm()
89
94
 
90
95
        bundle_config = {
91
96
            'wiki': {
92
97
                'services': {
93
98
                    'charm': {
94
 
                        'charm_url': str(store_url)
 
99
                        'charm_url': str(charm.store_url),
95
100
                    }
96
101
                }
97
102
            }
99
104
 
100
105
        request = self.make_request('bundle', remainder='/proof')
101
106
        request.params = {
102
 
            'deployer_file': yaml.dump(bundle_config)
 
107
            'deployer_file': yaml.dump(bundle_config),
103
108
        }
104
109
        response = self.api_class(request)()
105
110
        self.assertEqual(200, response.status_code)
106
111
        results = response.json_body
107
112
        self.assertEqual(0, len(results['errors']))
108
113
        self.assertEqual(1, len(results['services_info'].items()))
109
 
        self.assertEqual(store_url,
 
114
        self.assertEqual(charm.store_url,
110
115
                         results['services_info']['charm']['store_url'])
111
116
 
112
117
    def test_bundle_proof_invalid_config_type(self):
113
 
        # We have to create the charm so that we can verify it exists.
114
 
        _id, charm = factory.makeCharm(
115
 
            self.db,
116
 
            description=''
117
 
        )
118
 
 
119
 
        # and we'll cheat and just find it straight by the store url.
120
 
        charm = Charm(charm)
121
 
        store_url = charm.store_url
 
118
        charm = self.setup_charm()
122
119
 
123
120
        bundle_config = {
124
121
            'wiki': {
125
122
                'services': {
126
123
                    'charm': {
127
 
                        'charm_url': str(store_url),
 
124
                        'charm_url': str(charm.store_url),
128
125
                        'options': {
129
 
                            'script-interval': 'invalid'
 
126
                            'script-interval': 'invalid',
130
127
                        }
131
128
                    }
132
129
                }
135
132
 
136
133
        request = self.make_request('bundle', remainder='/proof')
137
134
        request.params = {
138
 
            'deployer_file': yaml.dump(bundle_config)
 
135
            'deployer_file': yaml.dump(bundle_config),
139
136
        }
140
137
        response = self.api_class(request)()
141
138
        self.assertEqual(200, response.status_code)
142
139
        results = response.json_body
143
140
        self.assertEqual(1, len(results['errors']))
144
141
        self.assertEqual(1, len(results['error_messages']))
 
142
        self.assertEqual(0, len(results['warning_messages']))
145
143
 
146
144
        # Note that the View will prefix the error message from the proof with
147
145
        # the bundle name to help identify where the error message came from.
148
146
        self.assertEqual(
149
 
            'wiki: script-interval is not of type int.',
150
 
            results['error_messages'][0]
151
 
        )
 
147
            ['wiki/charm: script-interval is not of type int.'],
 
148
            results['error_messages'])
152
149
 
153
150
    def test_bundle_proof_supports_multiple_errors(self):
154
 
        _id, charm = factory.makeCharm(
155
 
            self.db,
156
 
            description=''
157
 
        )
158
 
 
159
 
        # and we'll cheat and just find it straight by the store url.
160
 
        charm = Charm(charm)
161
 
        store_url = charm.store_url
 
151
        charm = self.setup_charm()
162
152
 
163
153
        bundle_config = {
164
154
            'wiki': {
165
155
                'services': {
166
156
                    'charm': {
167
 
                        'charm_url': str(store_url),
 
157
                        'charm_url': str(charm.store_url),
168
158
                        'options': {
169
159
                            'script-interval': 'invalid',
170
160
                            'no-exist': 'hah invalid',
179
169
 
180
170
        request = self.make_request('bundle', remainder='/proof')
181
171
        request.params = {
182
 
            'deployer_file': yaml.dump(bundle_config)
 
172
            'deployer_file': yaml.dump(bundle_config),
183
173
        }
184
174
        response = self.api_class(request)()
185
175
        self.assertEqual(200, response.status_code)
186
176
        results = response.json_body
187
177
        self.assertEqual(1, len(results['errors']))
188
178
        self.assertEqual(3, len(results['error_messages']))
 
179
        self.assertEqual(0, len(results['warning_messages']))
189
180
 
190
181
        # One message from not finding a charm.
191
182
        self.assertEqual(
192
 
            'wiki: Could not find charm for service: fail',
193
 
            results['error_messages'][0]
194
 
        )
195
 
        # The other two from checking the config of the other charm.
196
 
        self.assertEqual(
197
 
            'wiki: script-interval is not of type int.',
198
 
            results['error_messages'][1]
199
 
        )
200
 
        self.assertEqual(
201
 
            'wiki: The charm has no option for: no-exist',
202
 
            results['error_messages'][2]
203
 
        )
 
183
            ['wiki/fail: Could not find charm for service.',
 
184
             'wiki/charm: script-interval is not of type int.',
 
185
             'wiki/charm: The charm has no option for: no-exist'],
 
186
            results['error_messages'])
204
187
 
205
188
    def test_bundle_proof_errors_on_invalid_relations(self):
206
189
        # The default charm provides website:https and requires
208
191
        one_store_url, two_store_url = self.setup_relatable_charms(
209
192
            {
210
193
                'proxy': {
211
 
                    'interface': 'http'
 
194
                    'interface': 'http',
212
195
                }
213
196
            },
214
197
            {
215
198
                'cache': {
216
 
                    'interface': 'memcache'
 
199
                    'interface': 'memcache',
217
200
                },
218
201
                'db': {
219
 
                    'interface': 'mongodb'
 
202
                    'interface': 'mongodb',
220
203
                }
221
 
            }
 
204
            },
222
205
        )
223
206
 
224
 
        bundle_base = """
225
 
wiki:
226
 
  series: precise
227
 
  promulgated: True
228
 
  services:
229
 
    wiki:
230
 
      charm: {}
231
 
    db:
232
 
      charm: {}
233
 
  relations:
234
 
    - ["wiki:proxy", db]""".format(one_store_url, two_store_url)
 
207
        bundle_base = dedent("""\
 
208
            wiki:
 
209
              series: precise
 
210
              promulgated: True
 
211
              services:
 
212
                wiki:
 
213
                  charm: {}
 
214
                db:
 
215
                  charm: {}
 
216
              relations:
 
217
                - ["wiki:proxy", db]""").format(one_store_url, two_store_url)
235
218
 
236
219
        request = self.make_request('bundle', remainder='/proof')
237
220
        request.params = {
238
 
            'deployer_file': bundle_base
 
221
            'deployer_file': bundle_base,
239
222
        }
240
223
        response = self.api_class(request)()
241
224
        self.assertEqual(200, response.status_code)
 
225
        results = response.json_body
 
226
        self.assertEqual(1, len(results['error_messages']))
 
227
        self.assertEqual(0, len(results['warning_messages']))
242
228
        self.assertIn(
243
229
            'wiki: Invalid relation requested: proxy',
244
230
            response.json_body['error_messages'][0])
247
233
        one_store_url, two_store_url = self.setup_relatable_charms(
248
234
            {
249
235
                'proxy': {
250
 
                    'interface': 'http'
 
236
                    'interface': 'http',
251
237
                }
252
238
            }, {
253
239
                'cache': {
256
242
                'db': {
257
243
                    'interface': 'mongodb'
258
244
                }
259
 
            }
 
245
            },
260
246
        )
261
247
 
262
 
        bundle_base = """
263
 
wiki:
264
 
  series: precise
265
 
  promulgated: True
266
 
  services:
267
 
    wiki:
268
 
      charm: {}
269
 
    db:
270
 
      charm: {}
271
 
  relations:
272
 
    - ["wiki:db", "db:proxy"]""".format(one_store_url, two_store_url)
 
248
        bundle_base = dedent("""\
 
249
            wiki:
 
250
              series: precise
 
251
              promulgated: True
 
252
              services:
 
253
                wiki:
 
254
                  charm: {}
 
255
                db:
 
256
                  charm: {}
 
257
              relations:
 
258
                - ["wiki:db", "db:proxy"]""").format(
 
259
            one_store_url, two_store_url)
273
260
 
274
261
        request = self.make_request('bundle', remainder='/proof')
275
262
        request.params = {
276
 
            'deployer_file': bundle_base
 
263
            'deployer_file': bundle_base,
277
264
        }
278
265
        response = self.api_class(request)()
279
266
        self.assertEqual(200, response.status_code)
 
267
        results = response.json_body
 
268
        self.assertEqual(1, len(results['error_messages']))
 
269
        self.assertEqual(0, len(results['warning_messages']))
280
270
        self.assertIn(
281
271
            'wiki: The requested relation db to proxy is incompatible',
282
272
            response.json_body['error_messages'][0])
302
292
            """.format(one_store_url, two_store_url))
303
293
 
304
294
        request = self.make_request('bundle', remainder='/proof')
305
 
        request.params = {
306
 
            'deployer_file': bundle_base
307
 
        }
 
295
        request.params = {'deployer_file': bundle_base}
308
296
        response = self.api_class(request)()
309
297
        self.assertEqual(200, response.status_code)
310
 
        error_messages = response.json_body['error_messages']
311
 
        self.assertEqual(1, len(error_messages))
 
298
        results = response.json_body
 
299
        self.assertEqual(1, len(results['error_messages']))
 
300
        self.assertEqual(0, len(results['warning_messages']))
312
301
        self.assertIn(
313
302
            'wiki: The requested relation db to database is incompatible',
314
 
            error_messages[0])
 
303
            results['error_messages'][0])
315
304
        self.assertIn(
316
305
            'Hint: if order of the relation were reversed it would be valid.',
317
 
            error_messages[0])
 
306
            results['error_messages'][0])
318
307
 
319
308
    def test_bundle_proof_passes_with_valid_relations(self):
320
309
        one_store_url, two_store_url = self.setup_relatable_charms(
321
310
            {
322
311
                'proxy': {
323
 
                    'interface': 'https'
 
312
                    'interface': 'https',
324
313
                }
325
314
            }, {
326
315
                'cache': {
332
321
            }
333
322
        )
334
323
 
335
 
        bundle_base = """
336
 
wiki:
337
 
  series: precise
338
 
  promulgated: True
339
 
  services:
340
 
    wiki:
341
 
      charm: {}
342
 
    db:
343
 
      charm: {}
344
 
  relations:
345
 
    - ["wiki:database", "db:db"]""".format(one_store_url, two_store_url)
 
324
        bundle_base = dedent("""\
 
325
            wiki:
 
326
              series: precise
 
327
              promulgated: True
 
328
              services:
 
329
                wiki:
 
330
                  charm: {}
 
331
                db:
 
332
                  charm: {}
 
333
              relations:
 
334
                - ["wiki:database", "db:db"]""").format(
 
335
            one_store_url, two_store_url)
346
336
 
347
337
        request = self.make_request('bundle', remainder='/proof')
348
338
        request.params = {
349
 
            'deployer_file': bundle_base
 
339
            'deployer_file': bundle_base,
350
340
        }
351
341
        response = self.api_class(request)()
 
342
        results = response.json_body
 
343
        self.assertEqual(0, len(results['error_messages']))
 
344
        self.assertEqual(0, len(results['warning_messages']))
352
345
        self.assertEqual(200, response.status_code)
353
 
        self.assertEqual(0, len(response.json_body['error_messages']))
354
346
 
355
347
    def test_bundle_proof_passes_with_valid_vague_as_can_be(self):
356
348
        one_store_url, two_store_url = self.setup_relatable_charms(
357
349
            {
358
350
                'proxy': {
359
 
                    'interface': 'https'
 
351
                    'interface': 'https',
360
352
                }
361
353
            }, {
362
354
                'cache': {
365
357
                'db': {
366
358
                    'interface': 'mongodb'
367
359
                }
368
 
            }
 
360
            },
369
361
        )
370
362
 
371
363
        bundle_base = dedent("""
372
 
        wiki:
373
 
          series: precise
374
 
          promulgated: True
375
 
          services:
376
364
            wiki:
377
 
              charm: {}
378
 
            db:
379
 
              charm: {}
380
 
          relations:
381
 
            - ["wiki", "db"]""").format(one_store_url, two_store_url)
 
365
              series: precise
 
366
              promulgated: True
 
367
              services:
 
368
                wiki:
 
369
                  charm: {}
 
370
                db:
 
371
                  charm: {}
 
372
              relations:
 
373
                - ["wiki", "db"]""").format(one_store_url, two_store_url)
382
374
 
383
375
        request = self.make_request('bundle', remainder='/proof')
384
376
        request.params = {
385
 
            'deployer_file': bundle_base
 
377
            'deployer_file': bundle_base,
386
378
        }
387
379
        response = self.api_class(request)()
 
380
        results = response.json_body
 
381
        self.assertEqual(0, len(results['error_messages']))
 
382
        self.assertEqual(0, len(results['warning_messages']))
388
383
        self.assertEqual(200, response.status_code)
389
 
        self.assertEqual(0, len(response.json_body['error_messages']))
390
384
 
391
385
    def test_bundle_proof_errors_with_missing_service(self):
392
386
        one_store_url, two_store_url = self.setup_relatable_charms(
393
387
            {
394
388
                'proxy': {
395
 
                    'interface': 'https'
 
389
                    'interface': 'https',
396
390
                }
397
391
            }, {
398
392
                'cache': {
401
395
                'db': {
402
396
                    'interface': 'mongodb'
403
397
                }
404
 
            }
 
398
            },
405
399
        )
406
400
 
407
401
        bundle_base = dedent("""
408
 
        wiki:
409
 
          series: precise
410
 
          promulgated: True
411
 
          services:
412
402
            wiki:
413
 
              charm: {}
414
 
            db:
415
 
              charm: {}
416
 
          relations:
417
 
            - ["wiki", "party"]""").format(one_store_url, two_store_url)
 
403
              series: precise
 
404
              promulgated: True
 
405
              services:
 
406
                wiki:
 
407
                  charm: {}
 
408
                db:
 
409
                  charm: {}
 
410
              relations:
 
411
                - ["wiki", "party"]""").format(one_store_url, two_store_url)
418
412
 
419
413
        request = self.make_request('bundle', remainder='/proof')
420
414
        request.params = {'deployer_file': bundle_base}
421
415
        response = self.api_class(request)()
 
416
        results = response.json_body
 
417
        self.assertEqual(1, len(results['error_messages']))
 
418
        self.assertEqual(0, len(results['warning_messages']))
422
419
        self.assertEqual(200, response.status_code)
423
420
        self.assertEqual(
424
421
            'wiki: Invalid relation to charm: party not found.',
425
 
            response.json_body['error_messages'][0])
 
422
            results['error_messages'][0])
426
423
 
427
424
    def test_bundle_proof_with_inheritance(self):
428
425
        # Show that inheritance works by relating a charm in wiki-ext to one
458
455
        url3 = charm3.store_url
459
456
 
460
457
        bundle_base = dedent("""
461
 
        wiki:
462
 
          series: precise
463
 
          promulgated: True
464
 
          services:
465
458
            wiki:
466
 
              charm: {}
467
 
            db:
468
 
              charm: {}
469
 
          relations:
470
 
            - ["wiki", "db"]
471
 
 
472
 
        wiki-ext:
473
 
          inherits: wiki
474
 
          services:
475
 
            log:
476
 
              charm: {}
477
 
          relations:
478
 
            - ["wiki", "log"]
479
 
            - ["log", "db"]""").format(url1, url2, url3)
480
 
 
481
 
        request = self.make_request('bundle', remainder='/proof')
482
 
        request.params = {
483
 
            'deployer_file': bundle_base
484
 
        }
485
 
        response = self.api_class(request)()
486
 
        self.assertEqual(200, response.status_code)
487
 
        self.assertEqual(0, len(response.json_body['error_messages']))
 
459
              series: precise
 
460
              promulgated: True
 
461
              services:
 
462
                wiki:
 
463
                  charm: {}
 
464
                db:
 
465
                  charm: {}
 
466
              relations:
 
467
                - ["wiki", "db"]
 
468
 
 
469
            wiki-ext:
 
470
              inherits: wiki
 
471
              services:
 
472
                log:
 
473
                  charm: {}
 
474
              relations:
 
475
                - ["wiki", "log"]
 
476
                - ["log", "db"]""").format(url1, url2, url3)
 
477
 
 
478
        request = self.make_request('bundle', remainder='/proof')
 
479
        request.params = {
 
480
            'deployer_file': bundle_base,
 
481
        }
 
482
        response = self.api_class(request)()
 
483
        results = response.json_body
 
484
        self.assertEqual(0, len(results['error_messages']))
 
485
        self.assertEqual(0, len(results['warning_messages']))
 
486
        self.assertEqual(200, response.status_code)
 
487
 
 
488
    def test_bundle_proof_invalid_constraints(self):
 
489
        charm = self.setup_charm()
 
490
 
 
491
        bundle_config = {
 
492
            'wiki': {
 
493
                'services': {
 
494
                    'charm': {
 
495
                        'charm_url': str(charm.store_url),
 
496
                        'constraints': 'cows=1 cpu-donkeys=4',
 
497
                    }
 
498
                }
 
499
            }
 
500
        }
 
501
 
 
502
        request = self.make_request('bundle', remainder='/proof')
 
503
        request.params = {
 
504
            'deployer_file': yaml.dump(bundle_config),
 
505
        }
 
506
        response = self.api_class(request)()
 
507
        self.assertEqual(200, response.status_code)
 
508
        results = response.json_body
 
509
        self.assertEqual(1, len(results['errors']))
 
510
        self.assertEqual(1, len(results['error_messages']))
 
511
        self.assertEqual(0, len(results['warning_messages']))
 
512
        self.assertEqual(
 
513
            ['wiki/charm: unsupported constraints: cows, cpu-donkeys'],
 
514
            results['error_messages'])
 
515
 
 
516
    def test_bundle_proof_comma_separated_constraints(self):
 
517
        charm = self.setup_charm()
 
518
 
 
519
        bundle_config = {
 
520
            'wiki': {
 
521
                'services': {
 
522
                    'charm': {
 
523
                        'charm_url': str(charm.store_url),
 
524
                        'constraints': 'mem=1G,cpu-cores=4',
 
525
                    }
 
526
                }
 
527
            }
 
528
        }
 
529
 
 
530
        request = self.make_request('bundle', remainder='/proof')
 
531
        request.params = {
 
532
            'deployer_file': yaml.dump(bundle_config),
 
533
        }
 
534
        response = self.api_class(request)()
 
535
        self.assertEqual(200, response.status_code)
 
536
        results = response.json_body
 
537
        self.assertEqual(1, len(results['errors']))
 
538
        self.assertEqual(0, len(results['error_messages']))
 
539
        self.assertEqual(1, len(results['warning_messages']))
 
540
        expected = ("wiki/charm: The constraints are comma-separated, "
 
541
                    "which is deprecated.")
 
542
        self.assertEqual([expected], results['warning_messages'])
 
543
 
 
544
    def test_bundle_proof_multi_error_constraints(self):
 
545
        # If there is a failure due to invalid constraints and comma-separated,
 
546
        # we only report the former.
 
547
        charm = self.setup_charm()
 
548
 
 
549
        bundle_config = {
 
550
            'wiki': {
 
551
                'services': {
 
552
                    'charm': {
 
553
                        'charm_url': str(charm.store_url),
 
554
                        'constraints': 'mem=1G,cpu-donkeys=4',
 
555
                    }
 
556
                }
 
557
            }
 
558
        }
 
559
 
 
560
        request = self.make_request('bundle', remainder='/proof')
 
561
        request.params = {
 
562
            'deployer_file': yaml.dump(bundle_config),
 
563
        }
 
564
        response = self.api_class(request)()
 
565
        self.assertEqual(200, response.status_code)
 
566
        results = response.json_body
 
567
 
 
568
        self.assertEqual(1, len(results['errors']))
 
569
        self.assertEqual(1, len(results['error_messages']))
 
570
        self.assertEqual(0, len(results['warning_messages']))
 
571
 
 
572
        expected = 'wiki/charm: unsupported constraints: cpu-donkeys'
 
573
        self.assertEqual([expected], results['error_messages'])