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', is_in_nlnet_mou=False, "
190 "milestone=Milestone(config=..., identifier='milestone 1', "
191 "canonical_bug_id=1), immediate_children=[#2], payments=[], "
192 "status=BugStatus.CONFIRMED, assignee=Person<'person3'>, "
193 "resolved_payments={}, payment_summaries={}), Node(graph=..., "
194 "id=#2, root=#1, parent=#1, budget_excluding_subtasks=10, "
195 "budget_including_subtasks=10, "
196 "fixed_budget_excluding_subtasks=10, "
197 "fixed_budget_including_subtasks=10, milestone_str='milestone "
198 "1', is_in_nlnet_mou=True, "
199 "milestone=Milestone(config=..., identifier='milestone 1', "
200 "canonical_bug_id=1), immediate_children=[], payments=[], "
201 "status=BugStatus.CONFIRMED, assignee=Person<'person3'>, "
202 "resolved_payments={}, payment_summaries={})], roots=[#1], "
203 "assigned_nodes={Person(config=..., identifier='person1', "
204 "full_name='Person One', "
205 "aliases=OrderedSet(['person1_alias1', 'alias1']), email=None): "
206 "[], Person(config=..., identifier='person2', "
207 "full_name='Person Two', "
208 "aliases=OrderedSet(['person1_alias2', 'alias2', 'person 2']), "
209 "email='person2@example.com'): [], Person(config=..., "
210 "identifier='person3', full_name='Person Three', "
211 "aliases=OrderedSet(), email='user@example.com'): [#1, #2]}, "
212 "assigned_nodes_for_milestones={Milestone(config=..., "
213 "identifier='milestone 1', canonical_bug_id=1): [#1, #2], "
214 "Milestone(config=..., identifier='milestone 2', "
215 "canonical_bug_id=2): []}, "
216 "milestone_payments={Milestone(config=..., identifier='milestone "
217 "1', canonical_bug_id=1): [], Milestone(config=..., "
218 "identifier='milestone 2', canonical_bug_id=2): []}, "
219 "payments={Person(config=..., identifier='person1', "
220 "full_name='Person One', "
221 "aliases=OrderedSet(['person1_alias1', 'alias1']), email=None): "
222 "{Milestone(config=..., identifier='milestone 1', "
223 "canonical_bug_id=1): [], Milestone(config=..., "
224 "identifier='milestone 2', canonical_bug_id=2): []}, "
225 "Person(config=..., identifier='person2', "
226 "full_name='Person Two', "
227 "aliases=OrderedSet(['person1_alias2', 'alias2', 'person 2']), "
228 "email='person2@example.com'): {Milestone(config=..., "
229 "identifier='milestone 1', canonical_bug_id=1): [], "
230 "Milestone(config=..., identifier='milestone 2', "
231 "canonical_bug_id=2): []}, Person(config=..., "
232 "identifier='person3', full_name='Person Three', "
233 "aliases=OrderedSet(), email='user@example.com'): "
234 "{Milestone(config=..., identifier='milestone 1', "
235 "canonical_bug_id=1): [], Milestone(config=..., "
236 "identifier='milestone 2', canonical_bug_id=2): []}}, "
237 "milestone_people={Milestone(config=..., identifier='milestone "
238 "1', canonical_bug_id=1): OrderedSet(), Milestone(config=..., "
239 "identifier='milestone 2', canonical_bug_id=2): OrderedSet()}}")
240 bg
= BudgetGraph([MockBug(bug_id
=1, status
="blah",
241 assigned_to
="unknown@example.com")],
245 "BudgetGraph{nodes=[Node(graph=..., id=#1, root=#1, parent=None, "
246 "budget_excluding_subtasks=0, budget_including_subtasks=0, "
247 "fixed_budget_excluding_subtasks=0, "
248 "fixed_budget_including_subtasks=0, milestone_str=None, "
249 "is_in_nlnet_mou=False, "
250 "milestone=None, immediate_children=[], payments=[], "
251 "status=<unknown status: 'blah'>, assignee=<unknown assignee: "
252 "'unknown@example.com'>, resolved_payments={}, "
253 "payment_summaries={})], roots=[#1], assigned_nodes=<failed>, "
254 "assigned_nodes_for_milestones={Milestone(config=..., "
255 "identifier='milestone 1', canonical_bug_id=1): [], "
256 "Milestone(config=..., identifier='milestone 2', "
257 "canonical_bug_id=2): []}, "
258 "milestone_payments={Milestone(config=..., "
259 "identifier='milestone 1', canonical_bug_id=1): [], "
260 "Milestone(config=..., identifier='milestone 2', "
261 "canonical_bug_id=2): []}, payments={Person(config=..., "
262 "identifier='person1', full_name='Person One', "
263 "aliases=OrderedSet(['person1_alias1', 'alias1']), email=None): "
264 "{Milestone(config=..., identifier='milestone 1', "
265 "canonical_bug_id=1): [], Milestone(config=..., "
266 "identifier='milestone 2', canonical_bug_id=2): []}, "
267 "Person(config=..., identifier='person2', "
268 "full_name='Person Two', "
269 "aliases=OrderedSet(['person1_alias2', 'alias2', "
270 "'person 2']), email='person2@example.com'): "
271 "{Milestone(config=..., identifier='milestone 1', "
272 "canonical_bug_id=1): [], Milestone(config=..., "
273 "identifier='milestone 2', canonical_bug_id=2): []}, "
274 "Person(config=..., identifier='person3', "
275 "full_name='Person Three', aliases=OrderedSet(), "
276 "email='user@example.com'): {Milestone(config=..., "
277 "identifier='milestone 1', canonical_bug_id=1): [], "
278 "Milestone(config=..., identifier='milestone 2', "
279 "canonical_bug_id=2): []}}, "
280 "milestone_people={Milestone(config=..., identifier='milestone "
281 "1', canonical_bug_id=1): OrderedSet(), Milestone(config=..., "
282 "identifier='milestone 2', canonical_bug_id=2): OrderedSet()}}")
283 bg
= BudgetGraph([MockBug(bug_id
=1, status
="blah",
284 assigned_to
="unknown@example.com",
286 person1 = {paid=2020-03-15,amount=5}
287 alias1 = {paid=2020-03-15,amount=10}
288 person2 = {submitted=2020-03-15,amount=15}
289 alias2 = {paid=2020-03-16,amount=23}
294 "BudgetGraph{nodes=[Node(graph=..., id=#1, root=#1, parent=None, "
295 "budget_excluding_subtasks=0, budget_including_subtasks=0, "
296 "fixed_budget_excluding_subtasks=0, "
297 "fixed_budget_including_subtasks=0, milestone_str=None, "
298 "is_in_nlnet_mou=False, "
299 "milestone=None, immediate_children=[], "
300 "payments=[Payment(node=#1, payee=Person<'person1'>, "
301 "payee_key='person1', amount=5, state=Paid, paid=2020-03-15, "
302 "submitted=None), Payment(node=#1, payee=Person<'person1'>, "
303 "payee_key='alias1', amount=10, state=Paid, paid=2020-03-15, "
304 "submitted=None), Payment(node=#1, payee=Person<'person2'>, "
305 "payee_key='person2', amount=15, state=Submitted, paid=None, "
306 "submitted=2020-03-15), Payment(node=#1, "
307 "payee=Person<'person2'>, payee_key='alias2', amount=23, "
308 "state=Paid, paid=2020-03-16, submitted=None)], status=<unknown "
309 "status: 'blah'>, assignee=<unknown assignee: "
310 "'unknown@example.com'>, resolved_payments={Person(config=..., "
311 "identifier='person1', full_name='Person One', "
312 "aliases=OrderedSet(['person1_alias1', 'alias1']), email=None): "
313 "[Payment(node=#1, payee=Person<'person1'>, payee_key='person1', "
314 "amount=5, state=Paid, paid=2020-03-15, submitted=None), "
315 "Payment(node=#1, payee=Person<'person1'>, payee_key='alias1', "
316 "amount=10, state=Paid, paid=2020-03-15, submitted=None)], "
317 "Person(config=..., identifier='person2', "
318 "full_name='Person Two', "
319 "aliases=OrderedSet(['person1_alias2', 'alias2', 'person 2']), "
320 "email='person2@example.com'): [Payment(node=#1, "
321 "payee=Person<'person2'>, payee_key='person2', amount=15, "
322 "state=Submitted, paid=None, submitted=2020-03-15), "
323 "Payment(node=#1, payee=Person<'person2'>, payee_key='alias2', "
324 "amount=23, state=Paid, paid=2020-03-16, submitted=None)]}, "
325 "payment_summaries={Person(config=..., identifier='person1', "
326 "full_name='Person One', "
327 "aliases=OrderedSet(['person1_alias1', 'alias1']), email=None): "
328 "PaymentSummary(total=15, total_paid=15, total_submitted=15, "
329 "submitted_date=None, paid_date=2020-03-15, "
330 "state=PaymentSummaryState.Paid, payments=(Payment(node=#1, "
331 "payee=Person<'person1'>, payee_key='person1', amount=5, "
332 "state=Paid, paid=2020-03-15, submitted=None), Payment(node=#1, "
333 "payee=Person<'person1'>, payee_key='alias1', amount=10, "
334 "state=Paid, paid=2020-03-15, submitted=None))), "
335 "Person(config=..., identifier='person2', "
336 "full_name='Person Two', "
337 "aliases=OrderedSet(['person1_alias2', 'alias2', 'person 2']), "
338 "email='person2@example.com'): PaymentSummary(total=38, "
339 "total_paid=23, total_submitted=38, submitted_date=None, "
340 "paid_date=None, state=PaymentSummaryState.Inconsistent, "
341 "payments=(Payment(node=#1, payee=Person<'person2'>, "
342 "payee_key='person2', amount=15, state=Submitted, paid=None, "
343 "submitted=2020-03-15), Payment(node=#1, "
344 "payee=Person<'person2'>, payee_key='alias2', amount=23, "
345 "state=Paid, paid=2020-03-16, submitted=None)))})], roots=[#1], "
346 "assigned_nodes=<failed>, "
347 "assigned_nodes_for_milestones={Milestone(config=..., "
348 "identifier='milestone 1', canonical_bug_id=1): [], "
349 "Milestone(config=..., identifier='milestone 2', "
350 "canonical_bug_id=2): []}, "
351 "milestone_payments={Milestone(config=..., identifier='milestone "
352 "1', canonical_bug_id=1): [], Milestone(config=..., "
353 "identifier='milestone 2', canonical_bug_id=2): []}, "
354 "payments={Person(config=..., identifier='person1', "
355 "full_name='Person One', "
356 "aliases=OrderedSet(['person1_alias1', 'alias1']), email=None): "
357 "{Milestone(config=..., identifier='milestone 1', "
358 "canonical_bug_id=1): [], Milestone(config=..., "
359 "identifier='milestone 2', canonical_bug_id=2): []}, "
360 "Person(config=..., identifier='person2', "
361 "full_name='Person Two', "
362 "aliases=OrderedSet(['person1_alias2', 'alias2', 'person 2']), "
363 "email='person2@example.com'): {Milestone(config=..., "
364 "identifier='milestone 1', canonical_bug_id=1): [], "
365 "Milestone(config=..., identifier='milestone 2', "
366 "canonical_bug_id=2): []}, Person(config=..., "
367 "identifier='person3', full_name='Person Three', "
368 "aliases=OrderedSet(), email='user@example.com'): "
369 "{Milestone(config=..., identifier='milestone 1', "
370 "canonical_bug_id=1): [], Milestone(config=..., "
371 "identifier='milestone 2', canonical_bug_id=2): []}}, "
372 "milestone_people={Milestone(config=..., identifier='milestone "
373 "1', canonical_bug_id=1): OrderedSet(), Milestone(config=..., "
374 "identifier='milestone 2', canonical_bug_id=2): OrderedSet()}}")
376 def test_empty(self
):
377 bg
= BudgetGraph([], EXAMPLE_CONFIG
)
378 self
.assertEqual(len(bg
.nodes
), 0)
379 self
.assertEqual(len(bg
.roots
), 0)
380 self
.assertIs(bg
.config
, EXAMPLE_CONFIG
)
382 def test_single(self
):
383 bg
= BudgetGraph([EXAMPLE_BUG1
], EXAMPLE_CONFIG
)
384 self
.assertEqual(len(bg
.nodes
), 1)
385 node
: Node
= bg
.nodes
[1]
386 self
.assertEqual(bg
.roots
, {node}
)
387 self
.assertIsInstance(node
, Node
)
388 self
.assertIs(node
.graph
, bg
)
389 self
.assertIs(node
.bug
, EXAMPLE_BUG1
)
390 self
.assertIs(node
.root
, node
)
391 self
.assertIsNone(node
.parent_id
)
392 self
.assertEqual(node
.immediate_children
, set())
393 self
.assertEqual(node
.bug_url
,
394 "https://bugzilla.example.com/show_bug.cgi?id=1")
395 self
.assertEqual(node
.budget_excluding_subtasks
, Money(cents
=0))
396 self
.assertEqual(node
.budget_including_subtasks
, Money(cents
=0))
397 self
.assertIsNone(node
.milestone
)
398 self
.assertEqual(node
.payments
, {})
400 def test_loop1(self
):
401 with self
.assertRaises(BudgetGraphLoopError
) as cm
:
402 BudgetGraph([EXAMPLE_LOOP1_BUG1
], EXAMPLE_CONFIG
).roots
403 self
.assertEqual(cm
.exception
.bug_ids
, [1])
405 def test_loop2(self
):
406 with self
.assertRaises(BudgetGraphLoopError
) as cm
:
407 BudgetGraph([EXAMPLE_LOOP2_BUG1
, EXAMPLE_LOOP2_BUG2
],
408 EXAMPLE_CONFIG
).roots
409 self
.assertEqual(cm
.exception
.bug_ids
, [2, 1])
411 def test_parent_child(self
):
412 bg
= BudgetGraph([EXAMPLE_PARENT_BUG1
, EXAMPLE_CHILD_BUG2
],
414 self
.assertEqual(len(bg
.nodes
), 2)
415 node1
: Node
= bg
.nodes
[1]
416 node2
: Node
= bg
.nodes
[2]
417 self
.assertEqual(bg
.roots
, {node1}
)
418 self
.assertEqual(node1
, node1
)
419 self
.assertEqual(node2
, node2
)
420 self
.assertNotEqual(node1
, node2
)
421 self
.assertNotEqual(node2
, node1
)
422 self
.assertIsInstance(node1
, Node
)
423 self
.assertIs(node1
.graph
, bg
)
424 self
.assertIs(node1
.bug
, EXAMPLE_PARENT_BUG1
)
425 self
.assertIsNone(node1
.parent_id
)
426 self
.assertEqual(node1
.root
, node1
)
427 self
.assertEqual(node1
.immediate_children
, {node2}
)
428 self
.assertEqual(node1
.budget_excluding_subtasks
, Money(cents
=1000))
429 self
.assertEqual(node1
.budget_including_subtasks
, Money(cents
=2000))
430 self
.assertEqual(node1
.milestone_str
, "milestone 1")
431 self
.assertEqual(node1
.bug_url
,
432 "https://bugzilla.example.com/show_bug.cgi?id=1")
433 self
.assertEqual(list(node1
.children()), [node2
])
434 self
.assertEqual(list(node1
.children_breadth_first()), [node2
])
435 self
.assertEqual(node1
.payments
, {})
436 self
.assertIsInstance(node2
, Node
)
437 self
.assertIs(node2
.graph
, bg
)
438 self
.assertIs(node2
.bug
, EXAMPLE_CHILD_BUG2
)
439 self
.assertEqual(node2
.parent_id
, 1)
440 self
.assertEqual(node2
.root
, node1
)
441 self
.assertEqual(node2
.immediate_children
, set())
442 self
.assertEqual(node2
.budget_excluding_subtasks
, Money(cents
=1000))
443 self
.assertEqual(node2
.budget_including_subtasks
, Money(cents
=1000))
444 self
.assertEqual(node2
.milestone_str
, "milestone 1")
445 self
.assertEqual(node2
.payments
, {})
446 self
.assertEqual(node2
.bug_url
,
447 "https://bugzilla.example.com/show_bug.cgi?id=2")
449 def test_children(self
):
452 cf_budget_parent
=None,
455 cf_nlnet_milestone
=None,
462 cf_nlnet_milestone
=None,
469 cf_nlnet_milestone
=None,
476 cf_nlnet_milestone
=None,
483 cf_nlnet_milestone
=None,
490 cf_nlnet_milestone
=None,
497 cf_nlnet_milestone
=None,
501 self
.assertEqual(len(bg
.nodes
), 7)
502 node1
: Node
= bg
.nodes
[1]
503 node2
: Node
= bg
.nodes
[2]
504 node3
: Node
= bg
.nodes
[3]
505 node4
: Node
= bg
.nodes
[4]
506 node5
: Node
= bg
.nodes
[5]
507 node6
: Node
= bg
.nodes
[6]
508 node7
: Node
= bg
.nodes
[7]
509 self
.assertEqual(bg
.roots
, {node1}
)
510 self
.assertEqual(list(node1
.children()),
511 [node2
, node3
, node5
, node7
, node6
, node4
])
512 self
.assertEqual(list(node1
.children_breadth_first()),
513 [node2
, node3
, node4
, node5
, node6
, node7
])
515 def test_money_with_no_milestone(self
):
518 cf_budget_parent
=None,
520 cf_total_budget
="10",
521 cf_nlnet_milestone
=None,
525 errors
= bg
.get_errors()
526 self
.assertErrorTypesMatches(errors
, [
527 BudgetGraphMoneyWithNoMilestone
,
528 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
])
529 self
.assertEqual(errors
[0].bug_id
, 1)
530 self
.assertEqual(errors
[0].root_bug_id
, 1)
533 cf_budget_parent
=None,
536 cf_nlnet_milestone
=None,
540 errors
= bg
.get_errors()
541 self
.assertErrorTypesMatches(errors
, [
542 BudgetGraphMoneyWithNoMilestone
,
543 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
])
544 self
.assertEqual(errors
[0].bug_id
, 1)
545 self
.assertEqual(errors
[0].root_bug_id
, 1)
548 cf_budget_parent
=None,
550 cf_total_budget
="10",
551 cf_nlnet_milestone
=None,
555 errors
= bg
.get_errors()
556 self
.assertErrorTypesMatches(errors
, [BudgetGraphMoneyWithNoMilestone
])
557 self
.assertEqual(errors
[0].bug_id
, 1)
558 self
.assertEqual(errors
[0].root_bug_id
, 1)
560 def test_money_mismatch(self
):
561 def helper(budget
, total_budget
, payees_list
, child_budget
,
562 expected_errors
, expected_fixed_error_types
=None):
563 if expected_fixed_error_types
is None:
564 expected_fixed_error_types
= []
567 cf_budget_parent
=None,
569 cf_total_budget
=total_budget
,
570 cf_nlnet_milestone
="milestone 1",
571 cf_payees_list
=payees_list
,
575 cf_budget
=child_budget
,
576 cf_total_budget
=child_budget
,
577 cf_nlnet_milestone
="milestone 1",
580 node1
: Node
= bg
.nodes
[1]
581 errors
= bg
.get_errors()
582 self
.assertErrorTypesMatches(errors
,
583 [type(i
) for i
in expected_errors
])
584 self
.assertEqual([str(i
) for i
in errors
],
585 [str(i
) for i
in expected_errors
])
588 cf_budget_parent
=None,
589 cf_budget
=str(node1
.fixed_budget_excluding_subtasks
),
591 node1
.fixed_budget_including_subtasks
),
592 cf_nlnet_milestone
="milestone 1",
593 cf_payees_list
=payees_list
,
597 cf_budget
=child_budget
,
598 cf_total_budget
=child_budget
,
599 cf_nlnet_milestone
="milestone 1",
603 errors
= bg
.get_errors()
604 self
.assertErrorTypesMatches(errors
,
605 expected_fixed_error_types
)
616 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
621 payees_list
="person1=1",
624 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks(
626 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
631 payees_list
="person1=1",
634 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks(
636 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
641 payees_list
="person1=10",
644 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks(
646 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
651 payees_list
="person1=10",
654 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks(
656 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
664 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks(
672 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks(
677 payees_list
="person1=1",
680 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks(
682 BudgetGraphPayeesMoneyMismatch(1, 1, Money(1), Money(100)),
684 expected_fixed_error_types
=[BudgetGraphPayeesMoneyMismatch
])
687 payees_list
="person1=1",
690 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks(
692 BudgetGraphPayeesMoneyMismatch(1, 1, Money(1), Money(95)),
694 expected_fixed_error_types
=[BudgetGraphPayeesMoneyMismatch
])
697 payees_list
="person1=10",
700 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks(
702 BudgetGraphPayeesMoneyMismatch(1, 1, Money(10), Money(100)),
704 expected_fixed_error_types
=[BudgetGraphPayeesMoneyMismatch
])
707 payees_list
="person1=10",
710 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks(
712 BudgetGraphPayeesMoneyMismatch(1, 1, Money(10), Money(95)),
714 expected_fixed_error_types
=[BudgetGraphPayeesMoneyMismatch
])
722 payees_list
="person1=1",
725 BudgetGraphPayeesMoneyMismatch(1, 1, Money(1), Money(0)),
727 expected_fixed_error_types
=[BudgetGraphPayeesMoneyMismatch
])
730 payees_list
="person1=10",
733 BudgetGraphPayeesMoneyMismatch(1, 1, Money(10), Money(0)),
735 expected_fixed_error_types
=[BudgetGraphPayeesMoneyMismatch
])
741 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
749 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
754 payees_list
="person1=1",
757 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
759 BudgetGraphPayeesMoneyMismatch(1, 1, Money(1), Money(10)),
761 expected_fixed_error_types
=[BudgetGraphPayeesMoneyMismatch
])
764 payees_list
="person1=1",
767 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
769 BudgetGraphPayeesMoneyMismatch(1, 1, Money(1), Money(10)),
771 expected_fixed_error_types
=[BudgetGraphPayeesMoneyMismatch
])
774 payees_list
="person1=10",
777 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
782 payees_list
="person1=10",
785 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
795 payees_list
="person1=1",
798 BudgetGraphPayeesMoneyMismatch(1, 1, Money(1), Money(10)),
800 expected_fixed_error_types
=[BudgetGraphPayeesMoneyMismatch
])
803 payees_list
="person1=10",
811 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks(
819 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks(
824 payees_list
="person1=1",
827 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
829 BudgetGraphPayeesMoneyMismatch(1, 1, Money(1), Money(10)),
831 expected_fixed_error_types
=[BudgetGraphPayeesMoneyMismatch
])
834 payees_list
="person1=1",
837 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
839 BudgetGraphPayeesMoneyMismatch(1, 1, Money(1), Money(10)),
841 expected_fixed_error_types
=[BudgetGraphPayeesMoneyMismatch
])
844 payees_list
="person1=10",
847 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
852 payees_list
="person1=10",
855 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
865 payees_list
="person1=1",
868 BudgetGraphPayeesMoneyMismatch(1, 1, Money(1), Money(10)),
870 expected_fixed_error_types
=[BudgetGraphPayeesMoneyMismatch
])
873 payees_list
="person1=10",
879 payees_list
="person1=10",
882 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks(
886 def test_negative_money(self
):
889 cf_budget_parent
=None,
891 cf_total_budget
="-10",
892 cf_nlnet_milestone
="milestone 1",
896 errors
= bg
.get_errors()
897 self
.assertErrorTypesMatches(errors
, [
898 BudgetGraphNegativeMoney
,
899 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
])
900 self
.assertEqual(errors
[0].bug_id
, 1)
901 self
.assertEqual(errors
[0].root_bug_id
, 1)
902 self
.assertEqual(errors
[1].bug_id
, 1)
903 self
.assertEqual(errors
[1].root_bug_id
, 1)
904 self
.assertEqual(errors
[1].expected_budget_including_subtasks
, 0)
907 cf_budget_parent
=None,
910 cf_nlnet_milestone
="milestone 1",
914 errors
= bg
.get_errors()
915 self
.assertErrorTypesMatches(errors
, [
916 BudgetGraphNegativeMoney
,
917 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
])
918 self
.assertEqual(errors
[0].bug_id
, 1)
919 self
.assertEqual(errors
[0].root_bug_id
, 1)
920 self
.assertEqual(errors
[1].bug_id
, 1)
921 self
.assertEqual(errors
[1].root_bug_id
, 1)
922 self
.assertEqual(errors
[1].expected_budget_including_subtasks
, -10)
925 cf_budget_parent
=None,
927 cf_total_budget
="-10",
928 cf_nlnet_milestone
="milestone 1",
932 errors
= bg
.get_errors()
933 self
.assertErrorTypesMatches(errors
,
934 [BudgetGraphNegativeMoney
])
935 self
.assertEqual(errors
[0].bug_id
, 1)
936 self
.assertEqual(errors
[0].root_bug_id
, 1)
938 def test_payees_parse(self
):
939 def check(cf_payees_list
, error_types
, expected_payments
):
940 bg
= BudgetGraph([MockBug(bug_id
=1,
941 cf_budget_parent
=None,
944 cf_nlnet_milestone
="milestone 1",
945 cf_payees_list
=cf_payees_list
,
948 self
.assertErrorTypesMatches(bg
.get_errors(), error_types
)
949 self
.assertEqual(len(bg
.nodes
), 1)
950 node
: Node
= bg
.nodes
[1]
951 self
.assertEqual([str(i
) for i
in node
.payments
.values()],
958 [BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
,
959 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
],
960 ["Payment(node=#1, payee=Person<'person1'>, "
961 "payee_key='person1', amount=123, "
962 "state=NotYetSubmitted, paid=None, submitted=None)"])
967 [BudgetGraphPayeesParseError
,
968 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
,
969 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
],
970 ["Payment(node=#1, payee=<unknown person>, payee_key='abc', "
971 "amount=123, state=NotYetSubmitted, paid=None, "
977 [BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
,
978 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
],
979 ["Payment(node=#1, payee=Person<'person1'>, "
980 "payee_key='person1', amount=123.45, "
981 "state=NotYetSubmitted, paid=None, submitted=None)"])
987 [BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
,
988 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
],
989 ["Payment(node=#1, payee=Person<'person1'>, payee_key='person1', "
990 'amount=123.45, state=NotYetSubmitted, paid=None, '
992 "Payment(node=#1, payee=Person<'person2'>, payee_key='person 2', "
993 'amount=21.35, state=NotYetSubmitted, paid=None, '
1000 [BudgetGraphPayeesParseError
,
1001 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
,
1002 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
],
1003 ["Payment(node=#1, payee=Person<'person1'>, payee_key='person1', "
1004 'amount=123.45, state=NotYetSubmitted, paid=None, '
1006 "Payment(node=#1, payee=<unknown person>, payee_key='d e f', "
1007 'amount=21.35, state=NotYetSubmitted, paid=None, '
1015 [BudgetGraphPayeesParseError
,
1016 BudgetGraphNegativePayeeMoney
,
1017 BudgetGraphPayeesParseError
,
1018 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
,
1019 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
],
1020 ["Payment(node=#1, payee=<unknown person>, payee_key='abc', "
1021 'amount=123.45, state=NotYetSubmitted, paid=None, '
1023 "Payment(node=#1, payee=<unknown person>, payee_key='AAA', "
1024 'amount=-21.35, state=NotYetSubmitted, paid=None, '
1028 "not-an-email@example.com" = "-2345"
1030 [BudgetGraphNegativePayeeMoney
,
1031 BudgetGraphPayeesParseError
,
1032 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
,
1033 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
],
1034 ['Payment(node=#1, payee=<unknown person>, '
1035 "payee_key='not-an-email@example.com', amount=-2345, "
1036 "state=NotYetSubmitted, paid=None, submitted=None)"])
1039 person1 = { amount = 123 }
1041 [BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
,
1042 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
],
1043 ["Payment(node=#1, payee=Person<'person1'>, payee_key='person1', "
1044 "amount=123, state=NotYetSubmitted, paid=None, submitted=None)"])
1047 person1 = { amount = 123, submitted = 2020-05-01 }
1049 [BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
,
1050 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
],
1051 ["Payment(node=#1, payee=Person<'person1'>, payee_key='person1', "
1052 + "amount=123, state=Submitted, paid=None, "
1053 + "submitted=2020-05-01)"])
1056 person1 = { amount = 123, submitted = 2020-05-01T00:00:00 }
1058 [BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
,
1059 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
],
1060 ["Payment(node=#1, payee=Person<'person1'>, payee_key='person1', "
1061 + "amount=123, state=Submitted, paid=None, "
1062 + "submitted=2020-05-01 00:00:00)"])
1065 person1 = { amount = 123, submitted = 2020-05-01T00:00:00Z }
1067 [BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
,
1068 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
],
1069 ["Payment(node=#1, payee=Person<'person1'>, payee_key='person1', "
1070 + "amount=123, state=Submitted, paid=None, "
1071 + "submitted=2020-05-01 00:00:00+00:00)"])
1074 person1 = { amount = 123, submitted = 2020-05-01T00:00:00-07:23 }
1076 [BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
,
1077 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
],
1078 ["Payment(node=#1, payee=Person<'person1'>, payee_key='person1', "
1079 + "amount=123, state=Submitted, paid=None, "
1080 + "submitted=2020-05-01 00:00:00-07:23)"])
1083 person1 = { amount = 123, paid = 2020-05-01 }
1085 [BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
,
1086 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
],
1087 ["Payment(node=#1, payee=Person<'person1'>, payee_key='person1', "
1088 + "amount=123, state=Paid, paid=2020-05-01, "
1089 + "submitted=None)"])
1092 person1 = { amount = 123, paid = 2020-05-01T00:00:00 }
1094 [BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
,
1095 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
],
1096 ["Payment(node=#1, payee=Person<'person1'>, payee_key='person1', "
1097 + "amount=123, state=Paid, paid=2020-05-01 00:00:00, "
1098 + "submitted=None)"])
1101 person1 = { amount = 123, paid = 2020-05-01T00:00:00Z }
1103 [BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
,
1104 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
],
1105 ["Payment(node=#1, payee=Person<'person1'>, payee_key='person1', "
1106 + "amount=123, state=Paid, paid=2020-05-01 00:00:00+00:00, "
1107 + "submitted=None)"])
1110 person1 = { amount = 123, paid = 2020-05-01T00:00:00-07:23 }
1112 [BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
,
1113 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
],
1114 ["Payment(node=#1, payee=Person<'person1'>, payee_key='person1', "
1115 + "amount=123, state=Paid, paid=2020-05-01 00:00:00-07:23, "
1116 + "submitted=None)"])
1121 submitted = 2020-05-23
1124 [BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
,
1125 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
],
1126 ["Payment(node=#1, payee=Person<'person1'>, payee_key='person1', "
1127 + "amount=123, state=Paid, paid=2020-05-01, "
1128 + "submitted=2020-05-23)"])
1133 submitted = 2020-05-23
1134 paid = 2020-05-01T00:00:00
1136 [BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
,
1137 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
],
1138 ["Payment(node=#1, payee=Person<'person1'>, payee_key='person1', "
1139 + "amount=123, state=Paid, paid=2020-05-01 00:00:00, "
1140 + "submitted=2020-05-23)"])
1145 submitted = 2020-05-23
1146 paid = 2020-05-01T00:00:00Z
1148 [BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
,
1149 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
],
1150 ["Payment(node=#1, payee=Person<'person1'>, payee_key='person1', "
1151 + "amount=123, state=Paid, paid=2020-05-01 00:00:00+00:00, "
1152 + "submitted=2020-05-23)"])
1157 submitted = 2020-05-23
1158 paid = 2020-05-01T00:00:00-07:23
1160 [BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
,
1161 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
],
1162 ["Payment(node=#1, payee=Person<'person1'>, payee_key='person1', "
1163 + "amount=123, state=Paid, paid=2020-05-01 00:00:00-07:23, "
1164 + "submitted=2020-05-23)"])
1166 def test_payees_money_mismatch(self
):
1169 cf_budget_parent
=None,
1171 cf_total_budget
="10",
1172 cf_nlnet_milestone
="milestone 1",
1173 cf_payees_list
="person1 = 5\nperson2 = 10",
1176 errors
= bg
.get_errors()
1177 self
.assertErrorTypesMatches(errors
,
1178 [BudgetGraphPayeesMoneyMismatch
])
1179 self
.assertEqual(errors
[0].bug_id
, 1)
1180 self
.assertEqual(errors
[0].root_bug_id
, 1)
1181 self
.assertEqual(errors
[0].payees_total
, 15)
1183 def test_payees_parse_error(self
):
1184 def check_parse_error(cf_payees_list
, expected_msg
):
1185 errors
= BudgetGraph([
1187 cf_budget_parent
=None,
1189 cf_total_budget
="0",
1190 cf_nlnet_milestone
="milestone 1",
1191 cf_payees_list
=cf_payees_list
,
1193 ], EXAMPLE_CONFIG
).get_errors()
1194 self
.assertErrorTypesMatches(errors
,
1195 [BudgetGraphPayeesParseError
])
1196 self
.assertEqual(errors
[0].bug_id
, 1)
1197 self
.assertEqual(errors
[0].msg
, expected_msg
)
1199 check_parse_error("""
1202 "value for key 'payee 1' is invalid -- it should "
1203 "either be a monetary value or a table")
1205 check_parse_error("""
1208 "failed to parse monetary amount for key 'payee': "
1209 "invalid Money string: characters after sign and "
1210 "before first `.` must be ascii digits")
1212 check_parse_error("""
1216 "TOML parse error: Duplicate keys! (line 3"
1217 " column 1 char 39)")
1219 check_parse_error("""
1222 "failed to parse monetary amount for key 'payee': "
1223 "monetary amount is not a string or integer (to "
1224 "use fractional amounts such as 123.45, write "
1225 "\"123.45\"): 123.45")
1227 check_parse_error("""
1230 "value for key 'payee' is missing the `amount` "
1231 "field which is required")
1233 check_parse_error("""
1234 payee = { amount = 123.45 }
1236 "failed to parse monetary amount for key 'payee': "
1237 "monetary amount is not a string or integer (to "
1238 "use fractional amounts such as 123.45, write "
1239 "\"123.45\"): 123.45")
1241 check_parse_error("""
1242 payee = { amount = 123, blah = false }
1244 "value for key 'payee' has an unknown field: `blah`")
1246 check_parse_error("""
1247 payee = { amount = 123, submitted = false }
1249 "failed to parse `submitted` field for key "
1250 "'payee': invalid date: false")
1252 check_parse_error("""
1253 payee = { amount = 123, submitted = 123 }
1255 "failed to parse `submitted` field for key 'payee':"
1256 " invalid date: 123")
1260 payee = { amount = 123, paid = 2020-01-01, submitted = "abc" }
1262 "failed to parse `submitted` field for key 'payee': "
1263 "invalid date: 'abc'")
1267 payee = { amount = 123, paid = 12:34:56 }
1269 "failed to parse `paid` field for key 'payee': just a time of "
1270 "day by itself is not enough, a date must be included: 12:34:56")
1274 payee = { amount = 123, submitted = 12:34:56.123456 }
1276 "failed to parse `submitted` field for key 'payee': just a time "
1277 "of day by itself is not enough, a date must be included: "
1280 def test_negative_payee_money(self
):
1283 cf_budget_parent
=None,
1285 cf_total_budget
="10",
1286 cf_nlnet_milestone
="milestone 1",
1287 cf_payees_list
="""person1 = -10""",
1290 errors
= bg
.get_errors()
1291 self
.assertErrorTypesMatches(errors
,
1292 [BudgetGraphNegativePayeeMoney
,
1293 BudgetGraphPayeesMoneyMismatch
])
1294 self
.assertEqual(errors
[0].bug_id
, 1)
1295 self
.assertEqual(errors
[0].root_bug_id
, 1)
1296 self
.assertEqual(errors
[0].payee_key
, "person1")
1297 self
.assertEqual(errors
[1].bug_id
, 1)
1298 self
.assertEqual(errors
[1].root_bug_id
, 1)
1299 self
.assertEqual(errors
[1].payees_total
, -10)
1301 def test_duplicate_payments(self
):
1304 cf_budget_parent
=None,
1306 cf_total_budget
="10",
1307 cf_nlnet_milestone
="milestone 1",
1314 errors
= bg
.get_errors()
1315 self
.assertErrorTypesMatches(errors
, [])
1316 person1
= EXAMPLE_CONFIG
.people
["person1"]
1317 person2
= EXAMPLE_CONFIG
.people
["person2"]
1318 person3
= EXAMPLE_CONFIG
.people
["person3"]
1319 milestone1
= EXAMPLE_CONFIG
.milestones
["milestone 1"]
1320 milestone2
= EXAMPLE_CONFIG
.milestones
["milestone 2"]
1321 node1
: Node
= bg
.nodes
[1]
1322 node1_payment_person1
= node1
.payments
["person1"]
1323 node1_payment_alias1
= node1
.payments
["alias1"]
1324 self
.assertEqual(bg
.payments
, {
1326 milestone1
: [node1_payment_person1
, node1_payment_alias1
],
1329 person2
: {milestone1
: [], milestone2
: []},
1330 person3
: {milestone1
: [], milestone2
: []},
1333 repr(node1
.payment_summaries
),
1334 "{Person(config=..., identifier='person1', "
1335 "full_name='Person One', "
1336 "aliases=OrderedSet(['person1_alias1', 'alias1']), email=None): "
1337 "PaymentSummary(total=10, total_paid=0, total_submitted=0, "
1338 "submitted_date=None, paid_date=None, "
1339 "state=PaymentSummaryState.NotYetSubmitted, "
1340 "payments=(Payment(node=#1, payee=Person<'person1'>, "
1341 "payee_key='person1', amount=5, state=NotYetSubmitted, "
1342 "paid=None, submitted=None), Payment(node=#1, "
1343 "payee=Person<'person1'>, payee_key='alias1', amount=5, "
1344 "state=NotYetSubmitted, paid=None, submitted=None)))}")
1346 def test_incorrect_root_for_milestone(self
):
1349 cf_budget_parent
=None,
1351 cf_total_budget
="10",
1352 cf_nlnet_milestone
="milestone 2",
1356 errors
= bg
.get_errors()
1357 self
.assertErrorTypesMatches(errors
,
1358 [BudgetGraphIncorrectRootForMilestone
])
1359 self
.assertEqual(errors
[0].bug_id
, 1)
1360 self
.assertEqual(errors
[0].root_bug_id
, 1)
1361 self
.assertEqual(errors
[0].milestone
, "milestone 2")
1362 self
.assertEqual(errors
[0].milestone_canonical_bug_id
, 2)
1365 cf_budget_parent
=None,
1367 cf_total_budget
="0",
1368 cf_nlnet_milestone
="milestone 2",
1372 errors
= bg
.get_errors()
1373 self
.assertErrorTypesMatches(errors
, [])
1375 def test_payments(self
):
1378 cf_budget_parent
=None,
1380 cf_total_budget
="10",
1381 cf_nlnet_milestone
="milestone 1",
1382 cf_payees_list
="person1 = 3\nperson2 = 7",
1385 cf_budget_parent
=None,
1387 cf_total_budget
="10",
1388 cf_nlnet_milestone
="milestone 2",
1389 cf_payees_list
="person3 = 5\nperson2 = 5",
1392 self
.assertErrorTypesMatches(bg
.get_errors(), [])
1393 person1
= EXAMPLE_CONFIG
.people
["person1"]
1394 person2
= EXAMPLE_CONFIG
.people
["person2"]
1395 person3
= EXAMPLE_CONFIG
.people
["person3"]
1396 milestone1
= EXAMPLE_CONFIG
.milestones
["milestone 1"]
1397 milestone2
= EXAMPLE_CONFIG
.milestones
["milestone 2"]
1398 node1
: Node
= bg
.nodes
[1]
1399 node2
: Node
= bg
.nodes
[2]
1400 node1_payment_person1
= node1
.payments
["person1"]
1401 node1_payment_person2
= node1
.payments
["person2"]
1402 node2_payment_person2
= node2
.payments
["person2"]
1403 node2_payment_person3
= node2
.payments
["person3"]
1404 self
.assertEqual(bg
.payments
,
1407 milestone1
: [node1_payment_person1
],
1411 milestone1
: [node1_payment_person2
],
1412 milestone2
: [node2_payment_person2
],
1416 milestone2
: [node2_payment_person3
],
1420 def test_status(self
):
1421 bg
= BudgetGraph([MockBug(bug_id
=1, status
="blah")],
1423 errors
= bg
.get_errors()
1424 self
.assertErrorTypesMatches(errors
,
1425 [BudgetGraphUnknownStatus
])
1426 self
.assertEqual(errors
[0].bug_id
, 1)
1427 self
.assertEqual(errors
[0].status_str
, "blah")
1428 for status
in BugStatus
:
1429 bg
= BudgetGraph([MockBug(bug_id
=1, status
=status
)],
1431 self
.assertErrorTypesMatches(bg
.get_errors(), [])
1432 self
.assertEqual(bg
.nodes
[1].status
, status
)
1434 def test_assignee(self
):
1435 bg
= BudgetGraph([MockBug(bug_id
=1, assigned_to
="blah")],
1437 errors
= bg
.get_errors()
1438 self
.assertErrorTypesMatches(errors
,
1439 [BudgetGraphUnknownAssignee
])
1440 self
.assertEqual(errors
[0].bug_id
, 1)
1441 self
.assertEqual(errors
[0].assignee
, "blah")
1442 bg
= BudgetGraph([MockBug(bug_id
=1,
1443 assigned_to
="person2@example.com")],
1445 self
.assertErrorTypesMatches(bg
.get_errors(), [])
1446 self
.assertEqual(bg
.nodes
[1].assignee
,
1447 EXAMPLE_CONFIG
.people
["person2"])
1449 def test_closest_bug_in_mou(self
):
1451 MockBug(bug_id
=1, cf_nlnet_milestone
="milestone 1"),
1452 MockBug(bug_id
=2, cf_budget_parent
=1,
1453 cf_nlnet_milestone
="milestone 1"),
1454 MockBug(bug_id
=3, cf_budget_parent
=2,
1455 cf_nlnet_milestone
="milestone 1"),
1456 MockBug(bug_id
=4, cf_budget_parent
=1,
1457 cf_nlnet_milestone
="milestone 1"),
1458 MockBug(bug_id
=5, cf_budget_parent
=4,
1459 cf_nlnet_milestone
="milestone 1"),
1461 MockBug(bug_id
=7, cf_nlnet_milestone
="bad milestone"),
1462 MockBug(bug_id
=8, cf_budget_parent
=7,
1463 cf_nlnet_milestone
="bad milestone"),
1465 errors
= bg
.get_errors()
1466 self
.assertErrorTypesMatches(errors
, [BudgetGraphUnknownMilestone
,
1467 BudgetGraphUnknownMilestone
])
1468 self
.assertEqual(bg
.nodes
[1].closest_bug_in_mou
, None)
1469 self
.assertEqual(bg
.nodes
[2].closest_bug_in_mou
, bg
.nodes
[2])
1470 self
.assertEqual(bg
.nodes
[3].closest_bug_in_mou
, bg
.nodes
[2])
1471 self
.assertEqual(bg
.nodes
[4].closest_bug_in_mou
, bg
.nodes
[4])
1472 self
.assertEqual(bg
.nodes
[5].closest_bug_in_mou
, bg
.nodes
[4])
1473 self
.assertEqual(bg
.nodes
[6].closest_bug_in_mou
, None)
1474 self
.assertEqual(bg
.nodes
[7].closest_bug_in_mou
, None)
1475 self
.assertEqual(bg
.nodes
[8].closest_bug_in_mou
, None)
1478 if __name__
== "__main__":