1 from budget_sync
.test
.mock_bug
import MockBug
2 from budget_sync
.config
import Config
3 from budget_sync
.budget_graph
import (
4 BudgetGraphLoopError
, BudgetGraph
, Node
, BudgetGraphMoneyWithNoMilestone
,
5 BudgetGraphBaseError
, BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
,
6 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
,
7 BudgetGraphNegativeMoney
, BudgetGraphMilestoneMismatch
,
8 BudgetGraphNegativePayeeMoney
, BudgetGraphPayeesParseError
,
9 BudgetGraphPayeesMoneyMismatch
, BudgetGraphUnknownMilestone
,
10 BudgetGraphIncorrectRootForMilestone
,
11 BudgetGraphUnknownStatus
, BudgetGraphUnknownAssignee
)
12 from budget_sync
.money
import Money
13 from budget_sync
.util
import BugStatus
14 from typing
import List
, Type
18 class TestErrorFormatting(unittest
.TestCase
):
19 def test_budget_graph_incorrect_root_for_milestone(self
):
20 self
.assertEqual(str(BudgetGraphIncorrectRootForMilestone(
21 2, "milestone 1", 1)),
22 "Bug #2 is not the canonical root bug for assigned milestone "
23 "'milestone 1' but has no parent bug set: the milestone's "
24 "canonical root bug is #1")
26 def test_budget_graph_loop_error(self
):
27 self
.assertEqual(str(BudgetGraphLoopError([1, 2, 3, 4, 5])),
28 "Detected Loop in Budget Graph: #5 -> #1 "
29 "-> #2 -> #3 -> #4 -> #5")
30 self
.assertEqual(str(BudgetGraphLoopError([1])),
31 "Detected Loop in Budget Graph: #1 -> #1")
33 def test_budget_graph_money_with_no_milestone(self
):
34 self
.assertEqual(str(BudgetGraphMoneyWithNoMilestone(1, 5)),
35 "Bug assigned money but without any assigned "
38 def test_budget_graph_milestone_mismatch(self
):
39 self
.assertEqual(str(BudgetGraphMilestoneMismatch(1, 5)),
40 "Bug's assigned milestone doesn't match the "
41 "milestone assigned to the root bug: descendant "
42 "bug #1, root bug #5")
44 def test_budget_graph_unknown_milestone(self
):
45 self
.assertEqual(str(BudgetGraphUnknownMilestone(
46 123, "fake milestone")),
47 "failed to parse cf_nlnet_milestone field of bug "
48 "#123: unknown milestone: 'fake milestone'")
50 def test_budget_graph_unknown_status(self
):
51 self
.assertEqual(str(BudgetGraphUnknownStatus(
53 "failed to parse status field of bug "
54 "#123: unknown status: 'fake status'")
56 def test_budget_graph_unknown_assignee(self
):
57 self
.assertEqual(str(BudgetGraphUnknownAssignee(
58 123, "unknown@example.com")),
59 "Bug #123 is assigned to an unknown person:"
60 " 'unknown@example.com'")
62 def test_budget_graph_money_mismatch(self
):
64 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks(
66 "Budget assigned to task excluding subtasks "
67 "(cf_budget field) doesn't match calculated value:"
68 " bug #1, calculated value 123.4")
70 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
72 "Budget assigned to task including subtasks "
73 "(cf_total_budget field) doesn't match calculated value:"
74 " bug #1, calculated value 123.4")
76 def test_budget_graph_negative_money(self
):
77 self
.assertEqual(str(BudgetGraphNegativeMoney(1, 5)),
78 "Budget assigned to task is less than zero: bug #1")
80 def test_budget_graph_negative_payee_money(self
):
81 self
.assertEqual(str(BudgetGraphNegativePayeeMoney(1, 5, "payee1")),
82 "Budget assigned to payee for task is less than "
83 "zero: bug #1, payee 'payee1'")
85 def test_budget_graph_payees_parse_error(self
):
87 BudgetGraphPayeesParseError(1, "my fake parse error")),
88 "Failed to parse cf_payees_list field of bug #1: "
89 "my fake parse error")
91 def test_budget_graph_payees_money_mismatch(self
):
93 BudgetGraphPayeesMoneyMismatch(1, 5, Money(123), Money(456))),
94 "Total budget assigned to payees (cf_payees_list) doesn't match "
95 "expected value: bug #1, calculated total 123, expected value 456")
98 EXAMPLE_BUG1
= MockBug(bug_id
=1,
99 cf_budget_parent
=None,
102 cf_nlnet_milestone
=None,
105 EXAMPLE_LOOP1_BUG1
= MockBug(bug_id
=1,
109 cf_nlnet_milestone
=None,
112 EXAMPLE_LOOP2_BUG1
= MockBug(bug_id
=1,
116 cf_nlnet_milestone
=None,
119 EXAMPLE_LOOP2_BUG2
= MockBug(bug_id
=2,
123 cf_nlnet_milestone
=None,
126 EXAMPLE_PARENT_BUG1
= MockBug(bug_id
=1,
127 cf_budget_parent
=None,
129 cf_total_budget
="20",
130 cf_nlnet_milestone
="milestone 1",
133 EXAMPLE_CHILD_BUG2
= MockBug(bug_id
=2,
136 cf_total_budget
="10",
137 cf_nlnet_milestone
="milestone 1",
141 EXAMPLE_CONFIG
= Config
.from_str(
143 bugzilla_url = "https://bugzilla.example.com/"
145 aliases = ["person1_alias1", "alias1"]
146 full_name = "Person One"
148 email = "person2@example.com"
149 aliases = ["person1_alias2", "alias2", "person 2"]
150 full_name = "Person Two"
152 email = "user@example.com"
153 full_name = "Person Three"
155 "milestone 1" = { canonical_bug_id = 1 }
156 "milestone 2" = { canonical_bug_id = 2 }
160 class TestBudgetGraph(unittest
.TestCase
):
163 def assertErrorTypesMatches(self
, errors
: List
[BudgetGraphBaseError
], template
: List
[Type
]):
164 def wrap_type_list(type_list
: List
[Type
]):
166 def __init__(self
, t
):
170 return self
.t
.__name
__
172 def __eq__(self
, other
):
173 return self
.t
== other
.t
174 return [TypeWrapper(i
) for i
in type_list
]
177 error_types
.append(type(error
))
178 self
.assertEqual(wrap_type_list(error_types
), wrap_type_list(template
))
181 bg
= BudgetGraph([EXAMPLE_PARENT_BUG1
, EXAMPLE_CHILD_BUG2
],
185 "BudgetGraph{nodes=[Node(graph=..., id=#1, root=#1, parent=None, "
186 "budget_excluding_subtasks=10, budget_including_subtasks=20, "
187 "fixed_budget_excluding_subtasks=10, "
188 "fixed_budget_including_subtasks=20, milestone_str='milestone "
189 "1', milestone=Milestone(config=..., identifier='milestone 1', "
190 "canonical_bug_id=1), immediate_children=[#2], payments=[], "
191 "status=BugStatus.CONFIRMED, assignee=Person<'person3'>, "
192 "resolved_payments={}, payment_summaries={}), Node(graph=..., "
193 "id=#2, root=#1, parent=#1, budget_excluding_subtasks=10, "
194 "budget_including_subtasks=10, "
195 "fixed_budget_excluding_subtasks=10, "
196 "fixed_budget_including_subtasks=10, milestone_str='milestone "
197 "1', milestone=Milestone(config=..., identifier='milestone 1', "
198 "canonical_bug_id=1), immediate_children=[], payments=[], "
199 "status=BugStatus.CONFIRMED, assignee=Person<'person3'>, "
200 "resolved_payments={}, payment_summaries={})], roots=[#1], "
201 "assigned_nodes={Person(config=..., identifier='person1', "
202 "full_name='Person One', "
203 "aliases=OrderedSet(['person1_alias1', 'alias1']), email=None): "
204 "[], Person(config=..., identifier='person2', "
205 "full_name='Person Two', "
206 "aliases=OrderedSet(['person1_alias2', 'alias2', 'person 2']), "
207 "email='person2@example.com'): [], Person(config=..., "
208 "identifier='person3', full_name='Person Three', "
209 "aliases=OrderedSet(), email='user@example.com'): [#1, #2]}, "
210 "assigned_nodes_for_milestones={Milestone(config=..., "
211 "identifier='milestone 1', canonical_bug_id=1): [#1, #2], "
212 "Milestone(config=..., identifier='milestone 2', "
213 "canonical_bug_id=2): []}, "
214 "milestone_payments={Milestone(config=..., identifier='milestone "
215 "1', canonical_bug_id=1): [], Milestone(config=..., "
216 "identifier='milestone 2', canonical_bug_id=2): []}, "
217 "payments={Person(config=..., identifier='person1', "
218 "full_name='Person One', "
219 "aliases=OrderedSet(['person1_alias1', 'alias1']), email=None): "
220 "{Milestone(config=..., identifier='milestone 1', "
221 "canonical_bug_id=1): [], Milestone(config=..., "
222 "identifier='milestone 2', canonical_bug_id=2): []}, "
223 "Person(config=..., identifier='person2', "
224 "full_name='Person Two', "
225 "aliases=OrderedSet(['person1_alias2', 'alias2', 'person 2']), "
226 "email='person2@example.com'): {Milestone(config=..., "
227 "identifier='milestone 1', canonical_bug_id=1): [], "
228 "Milestone(config=..., identifier='milestone 2', "
229 "canonical_bug_id=2): []}, Person(config=..., "
230 "identifier='person3', full_name='Person Three', "
231 "aliases=OrderedSet(), email='user@example.com'): "
232 "{Milestone(config=..., identifier='milestone 1', "
233 "canonical_bug_id=1): [], Milestone(config=..., "
234 "identifier='milestone 2', canonical_bug_id=2): []}}, "
235 "milestone_people={Milestone(config=..., identifier='milestone "
236 "1', canonical_bug_id=1): OrderedSet(), Milestone(config=..., "
237 "identifier='milestone 2', canonical_bug_id=2): OrderedSet()}}")
238 bg
= BudgetGraph([MockBug(bug_id
=1, status
="blah",
239 assigned_to
="unknown@example.com")],
243 "BudgetGraph{nodes=[Node(graph=..., id=#1, root=#1, parent=None, "
244 "budget_excluding_subtasks=0, budget_including_subtasks=0, "
245 "fixed_budget_excluding_subtasks=0, "
246 "fixed_budget_including_subtasks=0, milestone_str=None, "
247 "milestone=None, immediate_children=[], payments=[], "
248 "status=<unknown status: 'blah'>, assignee=<unknown assignee: "
249 "'unknown@example.com'>, resolved_payments={}, "
250 "payment_summaries={})], roots=[#1], assigned_nodes=<failed>, "
251 "assigned_nodes_for_milestones={Milestone(config=..., "
252 "identifier='milestone 1', canonical_bug_id=1): [], "
253 "Milestone(config=..., identifier='milestone 2', "
254 "canonical_bug_id=2): []}, "
255 "milestone_payments={Milestone(config=..., "
256 "identifier='milestone 1', canonical_bug_id=1): [], "
257 "Milestone(config=..., identifier='milestone 2', "
258 "canonical_bug_id=2): []}, payments={Person(config=..., "
259 "identifier='person1', full_name='Person One', "
260 "aliases=OrderedSet(['person1_alias1', 'alias1']), email=None): "
261 "{Milestone(config=..., identifier='milestone 1', "
262 "canonical_bug_id=1): [], Milestone(config=..., "
263 "identifier='milestone 2', canonical_bug_id=2): []}, "
264 "Person(config=..., identifier='person2', "
265 "full_name='Person Two', "
266 "aliases=OrderedSet(['person1_alias2', 'alias2', "
267 "'person 2']), email='person2@example.com'): "
268 "{Milestone(config=..., identifier='milestone 1', "
269 "canonical_bug_id=1): [], Milestone(config=..., "
270 "identifier='milestone 2', canonical_bug_id=2): []}, "
271 "Person(config=..., identifier='person3', "
272 "full_name='Person Three', aliases=OrderedSet(), "
273 "email='user@example.com'): {Milestone(config=..., "
274 "identifier='milestone 1', canonical_bug_id=1): [], "
275 "Milestone(config=..., identifier='milestone 2', "
276 "canonical_bug_id=2): []}}, "
277 "milestone_people={Milestone(config=..., identifier='milestone "
278 "1', canonical_bug_id=1): OrderedSet(), Milestone(config=..., "
279 "identifier='milestone 2', canonical_bug_id=2): OrderedSet()}}")
280 bg
= BudgetGraph([MockBug(bug_id
=1, status
="blah",
281 assigned_to
="unknown@example.com",
283 person1 = {paid=2020-03-15,amount=5}
284 alias1 = {paid=2020-03-15,amount=10}
285 person2 = {submitted=2020-03-15,amount=15}
286 alias2 = {paid=2020-03-16,amount=23}
291 "BudgetGraph{nodes=[Node(graph=..., id=#1, root=#1, parent=None, "
292 "budget_excluding_subtasks=0, budget_including_subtasks=0, "
293 "fixed_budget_excluding_subtasks=0, "
294 "fixed_budget_including_subtasks=0, milestone_str=None, "
295 "milestone=None, immediate_children=[], "
296 "payments=[Payment(node=#1, payee=Person<'person1'>, "
297 "payee_key='person1', amount=5, state=Paid, paid=2020-03-15, "
298 "submitted=None), Payment(node=#1, payee=Person<'person1'>, "
299 "payee_key='alias1', amount=10, state=Paid, paid=2020-03-15, "
300 "submitted=None), Payment(node=#1, payee=Person<'person2'>, "
301 "payee_key='person2', amount=15, state=Submitted, paid=None, "
302 "submitted=2020-03-15), Payment(node=#1, "
303 "payee=Person<'person2'>, payee_key='alias2', amount=23, "
304 "state=Paid, paid=2020-03-16, submitted=None)], status=<unknown "
305 "status: 'blah'>, assignee=<unknown assignee: "
306 "'unknown@example.com'>, resolved_payments={Person(config=..., "
307 "identifier='person1', full_name='Person One', "
308 "aliases=OrderedSet(['person1_alias1', 'alias1']), email=None): "
309 "[Payment(node=#1, payee=Person<'person1'>, payee_key='person1', "
310 "amount=5, state=Paid, paid=2020-03-15, submitted=None), "
311 "Payment(node=#1, payee=Person<'person1'>, payee_key='alias1', "
312 "amount=10, state=Paid, paid=2020-03-15, submitted=None)], "
313 "Person(config=..., identifier='person2', "
314 "full_name='Person Two', "
315 "aliases=OrderedSet(['person1_alias2', 'alias2', 'person 2']), "
316 "email='person2@example.com'): [Payment(node=#1, "
317 "payee=Person<'person2'>, payee_key='person2', amount=15, "
318 "state=Submitted, paid=None, submitted=2020-03-15), "
319 "Payment(node=#1, payee=Person<'person2'>, payee_key='alias2', "
320 "amount=23, state=Paid, paid=2020-03-16, submitted=None)]}, "
321 "payment_summaries={Person(config=..., identifier='person1', "
322 "full_name='Person One', "
323 "aliases=OrderedSet(['person1_alias1', 'alias1']), email=None): "
324 "PaymentSummary(total=15, total_paid=15, total_submitted=15, "
325 "submitted_date=None, paid_date=2020-03-15, "
326 "state=PaymentSummaryState.Paid, payments=(Payment(node=#1, "
327 "payee=Person<'person1'>, payee_key='person1', amount=5, "
328 "state=Paid, paid=2020-03-15, submitted=None), Payment(node=#1, "
329 "payee=Person<'person1'>, payee_key='alias1', amount=10, "
330 "state=Paid, paid=2020-03-15, submitted=None))), "
331 "Person(config=..., identifier='person2', "
332 "full_name='Person Two', "
333 "aliases=OrderedSet(['person1_alias2', 'alias2', 'person 2']), "
334 "email='person2@example.com'): PaymentSummary(total=38, "
335 "total_paid=23, total_submitted=38, submitted_date=None, "
336 "paid_date=None, state=PaymentSummaryState.Inconsistent, "
337 "payments=(Payment(node=#1, payee=Person<'person2'>, "
338 "payee_key='person2', amount=15, state=Submitted, paid=None, "
339 "submitted=2020-03-15), Payment(node=#1, "
340 "payee=Person<'person2'>, payee_key='alias2', amount=23, "
341 "state=Paid, paid=2020-03-16, submitted=None)))})], roots=[#1], "
342 "assigned_nodes=<failed>, "
343 "assigned_nodes_for_milestones={Milestone(config=..., "
344 "identifier='milestone 1', canonical_bug_id=1): [], "
345 "Milestone(config=..., identifier='milestone 2', "
346 "canonical_bug_id=2): []}, "
347 "milestone_payments={Milestone(config=..., identifier='milestone "
348 "1', canonical_bug_id=1): [], Milestone(config=..., "
349 "identifier='milestone 2', canonical_bug_id=2): []}, "
350 "payments={Person(config=..., identifier='person1', "
351 "full_name='Person One', "
352 "aliases=OrderedSet(['person1_alias1', 'alias1']), email=None): "
353 "{Milestone(config=..., identifier='milestone 1', "
354 "canonical_bug_id=1): [], Milestone(config=..., "
355 "identifier='milestone 2', canonical_bug_id=2): []}, "
356 "Person(config=..., identifier='person2', "
357 "full_name='Person Two', "
358 "aliases=OrderedSet(['person1_alias2', 'alias2', 'person 2']), "
359 "email='person2@example.com'): {Milestone(config=..., "
360 "identifier='milestone 1', canonical_bug_id=1): [], "
361 "Milestone(config=..., identifier='milestone 2', "
362 "canonical_bug_id=2): []}, Person(config=..., "
363 "identifier='person3', full_name='Person Three', "
364 "aliases=OrderedSet(), email='user@example.com'): "
365 "{Milestone(config=..., identifier='milestone 1', "
366 "canonical_bug_id=1): [], Milestone(config=..., "
367 "identifier='milestone 2', canonical_bug_id=2): []}}, "
368 "milestone_people={Milestone(config=..., identifier='milestone "
369 "1', canonical_bug_id=1): OrderedSet(), Milestone(config=..., "
370 "identifier='milestone 2', canonical_bug_id=2): OrderedSet()}}")
372 def test_empty(self
):
373 bg
= BudgetGraph([], EXAMPLE_CONFIG
)
374 self
.assertEqual(len(bg
.nodes
), 0)
375 self
.assertEqual(len(bg
.roots
), 0)
376 self
.assertIs(bg
.config
, EXAMPLE_CONFIG
)
378 def test_single(self
):
379 bg
= BudgetGraph([EXAMPLE_BUG1
], EXAMPLE_CONFIG
)
380 self
.assertEqual(len(bg
.nodes
), 1)
381 node
: Node
= bg
.nodes
[1]
382 self
.assertEqual(bg
.roots
, {node}
)
383 self
.assertIsInstance(node
, Node
)
384 self
.assertIs(node
.graph
, bg
)
385 self
.assertIs(node
.bug
, EXAMPLE_BUG1
)
386 self
.assertIs(node
.root
, node
)
387 self
.assertIsNone(node
.parent_id
)
388 self
.assertEqual(node
.immediate_children
, set())
389 self
.assertEqual(node
.bug_url
,
390 "https://bugzilla.example.com/show_bug.cgi?id=1")
391 self
.assertEqual(node
.budget_excluding_subtasks
, Money(cents
=0))
392 self
.assertEqual(node
.budget_including_subtasks
, Money(cents
=0))
393 self
.assertIsNone(node
.milestone
)
394 self
.assertEqual(node
.payments
, {})
396 def test_loop1(self
):
397 with self
.assertRaises(BudgetGraphLoopError
) as cm
:
398 BudgetGraph([EXAMPLE_LOOP1_BUG1
], EXAMPLE_CONFIG
).roots
399 self
.assertEqual(cm
.exception
.bug_ids
, [1])
401 def test_loop2(self
):
402 with self
.assertRaises(BudgetGraphLoopError
) as cm
:
403 BudgetGraph([EXAMPLE_LOOP2_BUG1
, EXAMPLE_LOOP2_BUG2
],
404 EXAMPLE_CONFIG
).roots
405 self
.assertEqual(cm
.exception
.bug_ids
, [2, 1])
407 def test_parent_child(self
):
408 bg
= BudgetGraph([EXAMPLE_PARENT_BUG1
, EXAMPLE_CHILD_BUG2
],
410 self
.assertEqual(len(bg
.nodes
), 2)
411 node1
: Node
= bg
.nodes
[1]
412 node2
: Node
= bg
.nodes
[2]
413 self
.assertEqual(bg
.roots
, {node1}
)
414 self
.assertEqual(node1
, node1
)
415 self
.assertEqual(node2
, node2
)
416 self
.assertNotEqual(node1
, node2
)
417 self
.assertNotEqual(node2
, node1
)
418 self
.assertIsInstance(node1
, Node
)
419 self
.assertIs(node1
.graph
, bg
)
420 self
.assertIs(node1
.bug
, EXAMPLE_PARENT_BUG1
)
421 self
.assertIsNone(node1
.parent_id
)
422 self
.assertEqual(node1
.root
, node1
)
423 self
.assertEqual(node1
.immediate_children
, {node2}
)
424 self
.assertEqual(node1
.budget_excluding_subtasks
, Money(cents
=1000))
425 self
.assertEqual(node1
.budget_including_subtasks
, Money(cents
=2000))
426 self
.assertEqual(node1
.milestone_str
, "milestone 1")
427 self
.assertEqual(node1
.bug_url
,
428 "https://bugzilla.example.com/show_bug.cgi?id=1")
429 self
.assertEqual(list(node1
.children()), [node2
])
430 self
.assertEqual(list(node1
.children_breadth_first()), [node2
])
431 self
.assertEqual(node1
.payments
, {})
432 self
.assertIsInstance(node2
, Node
)
433 self
.assertIs(node2
.graph
, bg
)
434 self
.assertIs(node2
.bug
, EXAMPLE_CHILD_BUG2
)
435 self
.assertEqual(node2
.parent_id
, 1)
436 self
.assertEqual(node2
.root
, node1
)
437 self
.assertEqual(node2
.immediate_children
, set())
438 self
.assertEqual(node2
.budget_excluding_subtasks
, Money(cents
=1000))
439 self
.assertEqual(node2
.budget_including_subtasks
, Money(cents
=1000))
440 self
.assertEqual(node2
.milestone_str
, "milestone 1")
441 self
.assertEqual(node2
.payments
, {})
442 self
.assertEqual(node2
.bug_url
,
443 "https://bugzilla.example.com/show_bug.cgi?id=2")
445 def test_children(self
):
448 cf_budget_parent
=None,
451 cf_nlnet_milestone
=None,
458 cf_nlnet_milestone
=None,
465 cf_nlnet_milestone
=None,
472 cf_nlnet_milestone
=None,
479 cf_nlnet_milestone
=None,
486 cf_nlnet_milestone
=None,
493 cf_nlnet_milestone
=None,
497 self
.assertEqual(len(bg
.nodes
), 7)
498 node1
: Node
= bg
.nodes
[1]
499 node2
: Node
= bg
.nodes
[2]
500 node3
: Node
= bg
.nodes
[3]
501 node4
: Node
= bg
.nodes
[4]
502 node5
: Node
= bg
.nodes
[5]
503 node6
: Node
= bg
.nodes
[6]
504 node7
: Node
= bg
.nodes
[7]
505 self
.assertEqual(bg
.roots
, {node1}
)
506 self
.assertEqual(list(node1
.children()),
507 [node2
, node3
, node5
, node7
, node6
, node4
])
508 self
.assertEqual(list(node1
.children_breadth_first()),
509 [node2
, node3
, node4
, node5
, node6
, node7
])
511 def test_money_with_no_milestone(self
):
514 cf_budget_parent
=None,
516 cf_total_budget
="10",
517 cf_nlnet_milestone
=None,
521 errors
= bg
.get_errors()
522 self
.assertErrorTypesMatches(errors
, [
523 BudgetGraphMoneyWithNoMilestone
,
524 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
])
525 self
.assertEqual(errors
[0].bug_id
, 1)
526 self
.assertEqual(errors
[0].root_bug_id
, 1)
529 cf_budget_parent
=None,
532 cf_nlnet_milestone
=None,
536 errors
= bg
.get_errors()
537 self
.assertErrorTypesMatches(errors
, [
538 BudgetGraphMoneyWithNoMilestone
,
539 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
])
540 self
.assertEqual(errors
[0].bug_id
, 1)
541 self
.assertEqual(errors
[0].root_bug_id
, 1)
544 cf_budget_parent
=None,
546 cf_total_budget
="10",
547 cf_nlnet_milestone
=None,
551 errors
= bg
.get_errors()
552 self
.assertErrorTypesMatches(errors
, [BudgetGraphMoneyWithNoMilestone
])
553 self
.assertEqual(errors
[0].bug_id
, 1)
554 self
.assertEqual(errors
[0].root_bug_id
, 1)
556 def test_money_mismatch(self
):
557 def helper(budget
, total_budget
, payees_list
, child_budget
,
558 expected_errors
, expected_fixed_error_types
=None):
559 if expected_fixed_error_types
is None:
560 expected_fixed_error_types
= []
563 cf_budget_parent
=None,
565 cf_total_budget
=total_budget
,
566 cf_nlnet_milestone
="milestone 1",
567 cf_payees_list
=payees_list
,
571 cf_budget
=child_budget
,
572 cf_total_budget
=child_budget
,
573 cf_nlnet_milestone
="milestone 1",
577 node1
: Node
= bg
.nodes
[1]
578 errors
= bg
.get_errors()
579 self
.assertErrorTypesMatches(errors
,
580 [type(i
) for i
in expected_errors
])
581 self
.assertEqual([str(i
) for i
in errors
],
582 [str(i
) for i
in expected_errors
])
585 cf_budget_parent
=None,
586 cf_budget
=str(node1
.fixed_budget_excluding_subtasks
),
588 node1
.fixed_budget_including_subtasks
),
589 cf_nlnet_milestone
="milestone 1",
590 cf_payees_list
=payees_list
,
594 cf_budget
=child_budget
,
595 cf_total_budget
=child_budget
,
596 cf_nlnet_milestone
="milestone 1",
600 errors
= bg
.get_errors()
601 self
.assertErrorTypesMatches(errors
,
602 expected_fixed_error_types
)
613 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
618 payees_list
="person1=1",
621 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks(
623 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
628 payees_list
="person1=1",
631 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks(
633 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
638 payees_list
="person1=10",
641 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks(
643 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
648 payees_list
="person1=10",
651 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks(
653 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
661 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks(
669 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks(
674 payees_list
="person1=1",
677 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks(
679 BudgetGraphPayeesMoneyMismatch(1, 1, Money(1), Money(100)),
681 expected_fixed_error_types
=[BudgetGraphPayeesMoneyMismatch
])
684 payees_list
="person1=1",
687 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks(
689 BudgetGraphPayeesMoneyMismatch(1, 1, Money(1), Money(95)),
691 expected_fixed_error_types
=[BudgetGraphPayeesMoneyMismatch
])
694 payees_list
="person1=10",
697 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks(
699 BudgetGraphPayeesMoneyMismatch(1, 1, Money(10), Money(100)),
701 expected_fixed_error_types
=[BudgetGraphPayeesMoneyMismatch
])
704 payees_list
="person1=10",
707 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks(
709 BudgetGraphPayeesMoneyMismatch(1, 1, Money(10), Money(95)),
711 expected_fixed_error_types
=[BudgetGraphPayeesMoneyMismatch
])
719 payees_list
="person1=1",
722 BudgetGraphPayeesMoneyMismatch(1, 1, Money(1), Money(0)),
724 expected_fixed_error_types
=[BudgetGraphPayeesMoneyMismatch
])
727 payees_list
="person1=10",
730 BudgetGraphPayeesMoneyMismatch(1, 1, Money(10), Money(0)),
732 expected_fixed_error_types
=[BudgetGraphPayeesMoneyMismatch
])
738 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
746 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
751 payees_list
="person1=1",
754 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
756 BudgetGraphPayeesMoneyMismatch(1, 1, Money(1), Money(10)),
758 expected_fixed_error_types
=[BudgetGraphPayeesMoneyMismatch
])
761 payees_list
="person1=1",
764 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
766 BudgetGraphPayeesMoneyMismatch(1, 1, Money(1), Money(10)),
768 expected_fixed_error_types
=[BudgetGraphPayeesMoneyMismatch
])
771 payees_list
="person1=10",
774 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
779 payees_list
="person1=10",
782 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
792 payees_list
="person1=1",
795 BudgetGraphPayeesMoneyMismatch(1, 1, Money(1), Money(10)),
797 expected_fixed_error_types
=[BudgetGraphPayeesMoneyMismatch
])
800 payees_list
="person1=10",
808 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks(
816 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks(
821 payees_list
="person1=1",
824 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
826 BudgetGraphPayeesMoneyMismatch(1, 1, Money(1), Money(10)),
828 expected_fixed_error_types
=[BudgetGraphPayeesMoneyMismatch
])
831 payees_list
="person1=1",
834 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
836 BudgetGraphPayeesMoneyMismatch(1, 1, Money(1), Money(10)),
838 expected_fixed_error_types
=[BudgetGraphPayeesMoneyMismatch
])
841 payees_list
="person1=10",
844 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
849 payees_list
="person1=10",
852 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
862 payees_list
="person1=1",
865 BudgetGraphPayeesMoneyMismatch(1, 1, Money(1), Money(10)),
867 expected_fixed_error_types
=[BudgetGraphPayeesMoneyMismatch
])
870 payees_list
="person1=10",
876 payees_list
="person1=10",
879 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks(
883 def test_negative_money(self
):
886 cf_budget_parent
=None,
888 cf_total_budget
="-10",
889 cf_nlnet_milestone
="milestone 1",
893 errors
= bg
.get_errors()
894 self
.assertErrorTypesMatches(errors
, [
895 BudgetGraphNegativeMoney
,
896 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
])
897 self
.assertEqual(errors
[0].bug_id
, 1)
898 self
.assertEqual(errors
[0].root_bug_id
, 1)
899 self
.assertEqual(errors
[1].bug_id
, 1)
900 self
.assertEqual(errors
[1].root_bug_id
, 1)
901 self
.assertEqual(errors
[1].expected_budget_including_subtasks
, 0)
904 cf_budget_parent
=None,
907 cf_nlnet_milestone
="milestone 1",
911 errors
= bg
.get_errors()
912 self
.assertErrorTypesMatches(errors
, [
913 BudgetGraphNegativeMoney
,
914 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
])
915 self
.assertEqual(errors
[0].bug_id
, 1)
916 self
.assertEqual(errors
[0].root_bug_id
, 1)
917 self
.assertEqual(errors
[1].bug_id
, 1)
918 self
.assertEqual(errors
[1].root_bug_id
, 1)
919 self
.assertEqual(errors
[1].expected_budget_including_subtasks
, -10)
922 cf_budget_parent
=None,
924 cf_total_budget
="-10",
925 cf_nlnet_milestone
="milestone 1",
929 errors
= bg
.get_errors()
930 self
.assertErrorTypesMatches(errors
,
931 [BudgetGraphNegativeMoney
])
932 self
.assertEqual(errors
[0].bug_id
, 1)
933 self
.assertEqual(errors
[0].root_bug_id
, 1)
935 def test_payees_parse(self
):
936 def check(cf_payees_list
, error_types
, expected_payments
):
937 bg
= BudgetGraph([MockBug(bug_id
=1,
938 cf_budget_parent
=None,
941 cf_nlnet_milestone
="milestone 1",
942 cf_payees_list
=cf_payees_list
,
945 self
.assertErrorTypesMatches(bg
.get_errors(), error_types
)
946 self
.assertEqual(len(bg
.nodes
), 1)
947 node
: Node
= bg
.nodes
[1]
948 self
.assertEqual([str(i
) for i
in node
.payments
.values()],
955 [BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
,
956 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
],
957 ["Payment(node=#1, payee=Person<'person1'>, "
958 "payee_key='person1', amount=123, "
959 "state=NotYetSubmitted, paid=None, submitted=None)"])
964 [BudgetGraphPayeesParseError
,
965 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
,
966 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
],
967 ["Payment(node=#1, payee=<unknown person>, payee_key='abc', "
968 "amount=123, state=NotYetSubmitted, paid=None, "
974 [BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
,
975 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
],
976 ["Payment(node=#1, payee=Person<'person1'>, "
977 "payee_key='person1', amount=123.45, "
978 "state=NotYetSubmitted, paid=None, submitted=None)"])
984 [BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
,
985 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
],
986 ["Payment(node=#1, payee=Person<'person1'>, payee_key='person1', "
987 'amount=123.45, state=NotYetSubmitted, paid=None, '
989 "Payment(node=#1, payee=Person<'person2'>, payee_key='person 2', "
990 'amount=21.35, state=NotYetSubmitted, paid=None, '
997 [BudgetGraphPayeesParseError
,
998 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
,
999 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
],
1000 ["Payment(node=#1, payee=Person<'person1'>, payee_key='person1', "
1001 'amount=123.45, state=NotYetSubmitted, paid=None, '
1003 "Payment(node=#1, payee=<unknown person>, payee_key='d e f', "
1004 'amount=21.35, state=NotYetSubmitted, paid=None, '
1012 [BudgetGraphPayeesParseError
,
1013 BudgetGraphNegativePayeeMoney
,
1014 BudgetGraphPayeesParseError
,
1015 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
,
1016 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
],
1017 ["Payment(node=#1, payee=<unknown person>, payee_key='abc', "
1018 'amount=123.45, state=NotYetSubmitted, paid=None, '
1020 "Payment(node=#1, payee=<unknown person>, payee_key='AAA', "
1021 'amount=-21.35, state=NotYetSubmitted, paid=None, '
1025 "not-an-email@example.com" = "-2345"
1027 [BudgetGraphNegativePayeeMoney
,
1028 BudgetGraphPayeesParseError
,
1029 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
,
1030 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
],
1031 ['Payment(node=#1, payee=<unknown person>, '
1032 "payee_key='not-an-email@example.com', amount=-2345, "
1033 "state=NotYetSubmitted, paid=None, submitted=None)"])
1036 person1 = { amount = 123 }
1038 [BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
,
1039 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
],
1040 ["Payment(node=#1, payee=Person<'person1'>, payee_key='person1', "
1041 "amount=123, state=NotYetSubmitted, paid=None, submitted=None)"])
1044 person1 = { amount = 123, submitted = 2020-05-01 }
1046 [BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
,
1047 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
],
1048 ["Payment(node=#1, payee=Person<'person1'>, payee_key='person1', "
1049 + "amount=123, state=Submitted, paid=None, "
1050 + "submitted=2020-05-01)"])
1053 person1 = { amount = 123, submitted = 2020-05-01T00:00:00 }
1055 [BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
,
1056 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
],
1057 ["Payment(node=#1, payee=Person<'person1'>, payee_key='person1', "
1058 + "amount=123, state=Submitted, paid=None, "
1059 + "submitted=2020-05-01 00:00:00)"])
1062 person1 = { amount = 123, submitted = 2020-05-01T00:00:00Z }
1064 [BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
,
1065 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
],
1066 ["Payment(node=#1, payee=Person<'person1'>, payee_key='person1', "
1067 + "amount=123, state=Submitted, paid=None, "
1068 + "submitted=2020-05-01 00:00:00+00:00)"])
1071 person1 = { amount = 123, submitted = 2020-05-01T00:00:00-07:23 }
1073 [BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
,
1074 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
],
1075 ["Payment(node=#1, payee=Person<'person1'>, payee_key='person1', "
1076 + "amount=123, state=Submitted, paid=None, "
1077 + "submitted=2020-05-01 00:00:00-07:23)"])
1080 person1 = { amount = 123, paid = 2020-05-01 }
1082 [BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
,
1083 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
],
1084 ["Payment(node=#1, payee=Person<'person1'>, payee_key='person1', "
1085 + "amount=123, state=Paid, paid=2020-05-01, "
1086 + "submitted=None)"])
1089 person1 = { amount = 123, paid = 2020-05-01T00:00:00 }
1091 [BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
,
1092 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
],
1093 ["Payment(node=#1, payee=Person<'person1'>, payee_key='person1', "
1094 + "amount=123, state=Paid, paid=2020-05-01 00:00:00, "
1095 + "submitted=None)"])
1098 person1 = { amount = 123, paid = 2020-05-01T00:00:00Z }
1100 [BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
,
1101 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
],
1102 ["Payment(node=#1, payee=Person<'person1'>, payee_key='person1', "
1103 + "amount=123, state=Paid, paid=2020-05-01 00:00:00+00:00, "
1104 + "submitted=None)"])
1107 person1 = { amount = 123, paid = 2020-05-01T00:00:00-07:23 }
1109 [BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
,
1110 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
],
1111 ["Payment(node=#1, payee=Person<'person1'>, payee_key='person1', "
1112 + "amount=123, state=Paid, paid=2020-05-01 00:00:00-07:23, "
1113 + "submitted=None)"])
1118 submitted = 2020-05-23
1121 [BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
,
1122 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
],
1123 ["Payment(node=#1, payee=Person<'person1'>, payee_key='person1', "
1124 + "amount=123, state=Paid, paid=2020-05-01, "
1125 + "submitted=2020-05-23)"])
1130 submitted = 2020-05-23
1131 paid = 2020-05-01T00:00:00
1133 [BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
,
1134 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
],
1135 ["Payment(node=#1, payee=Person<'person1'>, payee_key='person1', "
1136 + "amount=123, state=Paid, paid=2020-05-01 00:00:00, "
1137 + "submitted=2020-05-23)"])
1142 submitted = 2020-05-23
1143 paid = 2020-05-01T00:00:00Z
1145 [BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
,
1146 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
],
1147 ["Payment(node=#1, payee=Person<'person1'>, payee_key='person1', "
1148 + "amount=123, state=Paid, paid=2020-05-01 00:00:00+00:00, "
1149 + "submitted=2020-05-23)"])
1154 submitted = 2020-05-23
1155 paid = 2020-05-01T00:00:00-07:23
1157 [BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
,
1158 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
],
1159 ["Payment(node=#1, payee=Person<'person1'>, payee_key='person1', "
1160 + "amount=123, state=Paid, paid=2020-05-01 00:00:00-07:23, "
1161 + "submitted=2020-05-23)"])
1163 def test_payees_money_mismatch(self
):
1166 cf_budget_parent
=None,
1168 cf_total_budget
="10",
1169 cf_nlnet_milestone
="milestone 1",
1170 cf_payees_list
="person1 = 5\nperson2 = 10",
1173 errors
= bg
.get_errors()
1174 self
.assertErrorTypesMatches(errors
,
1175 [BudgetGraphPayeesMoneyMismatch
])
1176 self
.assertEqual(errors
[0].bug_id
, 1)
1177 self
.assertEqual(errors
[0].root_bug_id
, 1)
1178 self
.assertEqual(errors
[0].payees_total
, 15)
1180 def test_payees_parse_error(self
):
1181 def check_parse_error(cf_payees_list
, expected_msg
):
1182 errors
= BudgetGraph([
1184 cf_budget_parent
=None,
1186 cf_total_budget
="0",
1187 cf_nlnet_milestone
="milestone 1",
1188 cf_payees_list
=cf_payees_list
,
1190 ], EXAMPLE_CONFIG
).get_errors()
1191 self
.assertErrorTypesMatches(errors
,
1192 [BudgetGraphPayeesParseError
])
1193 self
.assertEqual(errors
[0].bug_id
, 1)
1194 self
.assertEqual(errors
[0].msg
, expected_msg
)
1196 check_parse_error("""
1199 "value for key 'payee 1' is invalid -- it should "
1200 "either be a monetary value or a table")
1202 check_parse_error("""
1205 "failed to parse monetary amount for key 'payee': "
1206 "invalid Money string: characters after sign and "
1207 "before first `.` must be ascii digits")
1209 check_parse_error("""
1213 "TOML parse error: Duplicate keys! (line 3"
1214 " column 1 char 39)")
1216 check_parse_error("""
1219 "failed to parse monetary amount for key 'payee': "
1220 "monetary amount is not a string or integer (to "
1221 "use fractional amounts such as 123.45, write "
1222 "\"123.45\"): 123.45")
1224 check_parse_error("""
1227 "value for key 'payee' is missing the `amount` "
1228 "field which is required")
1230 check_parse_error("""
1231 payee = { amount = 123.45 }
1233 "failed to parse monetary amount for key 'payee': "
1234 "monetary amount is not a string or integer (to "
1235 "use fractional amounts such as 123.45, write "
1236 "\"123.45\"): 123.45")
1238 check_parse_error("""
1239 payee = { amount = 123, blah = false }
1241 "value for key 'payee' has an unknown field: `blah`")
1243 check_parse_error("""
1244 payee = { amount = 123, submitted = false }
1246 "failed to parse `submitted` field for key "
1247 "'payee': invalid date: false")
1249 check_parse_error("""
1250 payee = { amount = 123, submitted = 123 }
1252 "failed to parse `submitted` field for key 'payee':"
1253 " invalid date: 123")
1257 payee = { amount = 123, paid = 2020-01-01, submitted = "abc" }
1259 "failed to parse `submitted` field for key 'payee': "
1260 "invalid date: 'abc'")
1264 payee = { amount = 123, paid = 12:34:56 }
1266 "failed to parse `paid` field for key 'payee': just a time of "
1267 "day by itself is not enough, a date must be included: 12:34:56")
1271 payee = { amount = 123, submitted = 12:34:56.123456 }
1273 "failed to parse `submitted` field for key 'payee': just a time "
1274 "of day by itself is not enough, a date must be included: "
1277 def test_negative_payee_money(self
):
1280 cf_budget_parent
=None,
1282 cf_total_budget
="10",
1283 cf_nlnet_milestone
="milestone 1",
1284 cf_payees_list
="""person1 = -10""",
1287 errors
= bg
.get_errors()
1288 self
.assertErrorTypesMatches(errors
,
1289 [BudgetGraphNegativePayeeMoney
,
1290 BudgetGraphPayeesMoneyMismatch
])
1291 self
.assertEqual(errors
[0].bug_id
, 1)
1292 self
.assertEqual(errors
[0].root_bug_id
, 1)
1293 self
.assertEqual(errors
[0].payee_key
, "person1")
1294 self
.assertEqual(errors
[1].bug_id
, 1)
1295 self
.assertEqual(errors
[1].root_bug_id
, 1)
1296 self
.assertEqual(errors
[1].payees_total
, -10)
1298 def test_duplicate_payments(self
):
1301 cf_budget_parent
=None,
1303 cf_total_budget
="10",
1304 cf_nlnet_milestone
="milestone 1",
1311 errors
= bg
.get_errors()
1312 self
.assertErrorTypesMatches(errors
, [])
1313 person1
= EXAMPLE_CONFIG
.people
["person1"]
1314 person2
= EXAMPLE_CONFIG
.people
["person2"]
1315 person3
= EXAMPLE_CONFIG
.people
["person3"]
1316 milestone1
= EXAMPLE_CONFIG
.milestones
["milestone 1"]
1317 milestone2
= EXAMPLE_CONFIG
.milestones
["milestone 2"]
1318 node1
: Node
= bg
.nodes
[1]
1319 node1_payment_person1
= node1
.payments
["person1"]
1320 node1_payment_alias1
= node1
.payments
["alias1"]
1321 self
.assertEqual(bg
.payments
, {
1323 milestone1
: [node1_payment_person1
, node1_payment_alias1
],
1326 person2
: {milestone1
: [], milestone2
: []},
1327 person3
: {milestone1
: [], milestone2
: []},
1330 repr(node1
.payment_summaries
),
1331 "{Person(config=..., identifier='person1', "
1332 "full_name='Person One', "
1333 "aliases=OrderedSet(['person1_alias1', 'alias1']), email=None): "
1334 "PaymentSummary(total=10, total_paid=0, total_submitted=0, "
1335 "submitted_date=None, paid_date=None, "
1336 "state=PaymentSummaryState.NotYetSubmitted, "
1337 "payments=(Payment(node=#1, payee=Person<'person1'>, "
1338 "payee_key='person1', amount=5, state=NotYetSubmitted, "
1339 "paid=None, submitted=None), Payment(node=#1, "
1340 "payee=Person<'person1'>, payee_key='alias1', amount=5, "
1341 "state=NotYetSubmitted, paid=None, submitted=None)))}")
1343 def test_incorrect_root_for_milestone(self
):
1346 cf_budget_parent
=None,
1348 cf_total_budget
="10",
1349 cf_nlnet_milestone
="milestone 2",
1353 errors
= bg
.get_errors()
1354 self
.assertErrorTypesMatches(errors
,
1355 [BudgetGraphIncorrectRootForMilestone
])
1356 self
.assertEqual(errors
[0].bug_id
, 1)
1357 self
.assertEqual(errors
[0].root_bug_id
, 1)
1358 self
.assertEqual(errors
[0].milestone
, "milestone 2")
1359 self
.assertEqual(errors
[0].milestone_canonical_bug_id
, 2)
1362 cf_budget_parent
=None,
1364 cf_total_budget
="0",
1365 cf_nlnet_milestone
="milestone 2",
1369 errors
= bg
.get_errors()
1370 self
.assertErrorTypesMatches(errors
, [])
1372 def test_payments(self
):
1375 cf_budget_parent
=None,
1377 cf_total_budget
="10",
1378 cf_nlnet_milestone
="milestone 1",
1379 cf_payees_list
="person1 = 3\nperson2 = 7",
1382 cf_budget_parent
=None,
1384 cf_total_budget
="10",
1385 cf_nlnet_milestone
="milestone 2",
1386 cf_payees_list
="person3 = 5\nperson2 = 5",
1389 self
.assertErrorTypesMatches(bg
.get_errors(), [])
1390 person1
= EXAMPLE_CONFIG
.people
["person1"]
1391 person2
= EXAMPLE_CONFIG
.people
["person2"]
1392 person3
= EXAMPLE_CONFIG
.people
["person3"]
1393 milestone1
= EXAMPLE_CONFIG
.milestones
["milestone 1"]
1394 milestone2
= EXAMPLE_CONFIG
.milestones
["milestone 2"]
1395 node1
: Node
= bg
.nodes
[1]
1396 node2
: Node
= bg
.nodes
[2]
1397 node1_payment_person1
= node1
.payments
["person1"]
1398 node1_payment_person2
= node1
.payments
["person2"]
1399 node2_payment_person2
= node2
.payments
["person2"]
1400 node2_payment_person3
= node2
.payments
["person3"]
1401 self
.assertEqual(bg
.payments
,
1404 milestone1
: [node1_payment_person1
],
1408 milestone1
: [node1_payment_person2
],
1409 milestone2
: [node2_payment_person2
],
1413 milestone2
: [node2_payment_person3
],
1417 def test_status(self
):
1418 bg
= BudgetGraph([MockBug(bug_id
=1, status
="blah")],
1420 errors
= bg
.get_errors()
1421 self
.assertErrorTypesMatches(errors
,
1422 [BudgetGraphUnknownStatus
])
1423 self
.assertEqual(errors
[0].bug_id
, 1)
1424 self
.assertEqual(errors
[0].status_str
, "blah")
1425 for status
in BugStatus
:
1426 bg
= BudgetGraph([MockBug(bug_id
=1, status
=status
)],
1428 self
.assertErrorTypesMatches(bg
.get_errors(), [])
1429 self
.assertEqual(bg
.nodes
[1].status
, status
)
1431 def test_assignee(self
):
1432 bg
= BudgetGraph([MockBug(bug_id
=1, assigned_to
="blah")],
1434 errors
= bg
.get_errors()
1435 self
.assertErrorTypesMatches(errors
,
1436 [BudgetGraphUnknownAssignee
])
1437 self
.assertEqual(errors
[0].bug_id
, 1)
1438 self
.assertEqual(errors
[0].assignee
, "blah")
1439 bg
= BudgetGraph([MockBug(bug_id
=1,
1440 assigned_to
="person2@example.com")],
1442 self
.assertErrorTypesMatches(bg
.get_errors(), [])
1443 self
.assertEqual(bg
.nodes
[1].assignee
,
1444 EXAMPLE_CONFIG
.people
["person2"])
1447 if __name__
== "__main__":