Merge lp:~nskaggs/juju-ci-tools/add-assess-budget into lp:juju-ci-tools
- add-assess-budget
- Merge into trunk
Proposed by
Nicholas Skaggs
Status: | Merged |
---|---|
Merged at revision: | 1890 |
Proposed branch: | lp:~nskaggs/juju-ci-tools/add-assess-budget |
Merge into: | lp:juju-ci-tools |
Diff against target: |
545 lines (+536/-0) 2 files modified
assess_budget.py (+254/-0) tests/test_assess_budget.py (+282/-0) |
To merge this branch: | bzr merge lp:~nskaggs/juju-ci-tools/add-assess-budget |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Christopher Lee (community) | Approve | ||
Review via email: mp+317406@code.launchpad.net |
Commit message
Description of the change
This change adds test for the budget commands within juju. Note the following bugs which affect the ability of this test to run. bug 1665013 and bug 1663258
You will need to authenticate to jujucharms.com using charm login, or setting the macaroon yourself with juju.
To post a comment you must log in.
- 1899. By Nicholas Skaggs
-
addressed review comments
Revision history for this message
Nicholas Skaggs (nskaggs) wrote : | # |
See inline comments. I'm going to have a look at the whole number vs float issue.
Revision history for this message
Christopher Lee (veebers) wrote : | # |
Replied to the incline comment replies.
Revision history for this message
Christopher Lee (veebers) wrote : | # |
LGTM, a couple of comments re: test naming but nothing blocker.
review:
Approve
- 1900. By Nicholas Skaggs
-
final review tweaks
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === added file 'assess_budget.py' |
2 | --- assess_budget.py 1970-01-01 00:00:00 +0000 |
3 | +++ assess_budget.py 2017-02-16 21:51:25 +0000 |
4 | @@ -0,0 +1,254 @@ |
5 | +#!/usr/bin/env python |
6 | +""" |
7 | +This tests the budget commands utilized for commercial charm billing. |
8 | +These commands are linked to a ubuntu sso account, and as such, require the |
9 | +user account to be setup before test execution (including authentication). |
10 | +You can use charm login to do this, or let juju authenticate with a browser. |
11 | +""" |
12 | + |
13 | +from __future__ import print_function |
14 | + |
15 | +import argparse |
16 | +import json |
17 | +import logging |
18 | +from random import randint |
19 | +import subprocess |
20 | +import sys |
21 | + |
22 | + |
23 | +from jujupy import ( |
24 | + client_from_config, |
25 | +) |
26 | +from utility import ( |
27 | + add_basic_testing_arguments, |
28 | + JujuAssertionError, |
29 | + configure_logging, |
30 | +) |
31 | + |
32 | +__metaclass__ = type |
33 | + |
34 | + |
35 | +log = logging.getLogger("assess_budget") |
36 | + |
37 | + |
38 | +def _get_new_budget_limit(client): |
39 | + """Return availible limit for new budget""" |
40 | + budgets = json.loads(list_budgets(client)) |
41 | + limit = int(budgets['total']['limit']) |
42 | + credit = int(budgets['credit']) |
43 | + log.debug('Found credit limit {}, currently used {}'.format( |
44 | + credit, limit)) |
45 | + return credit - limit |
46 | + |
47 | + |
48 | +def _get_budgets(client): |
49 | + return json.loads(list_budgets(client))['budgets'] |
50 | + |
51 | + |
52 | +def _set_budget_value_expectations(expected_budgets, name, value): |
53 | + # Update our expectations accordingly |
54 | + for budget in expected_budgets: |
55 | + if budget['budget'] == name: |
56 | + # For now, we assume we aren't spending down the budget |
57 | + budget['limit'] = value |
58 | + budget['unallocated'] = value |
59 | + # .00 is appended to availible for some reason |
60 | + budget['available'] = '{:.2f}'.format(float(value)) |
61 | + log.info('Expected budget updated: "{}" to {}'.format(name, value)) |
62 | + |
63 | + |
64 | +def _try_setting_budget(client, name, value): |
65 | + try: |
66 | + output = set_budget(client, name, value) |
67 | + except subprocess.CalledProcessError as e: |
68 | + output = [e.output, getattr(e, 'stderr', '')] |
69 | + raise JujuAssertionError('Could not set budget {}'.format(output)) |
70 | + |
71 | + if 'budget limit updated' not in output: |
72 | + raise JujuAssertionError('Error calling set-budget {}'.format(output)) |
73 | + |
74 | + |
75 | +def _try_creating_budget(client, name, value): |
76 | + try: |
77 | + create_budget(client, name, value) |
78 | + log.info('Created new budget "{}" with value {}'.format(name, |
79 | + value)) |
80 | + except subprocess.CalledProcessError as e: |
81 | + output = [e.output, getattr(e, 'stderr', '')] |
82 | + if any('already exists' in message for message in output): |
83 | + log.info('Reusing budget "{}" with value {}'.format(name, value)) |
84 | + pass # this will be a failure once lp:1663258 is fixed |
85 | + else: |
86 | + raise JujuAssertionError( |
87 | + 'Error testing create-budget: {}'.format(output)) |
88 | + except: |
89 | + raise JujuAssertionError('Added duplicate budget') |
90 | + |
91 | + |
92 | +def _try_greater_than_limit_budget(client, name, limit): |
93 | + error_strings = { |
94 | + 'pass': 'exceed the credit limit', |
95 | + 'unknown': 'Error testing budget greater than credit limit', |
96 | + 'fail': 'Credit limit exceeded' |
97 | + } |
98 | + over_limit_value = str(limit + randint(1, 100)) |
99 | + assert_set_budget(client, name, over_limit_value, error_strings) |
100 | + |
101 | + |
102 | +def _try_negative_budget(client, name): |
103 | + error_strings = { |
104 | + 'pass': 'Could not set budget', |
105 | + 'unknown': 'Error testing negative budget', |
106 | + 'fail': 'Negative budget allowed' |
107 | + } |
108 | + negative_budget_value = str(randint(-1000, -1)) |
109 | + assert_set_budget(client, name, negative_budget_value, error_strings) |
110 | + |
111 | + |
112 | +def assert_sorted_equal(found, expected): |
113 | + found = sorted(found) |
114 | + expected = sorted(expected) |
115 | + if found != expected: |
116 | + raise JujuAssertionError( |
117 | + 'Found: {}\nExpected: {}'.format(found, expected)) |
118 | + |
119 | + |
120 | +def assert_set_budget(client, name, limit, error_strings): |
121 | + try: |
122 | + _try_setting_budget(client, name, limit) |
123 | + except JujuAssertionError as e: |
124 | + if error_strings['pass'] not in e.message: |
125 | + raise JujuAssertionError( |
126 | + '{}: {}'.format(error_strings['unknown'], e)) |
127 | + else: |
128 | + raise JujuAssertionError(error_strings['fail']) |
129 | + |
130 | + |
131 | +def create_budget(client, name, value): |
132 | + """Create a budget""" |
133 | + return client.get_juju_output('create-budget', name, value, |
134 | + include_e=False) |
135 | + |
136 | + |
137 | +def list_budgets(client): |
138 | + """Return defined budgets as json.""" |
139 | + return client.get_juju_output('list-budgets', '--format', 'json', |
140 | + include_e=False) |
141 | + |
142 | + |
143 | +def set_budget(client, name, value): |
144 | + """Change an existing budgets allocation.""" |
145 | + return client.get_juju_output('set-budget', name, value, include_e=False) |
146 | + |
147 | + |
148 | +def show_budget(client, name): |
149 | + """Return specified budget as json.""" |
150 | + return client.get_juju_output('show-budget', name, '--format', 'json', |
151 | + include_e=False) |
152 | + |
153 | + |
154 | +def assess_budget(client): |
155 | + # Since we can't remove budgets until lp:1663258 |
156 | + # is fixed, we avoid creating new random budgets and hardcode. |
157 | + # We also, zero out the previous budget |
158 | + budget_name = 'test' |
159 | + _try_setting_budget(client, budget_name, '0') |
160 | + |
161 | + budget_limit = _get_new_budget_limit(client) |
162 | + assess_budget_limit(budget_limit) |
163 | + |
164 | + expected_budgets = _get_budgets(client) |
165 | + budget_value = str(randint(1, budget_limit / 2)) |
166 | + assess_create_budget(client, budget_name, budget_value, budget_limit) |
167 | + |
168 | + budget_value = str(randint(budget_limit / 2 + 1, budget_limit)) |
169 | + assess_set_budget(client, budget_name, budget_value, budget_limit) |
170 | + assess_show_budget(client, budget_name, budget_value) |
171 | + |
172 | + _set_budget_value_expectations(expected_budgets, budget_name, budget_value) |
173 | + assess_list_budgets(client, expected_budgets) |
174 | + |
175 | + |
176 | +def assess_budget_limit(budget_limit): |
177 | + log.info('Assessing budget limit {}'.format(budget_limit)) |
178 | + |
179 | + if budget_limit < 0: |
180 | + raise JujuAssertionError( |
181 | + 'Negative Budget Limit {}'.format(budget_limit)) |
182 | + |
183 | + |
184 | +def assess_create_budget(client, budget_name, budget_value, budget_limit): |
185 | + """Test create-budget command""" |
186 | + log.info('create-budget "{}" with value {}, limit {}'.format(budget_name, |
187 | + budget_value, |
188 | + budget_limit)) |
189 | + |
190 | + # Do this twice, to ensure budget exists and we can check for |
191 | + # duplicate message. Ideally, once lp:1663258 is fixed, we will |
192 | + # assert on initial budget creation as well. |
193 | + _try_creating_budget(client, budget_name, budget_value) |
194 | + |
195 | + log.info('Trying duplicate create-budget') |
196 | + _try_creating_budget(client, budget_name, budget_value) |
197 | + |
198 | + |
199 | +def assess_list_budgets(client, expected_budgets): |
200 | + log.info('list-budgets testing expected values') |
201 | + # Since we can't remove budgets until lp:1663258 |
202 | + # is fixed, we don't modify the list contents or count |
203 | + # Nonetheless, we assert on it for future use |
204 | + budgets = _get_budgets(client) |
205 | + assert_sorted_equal(budgets, expected_budgets) |
206 | + |
207 | + |
208 | +def assess_set_budget(client, budget_name, budget_value, budget_limit): |
209 | + """Test set-budget command""" |
210 | + log.info('set-budget "{}" with value {}, limit {}'.format(budget_name, |
211 | + budget_value, |
212 | + budget_limit)) |
213 | + _try_setting_budget(client, budget_name, budget_value) |
214 | + |
215 | + # Check some bounds |
216 | + # Since budgetting is important, and the functional test is cheap, |
217 | + # let's test some basic bounds |
218 | + log.info('Trying set-budget with value greater than budget limit') |
219 | + _try_greater_than_limit_budget(client, budget_name, budget_limit) |
220 | + |
221 | + log.info('Trying set-budget with negative value') |
222 | + _try_negative_budget(client, budget_name) |
223 | + |
224 | + |
225 | +def assess_show_budget(client, budget_name, budget_value): |
226 | + log.info('show-budget "{}" with value {}'.format(budget_name, |
227 | + budget_value)) |
228 | + |
229 | + budget = json.loads(show_budget(client, budget_name)) |
230 | + |
231 | + # assert budget value |
232 | + if budget['limit'] != budget_value: |
233 | + raise JujuAssertionError('Budget limit found {}, expected {}'.format( |
234 | + budget['limit'], budget_value)) |
235 | + |
236 | + # assert on usage (0% until we use it) |
237 | + if budget['total']['usage'] != '0%': |
238 | + raise JujuAssertionError('Budget usage found {}, expected {}'.format( |
239 | + budget['total']['usage'], '0%')) |
240 | + |
241 | + |
242 | +def parse_args(argv): |
243 | + """Parse all arguments.""" |
244 | + parser = argparse.ArgumentParser(description="Test budget commands") |
245 | + add_basic_testing_arguments(parser) |
246 | + return parser.parse_args(argv) |
247 | + |
248 | + |
249 | +def main(argv=None): |
250 | + args = parse_args(argv) |
251 | + configure_logging(args.verbose) |
252 | + client = client_from_config(args.env, args.juju_bin, False) |
253 | + assess_budget(client) |
254 | + return 0 |
255 | + |
256 | + |
257 | +if __name__ == '__main__': |
258 | + sys.exit(main()) |
259 | |
260 | === added file 'tests/test_assess_budget.py' |
261 | --- tests/test_assess_budget.py 1970-01-01 00:00:00 +0000 |
262 | +++ tests/test_assess_budget.py 2017-02-16 21:51:25 +0000 |
263 | @@ -0,0 +1,282 @@ |
264 | +"""Tests for assess_budget module.""" |
265 | + |
266 | +import logging |
267 | +import json |
268 | +from random import randint |
269 | +import StringIO |
270 | +from subprocess import CalledProcessError |
271 | + |
272 | +from mock import ( |
273 | + Mock, |
274 | + patch, |
275 | +) |
276 | + |
277 | +from assess_budget import ( |
278 | + _try_greater_than_limit_budget, |
279 | + _try_negative_budget, |
280 | + assess_budget, |
281 | + assess_budget_limit, |
282 | + assess_create_budget, |
283 | + assess_list_budgets, |
284 | + assess_set_budget, |
285 | + assess_show_budget, |
286 | + main, |
287 | + parse_args, |
288 | +) |
289 | +from jujupy import ( |
290 | + fake_juju_client, |
291 | +) |
292 | +from tests import ( |
293 | + parse_error, |
294 | + TestCase, |
295 | +) |
296 | +from utility import ( |
297 | + JujuAssertionError, |
298 | +) |
299 | + |
300 | + |
301 | +class TestParseArgs(TestCase): |
302 | + |
303 | + def test_common_args(self): |
304 | + args = parse_args(["an-env", "/bin/juju", "/tmp/logs", "an-env-mod"]) |
305 | + self.assertEqual("an-env", args.env) |
306 | + self.assertEqual("/bin/juju", args.juju_bin) |
307 | + self.assertEqual("/tmp/logs", args.logs) |
308 | + self.assertEqual("an-env-mod", args.temp_env_name) |
309 | + self.assertEqual(False, args.debug) |
310 | + |
311 | + def test_help(self): |
312 | + fake_stdout = StringIO.StringIO() |
313 | + with parse_error(self) as fake_stderr: |
314 | + with patch("sys.stdout", fake_stdout): |
315 | + parse_args(["--help"]) |
316 | + self.assertEqual("", fake_stderr.getvalue()) |
317 | + |
318 | + |
319 | +class TestMain(TestCase): |
320 | + |
321 | + def test_main(self): |
322 | + argv = ["an-env", "/bin/juju", "/tmp/logs", "an-env-mod", "--verbose"] |
323 | + client = Mock(spec=["is_jes_enabled"]) |
324 | + with patch('assess_budget.subprocess.check_output', |
325 | + autospec=True): |
326 | + with patch("assess_budget.configure_logging", |
327 | + autospec=True) as mock_cl: |
328 | + with patch('assess_budget.client_from_config', |
329 | + return_value=client) as mock_cfc: |
330 | + with patch("assess_budget.assess_budget", |
331 | + autospec=True) as mock_assess: |
332 | + main(argv) |
333 | + mock_cl.assert_called_once_with(logging.DEBUG) |
334 | + mock_cfc.assert_called_once_with('an-env', "/bin/juju", False) |
335 | + mock_assess.assert_called_once_with(client) |
336 | + |
337 | + |
338 | +class TestAssess(TestCase): |
339 | + |
340 | + def setUp(self): |
341 | + super(TestAssess, self).setUp() |
342 | + self.fake_client = fake_juju_client() |
343 | + self.budget_name = 'test' |
344 | + self.budget_limit = randint(1000, 10000) |
345 | + self.budget_value = str(randint(100, 900)) |
346 | + |
347 | + def test_assess_budget(self): |
348 | + show_b = patch("assess_budget.assess_show_budget", |
349 | + autospec=True) |
350 | + list_b = patch("assess_budget.assess_list_budgets", |
351 | + autospec=True) |
352 | + set_b = patch("assess_budget.assess_set_budget", |
353 | + autospec=True) |
354 | + create_b = patch("assess_budget.assess_create_budget", |
355 | + autospec=True) |
356 | + b_limit = patch("assess_budget.assess_budget_limit", |
357 | + autospec=True) |
358 | + init_b = patch("assess_budget._try_setting_budget", |
359 | + autospec=True) |
360 | + expect_b = patch("assess_budget._set_budget_value_expectations", |
361 | + autospec=True) |
362 | + update_e = patch("assess_budget._get_budgets", autospec=True) |
363 | + |
364 | + with show_b as show_b_mock, list_b as list_b_mock, \ |
365 | + set_b as set_b_mock, create_b as create_b_mock, \ |
366 | + b_limit as b_limit_mock, init_b as init_b_mock, \ |
367 | + expect_b as expect_b_mock, update_e as update_e_mock: |
368 | + with patch("assess_budget.json.loads"): |
369 | + with patch("assess_budget.randint", |
370 | + return_value=self.budget_value): |
371 | + assess_budget(self.fake_client) |
372 | + |
373 | + init_b_mock.assert_called_once_with(self.fake_client, |
374 | + self.budget_name, |
375 | + '0') |
376 | + expect_b_mock.assert_called_once_with( |
377 | + update_e_mock(self.fake_client), |
378 | + self.budget_name, self.budget_value) |
379 | + b_limit_mock.assert_called_once_with(0) |
380 | + create_b_mock.assert_called_once_with( |
381 | + self.fake_client, self.budget_name, |
382 | + self.budget_value, 0) |
383 | + set_b_mock.assert_called_once_with( |
384 | + self.fake_client, self.budget_name, |
385 | + self.budget_value, 0) |
386 | + show_b_mock.assert_called_once_with( |
387 | + self.fake_client, self.budget_name, |
388 | + self.budget_value) |
389 | + list_b_mock.assert_called_once_with( |
390 | + self.fake_client, update_e_mock(self.fake_client)) |
391 | + |
392 | + self.assertEqual(init_b_mock.call_count, 1) |
393 | + self.assertEqual(expect_b_mock.call_count, 1) |
394 | + self.assertEqual(b_limit_mock.call_count, 1) |
395 | + self.assertEqual(create_b_mock.call_count, 1) |
396 | + self.assertEqual(set_b_mock.call_count, 1) |
397 | + self.assertEqual(show_b_mock.call_count, 1) |
398 | + self.assertEqual(list_b_mock.call_count, 1) |
399 | + |
400 | + |
401 | +class TestAssessShowBudget(TestAssess): |
402 | + |
403 | + def setUp(self): |
404 | + super(TestAssessShowBudget, self).setUp() |
405 | + self.fake_json = json.loads('{"limit":"' + self.budget_value + |
406 | + '","total":{"usage":"0%"}}') |
407 | + |
408 | + def test_assess_show_budget(self): |
409 | + with patch.object(self.fake_client, 'get_juju_output'): |
410 | + with patch("assess_budget.json.loads", |
411 | + return_value=self.fake_json): |
412 | + assess_show_budget(self.fake_client, self.budget_name, |
413 | + self.budget_value) |
414 | + |
415 | + def test_raises_budget_usage_error(self): |
416 | + error_usage = randint(1, 100) |
417 | + self.fake_json['total']['usage'] = error_usage |
418 | + with patch.object(self.fake_client, 'get_juju_output'): |
419 | + with patch("assess_budget.json.loads", |
420 | + return_value=self.fake_json): |
421 | + with self.assertRaises(JujuAssertionError) as ex: |
422 | + assess_show_budget(self.fake_client, self.budget_name, |
423 | + self.budget_value) |
424 | + self.assertEqual(ex.exception.message, |
425 | + 'Budget usage found {}, expected 0%'.format( |
426 | + error_usage)) |
427 | + |
428 | + def test_raises_budget_limit_error(self): |
429 | + self.fake_json['limit'] = 0 |
430 | + with patch.object(self.fake_client, 'get_juju_output'): |
431 | + with patch("assess_budget.json.loads", |
432 | + return_value=self.fake_json): |
433 | + with self.assertRaises(JujuAssertionError) as ex: |
434 | + assess_show_budget(self.fake_client, self.budget_name, |
435 | + self.budget_value) |
436 | + self.assertEqual(ex.exception.message, |
437 | + 'Budget limit found 0, expected {}'.format( |
438 | + self.budget_value)) |
439 | + |
440 | + |
441 | +class TestAssessListBudgets(TestAssess): |
442 | + |
443 | + def setUp(self): |
444 | + super(TestAssessListBudgets, self).setUp() |
445 | + snippet = '[{"budget": "test", "limit": "300"}]' |
446 | + self.fake_budgets_json = json.loads('{"budgets":' + snippet + '}') |
447 | + self.fake_budget_json = json.loads(snippet) |
448 | + unexpected_snippet = '[{"budget": "test", "limit": "100"}]' |
449 | + self.fake_unexpected_budgets_json = json.loads( |
450 | + '{"budgets":' + unexpected_snippet + '}') |
451 | + self.fake_unexpected_budget_json = json.loads(unexpected_snippet) |
452 | + |
453 | + def test_assess_list_budgets(self): |
454 | + with patch.object(self.fake_client, 'get_juju_output'): |
455 | + with patch("assess_budget.json.loads", |
456 | + return_value=self.fake_budgets_json): |
457 | + assess_list_budgets(self.fake_client, self.fake_budget_json) |
458 | + |
459 | + def test_raises_list_mismatch(self): |
460 | + with patch.object(self.fake_client, 'get_juju_output'): |
461 | + with patch("assess_budget.json.loads", |
462 | + return_value=self.fake_unexpected_budgets_json): |
463 | + with self.assertRaises(JujuAssertionError) as ex: |
464 | + assess_list_budgets( |
465 | + self.fake_client, self.fake_budget_json) |
466 | + self.assertEqual(ex.exception.message, |
467 | + 'Found: {}\nExpected: {}'.format( |
468 | + self.fake_unexpected_budget_json, |
469 | + self.fake_budget_json)) |
470 | + |
471 | + |
472 | +class TestAssessSetBudget(TestAssess): |
473 | + |
474 | + def test_assess_set_budget(self): |
475 | + with patch.object(self.fake_client, 'get_juju_output'): |
476 | + with patch("assess_budget.json.loads"): |
477 | + with patch("assess_budget._try_setting_budget"): |
478 | + with patch("assess_budget.assert_set_budget"): |
479 | + assess_set_budget(self.fake_client, self.budget_name, |
480 | + self.budget_value, self.budget_limit) |
481 | + |
482 | + def test_raises_on_exceed_credit_limit(self): |
483 | + with patch.object(self.fake_client, 'get_juju_output'): |
484 | + with patch("assess_budget.json.loads"): |
485 | + with patch("assess_budget._try_setting_budget"): |
486 | + with self.assertRaises(JujuAssertionError) as ex: |
487 | + _try_greater_than_limit_budget(self.fake_client, |
488 | + self.budget_name, |
489 | + self.budget_limit) |
490 | + self.assertEqual(ex.exception.message, |
491 | + 'Credit limit exceeded') |
492 | + |
493 | + def test_raises_on_negative_budget(self): |
494 | + self.budget_limit = -abs(self.budget_limit) |
495 | + with patch.object(self.fake_client, 'get_juju_output'): |
496 | + with patch("assess_budget.json.loads"): |
497 | + with patch("assess_budget._try_setting_budget"): |
498 | + with self.assertRaises(JujuAssertionError) as ex: |
499 | + _try_negative_budget(self.fake_client, |
500 | + self.budget_name) |
501 | + self.assertEqual(ex.exception.message, |
502 | + 'Negative budget allowed') |
503 | + |
504 | + |
505 | +class TestAssessCreateBudget(TestAssess): |
506 | + |
507 | + def test_assess_create_budget(self): |
508 | + with patch.object(self.fake_client, 'get_juju_output'): |
509 | + with patch("assess_budget.create_budget"): |
510 | + assess_create_budget(self.fake_client, self.budget_name, |
511 | + self.budget_value, self.budget_limit) |
512 | + |
513 | + def test_raises_duplicate_budget(self): |
514 | + with patch.object(self.fake_client, 'get_juju_output'): |
515 | + with patch("assess_budget.create_budget", |
516 | + side_effect=JujuAssertionError): |
517 | + with self.assertRaises(JujuAssertionError) as ex: |
518 | + assess_create_budget(self.fake_client, self.budget_name, |
519 | + self.budget_value, self.budget_limit) |
520 | + self.assertEqual(ex.exception.message, |
521 | + 'Added duplicate budget') |
522 | + |
523 | + def test_raises_creation_error(self): |
524 | + with patch.object(self.fake_client, 'get_juju_output'): |
525 | + with patch("assess_budget.create_budget", |
526 | + side_effect=CalledProcessError(1, 'foo', 'bar')): |
527 | + with self.assertRaises(JujuAssertionError) as ex: |
528 | + assess_create_budget(self.fake_client, self.budget_name, |
529 | + self.budget_value, self.budget_limit) |
530 | + self.assertEqual(ex.exception.message, |
531 | + "Error testing create-budget: ['bar', '']") |
532 | + |
533 | + |
534 | +class TestAssessBudgetLimit(TestAssess): |
535 | + |
536 | + def test_assess_budget_limt(self): |
537 | + budget_limit = randint(1, 10000) |
538 | + assess_budget_limit(budget_limit) |
539 | + |
540 | + def test_raises_error_on_negative_limit(self): |
541 | + neg_budget_limit = randint(-1000, -1) |
542 | + with self.assertRaises(JujuAssertionError) as ex: |
543 | + assess_budget_limit(neg_budget_limit) |
544 | + self.assertEqual(ex.exception.message, |
545 | + 'Negative Budget Limit {}'.format(neg_budget_limit)) |
Please see inline comments.