1 from budget_sync
.test
.mock_bug
import MockBug
2 from budget_sync
.budget_graph
import (BudgetGraphLoopError
, BudgetGraph
,
3 Node
, BudgetGraphMoneyWithNoMilestone
,
5 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
,
6 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
,
7 BudgetGraphNegativeMoney
,
8 BudgetGraphMilestoneMismatch
,
9 BudgetGraphNegativePayeeMoney
,
10 BudgetGraphPayeesParseError
,
11 BudgetGraphPayeesMoneyMismatch
)
12 from budget_sync
.money
import Money
13 from typing
import List
, Type
17 class TestErrorFormatting(unittest
.TestCase
):
18 def test_budget_graph_loop_error(self
):
19 self
.assertEqual(str(BudgetGraphLoopError([1, 2, 3, 4, 5])),
20 "Detected Loop in Budget Graph: #5 -> #1 "
21 "-> #2 -> #3 -> #4 -> #5")
22 self
.assertEqual(str(BudgetGraphLoopError([1])),
23 "Detected Loop in Budget Graph: #1 -> #1")
25 def test_budget_graph_money_with_no_milestone(self
):
26 self
.assertEqual(str(BudgetGraphMoneyWithNoMilestone(1, 5)),
27 "Bug assigned money but without any assigned "
30 def test_budget_graph_milestone_mismatch(self
):
31 self
.assertEqual(str(BudgetGraphMilestoneMismatch(1, 5)),
32 "Bug's assigned milestone doesn't match the "
33 "milestone assigned to the root bug: descendant "
34 "bug #1, root bug #5")
36 def test_budget_graph_money_mismatch(self
):
38 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks(
40 "Budget assigned to task excluding subtasks "
41 "(cf_budget field) doesn't match calculated value:"
42 " bug #1, calculated value 123.4")
44 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
46 "Budget assigned to task including subtasks "
47 "(cf_total_budget field) doesn't match calculated value:"
48 " bug #1, calculated value 123.4")
50 def test_budget_graph_negative_money(self
):
51 self
.assertEqual(str(BudgetGraphNegativeMoney(1, 5)),
52 "Budget assigned to task is less than zero: bug #1")
54 def test_budget_graph_negative_payee_money(self
):
55 self
.assertEqual(str(BudgetGraphNegativePayeeMoney(1, 5, "payee1")),
56 "Budget assigned to payee for task is less than "
57 "zero: bug #1, payee 'payee1'")
59 def test_budget_graph_payees_parse_error(self
):
61 BudgetGraphPayeesParseError(1, "my fake parse error")),
62 "Failed to parse cf_payees_list field of bug #1: "
63 "my fake parse error")
65 def test_budget_graph_payees_money_mismatch(self
):
67 BudgetGraphPayeesMoneyMismatch(1, 5, Money(123), Money(456))),
68 "Total budget assigned to payees (cf_payees_list) doesn't match "
69 "expected value: bug #1, calculated total 123, expected value 456")
72 EXAMPLE_BUG1
= MockBug(bug_id
=1,
73 cf_budget_parent
=None,
76 cf_nlnet_milestone
=None,
78 EXAMPLE_LOOP1_BUG1
= MockBug(bug_id
=1,
82 cf_nlnet_milestone
=None,
84 EXAMPLE_LOOP2_BUG1
= MockBug(bug_id
=1,
88 cf_nlnet_milestone
=None,
90 EXAMPLE_LOOP2_BUG2
= MockBug(bug_id
=2,
94 cf_nlnet_milestone
=None,
96 EXAMPLE_PARENT_BUG1
= MockBug(bug_id
=1,
97 cf_budget_parent
=None,
100 cf_nlnet_milestone
="abc",
102 EXAMPLE_CHILD_BUG2
= MockBug(bug_id
=2,
105 cf_total_budget
="10",
106 cf_nlnet_milestone
="abc",
110 class TestBudgetGraph(unittest
.TestCase
):
111 def assertErrorTypesMatches(self
, errors
: List
[BudgetGraphBaseError
], template
: List
[Type
]):
114 error_types
.append(type(error
))
115 self
.assertEqual(error_types
, template
)
117 def test_empty(self
):
119 self
.assertEqual(len(bg
.nodes
), 0)
120 self
.assertEqual(len(bg
.roots
), 0)
122 def test_single(self
):
123 bg
= BudgetGraph([EXAMPLE_BUG1
])
124 self
.assertEqual(len(bg
.nodes
), 1)
125 node
: Node
= bg
.nodes
[1]
126 self
.assertEqual(bg
.roots
, {node}
)
127 self
.assertIsInstance(node
, Node
)
128 self
.assertIs(node
.graph
, bg
)
129 self
.assertIs(node
.bug
, EXAMPLE_BUG1
)
130 self
.assertIs(node
.root
, node
)
131 self
.assertIsNone(node
.parent_id
)
132 self
.assertEqual(node
.immediate_children
, set())
133 self
.assertEqual(node
.budget_excluding_subtasks
, Money(cents
=0))
134 self
.assertEqual(node
.budget_including_subtasks
, Money(cents
=0))
135 self
.assertIsNone(node
.nlnet_milestone
)
136 self
.assertEqual(node
.payments
, {})
138 def test_loop1(self
):
139 with self
.assertRaises(BudgetGraphLoopError
) as cm
:
140 BudgetGraph([EXAMPLE_LOOP1_BUG1
]).roots
141 self
.assertEqual(cm
.exception
.bug_ids
, [1])
143 def test_loop2(self
):
144 with self
.assertRaises(BudgetGraphLoopError
) as cm
:
145 BudgetGraph([EXAMPLE_LOOP2_BUG1
, EXAMPLE_LOOP2_BUG2
]).roots
146 self
.assertEqual(cm
.exception
.bug_ids
, [2, 1])
148 def test_parent_child(self
):
149 bg
= BudgetGraph([EXAMPLE_PARENT_BUG1
, EXAMPLE_CHILD_BUG2
])
150 self
.assertEqual(len(bg
.nodes
), 2)
151 node1
: Node
= bg
.nodes
[1]
152 node2
: Node
= bg
.nodes
[2]
153 self
.assertEqual(bg
.roots
, {node1}
)
154 self
.assertEqual(node1
, node1
)
155 self
.assertEqual(node2
, node2
)
156 self
.assertNotEqual(node1
, node2
)
157 self
.assertNotEqual(node2
, node1
)
158 self
.assertIsInstance(node1
, Node
)
159 self
.assertIs(node1
.graph
, bg
)
160 self
.assertIs(node1
.bug
, EXAMPLE_PARENT_BUG1
)
161 self
.assertIsNone(node1
.parent_id
)
162 self
.assertEqual(node1
.root
, node1
)
163 self
.assertEqual(node1
.immediate_children
, {node2}
)
164 self
.assertEqual(node1
.budget_excluding_subtasks
, Money(cents
=1000))
165 self
.assertEqual(node1
.budget_including_subtasks
, Money(cents
=2000))
166 self
.assertEqual(node1
.nlnet_milestone
, "abc")
167 self
.assertEqual(list(node1
.children()), [node2
])
168 self
.assertEqual(list(node1
.children_breadth_first()), [node2
])
169 self
.assertEqual(node1
.payments
, {})
170 self
.assertIsInstance(node2
, Node
)
171 self
.assertIs(node2
.graph
, bg
)
172 self
.assertIs(node2
.bug
, EXAMPLE_CHILD_BUG2
)
173 self
.assertEqual(node2
.parent_id
, 1)
174 self
.assertEqual(node2
.root
, node1
)
175 self
.assertEqual(node2
.immediate_children
, set())
176 self
.assertEqual(node2
.budget_excluding_subtasks
, Money(cents
=1000))
177 self
.assertEqual(node2
.budget_including_subtasks
, Money(cents
=1000))
178 self
.assertEqual(node2
.nlnet_milestone
, "abc")
179 self
.assertEqual(node2
.payments
, {})
181 def test_children(self
):
184 cf_budget_parent
=None,
187 cf_nlnet_milestone
=None,
193 cf_nlnet_milestone
=None,
199 cf_nlnet_milestone
=None,
205 cf_nlnet_milestone
=None,
211 cf_nlnet_milestone
=None,
217 cf_nlnet_milestone
=None,
223 cf_nlnet_milestone
=None,
226 self
.assertEqual(len(bg
.nodes
), 7)
227 node1
: Node
= bg
.nodes
[1]
228 node2
: Node
= bg
.nodes
[2]
229 node3
: Node
= bg
.nodes
[3]
230 node4
: Node
= bg
.nodes
[4]
231 node5
: Node
= bg
.nodes
[5]
232 node6
: Node
= bg
.nodes
[6]
233 node7
: Node
= bg
.nodes
[7]
234 self
.assertEqual(bg
.roots
, {node1}
)
235 self
.assertEqual(list(node1
.children()),
236 [node2
, node3
, node5
, node7
, node6
, node4
])
237 self
.assertEqual(list(node1
.children_breadth_first()),
238 [node2
, node3
, node4
, node5
, node6
, node7
])
240 def test_money_with_no_milestone(self
):
243 cf_budget_parent
=None,
245 cf_total_budget
="10",
246 cf_nlnet_milestone
=None,
249 errors
= bg
.get_errors()
250 self
.assertErrorTypesMatches(errors
, [
251 BudgetGraphMoneyWithNoMilestone
,
252 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
])
253 self
.assertEqual(errors
[0].bug_id
, 1)
254 self
.assertEqual(errors
[0].root_bug_id
, 1)
257 cf_budget_parent
=None,
260 cf_nlnet_milestone
=None,
263 errors
= bg
.get_errors()
264 self
.assertErrorTypesMatches(errors
, [
265 BudgetGraphMoneyWithNoMilestone
,
266 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
])
267 self
.assertEqual(errors
[0].bug_id
, 1)
268 self
.assertEqual(errors
[0].root_bug_id
, 1)
271 cf_budget_parent
=None,
273 cf_total_budget
="10",
274 cf_nlnet_milestone
=None,
277 errors
= bg
.get_errors()
278 self
.assertErrorTypesMatches(errors
, [BudgetGraphMoneyWithNoMilestone
])
279 self
.assertEqual(errors
[0].bug_id
, 1)
280 self
.assertEqual(errors
[0].root_bug_id
, 1)
282 def test_money_mismatch(self
):
283 def helper(budget
, total_budget
, payees_list
, child_budget
,
284 expected_errors
, expected_fixed_error_types
=None):
285 if expected_fixed_error_types
is None:
286 expected_fixed_error_types
= []
289 cf_budget_parent
=None,
291 cf_total_budget
=total_budget
,
292 cf_nlnet_milestone
="abc",
293 cf_payees_list
=payees_list
),
296 cf_budget
=child_budget
,
297 cf_total_budget
=child_budget
,
298 cf_nlnet_milestone
="abc",
301 node1
: Node
= bg
.nodes
[1]
302 errors
= bg
.get_errors()
303 self
.assertErrorTypesMatches(errors
,
304 [type(i
) for i
in expected_errors
])
305 self
.assertEqual([str(i
) for i
in errors
],
306 [str(i
) for i
in expected_errors
])
309 cf_budget_parent
=None,
310 cf_budget
=str(node1
.fixed_budget_excluding_subtasks
),
312 node1
.fixed_budget_including_subtasks
),
313 cf_nlnet_milestone
="abc",
314 cf_payees_list
=payees_list
),
317 cf_budget
=child_budget
,
318 cf_total_budget
=child_budget
,
319 cf_nlnet_milestone
="abc",
322 errors
= bg
.get_errors()
323 self
.assertErrorTypesMatches(errors
,
324 expected_fixed_error_types
)
335 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
343 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks(
345 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
353 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks(
355 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
363 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks(
365 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
373 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks(
375 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
383 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks(
391 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks(
399 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks(
401 BudgetGraphPayeesMoneyMismatch(1, 1, Money(1), Money(100)),
403 expected_fixed_error_types
=[BudgetGraphPayeesMoneyMismatch
])
409 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks(
411 BudgetGraphPayeesMoneyMismatch(1, 1, Money(1), Money(95)),
413 expected_fixed_error_types
=[BudgetGraphPayeesMoneyMismatch
])
419 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks(
421 BudgetGraphPayeesMoneyMismatch(1, 1, Money(10), Money(100)),
423 expected_fixed_error_types
=[BudgetGraphPayeesMoneyMismatch
])
429 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks(
431 BudgetGraphPayeesMoneyMismatch(1, 1, Money(10), Money(95)),
433 expected_fixed_error_types
=[BudgetGraphPayeesMoneyMismatch
])
444 BudgetGraphPayeesMoneyMismatch(1, 1, Money(1), Money(0)),
446 expected_fixed_error_types
=[BudgetGraphPayeesMoneyMismatch
])
452 BudgetGraphPayeesMoneyMismatch(1, 1, Money(10), Money(0)),
454 expected_fixed_error_types
=[BudgetGraphPayeesMoneyMismatch
])
460 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
468 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
476 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
478 BudgetGraphPayeesMoneyMismatch(1, 1, Money(1), Money(10)),
480 expected_fixed_error_types
=[BudgetGraphPayeesMoneyMismatch
])
486 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
488 BudgetGraphPayeesMoneyMismatch(1, 1, Money(1), Money(10)),
490 expected_fixed_error_types
=[BudgetGraphPayeesMoneyMismatch
])
496 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
504 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
517 BudgetGraphPayeesMoneyMismatch(1, 1, Money(1), Money(10)),
519 expected_fixed_error_types
=[BudgetGraphPayeesMoneyMismatch
])
530 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks(
538 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks(
546 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
548 BudgetGraphPayeesMoneyMismatch(1, 1, Money(1), Money(10)),
550 expected_fixed_error_types
=[BudgetGraphPayeesMoneyMismatch
])
556 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
558 BudgetGraphPayeesMoneyMismatch(1, 1, Money(1), Money(10)),
560 expected_fixed_error_types
=[BudgetGraphPayeesMoneyMismatch
])
566 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
574 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks(
587 BudgetGraphPayeesMoneyMismatch(1, 1, Money(1), Money(10)),
589 expected_fixed_error_types
=[BudgetGraphPayeesMoneyMismatch
])
601 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks(
605 def test_negative_money(self
):
608 cf_budget_parent
=None,
610 cf_total_budget
="-10",
611 cf_nlnet_milestone
="abc",
614 errors
= bg
.get_errors()
615 self
.assertErrorTypesMatches(errors
, [
616 BudgetGraphNegativeMoney
,
617 BudgetGraphMoneyMismatchForBudgetExcludingSubtasks
])
618 self
.assertEqual(errors
[0].bug_id
, 1)
619 self
.assertEqual(errors
[0].root_bug_id
, 1)
620 self
.assertEqual(errors
[1].bug_id
, 1)
621 self
.assertEqual(errors
[1].root_bug_id
, 1)
622 self
.assertEqual(errors
[1].expected_budget_excluding_subtasks
, -10)
625 cf_budget_parent
=None,
628 cf_nlnet_milestone
="abc",
631 errors
= bg
.get_errors()
632 self
.assertErrorTypesMatches(errors
, [
633 BudgetGraphNegativeMoney
,
634 BudgetGraphMoneyMismatchForBudgetIncludingSubtasks
])
635 self
.assertEqual(errors
[0].bug_id
, 1)
636 self
.assertEqual(errors
[0].root_bug_id
, 1)
637 self
.assertEqual(errors
[1].bug_id
, 1)
638 self
.assertEqual(errors
[1].root_bug_id
, 1)
639 self
.assertEqual(errors
[1].expected_budget_including_subtasks
, -10)
642 cf_budget_parent
=None,
644 cf_total_budget
="-10",
645 cf_nlnet_milestone
="abc",
648 errors
= bg
.get_errors()
649 self
.assertErrorTypesMatches(errors
,
650 [BudgetGraphNegativeMoney
])
651 self
.assertEqual(errors
[0].bug_id
, 1)
652 self
.assertEqual(errors
[0].root_bug_id
, 1)
654 def test_payees_parse(self
):
655 def check(cf_payees_list
, expected_payments
):
656 bg
= BudgetGraph([MockBug(bug_id
=1,
657 cf_budget_parent
=None,
660 cf_nlnet_milestone
="abc",
661 cf_payees_list
=cf_payees_list
),
663 self
.assertEqual(len(bg
.nodes
), 1)
664 node
: Node
= bg
.nodes
[1]
665 self
.assertEqual([str(i
) for i
in node
.payments
.values()],
671 ["Payment(node=#1, payee_key='abc', amount=123, "
672 + "state=NotYetSubmitted, paid=None, submitted=None)"])
676 ["Payment(node=#1, payee_key='abc', amount=123, "
677 + "state=NotYetSubmitted, paid=None, submitted=None)"])
681 ["Payment(node=#1, payee_key='abc', amount=123.45, "
682 + "state=NotYetSubmitted, paid=None, submitted=None)"])
687 ["Payment(node=#1, payee_key='abc', amount=123.45, "
688 + "state=NotYetSubmitted, paid=None, submitted=None)",
689 "Payment(node=#1, payee_key='d e f', amount=21.35, "
690 + "state=NotYetSubmitted, paid=None, submitted=None)"])
696 ["Payment(node=#1, payee_key='abc', amount=123.45, "
697 + "state=NotYetSubmitted, paid=None, submitted=None)",
698 "Payment(node=#1, payee_key='AAA', amount=-21.35, "
699 + "state=NotYetSubmitted, paid=None, submitted=None)"])
701 "not-an-email@example.com" = "-2345"
703 ["Payment(node=#1, payee_key='not-an-email@example.com', "
704 + "amount=-2345, state=NotYetSubmitted, paid=None, "
705 + "submitted=None)"])
707 payee = { amount = 123 }
709 ["Payment(node=#1, payee_key='payee', "
710 + "amount=123, state=NotYetSubmitted, paid=None, "
711 + "submitted=None)"])
713 payee = { amount = 123, submitted = 2020-05-01 }
715 ["Payment(node=#1, payee_key='payee', "
716 + "amount=123, state=Submitted, paid=None, "
717 + "submitted=2020-05-01)"])
719 payee = { amount = 123, submitted = 2020-05-01T00:00:00 }
721 ["Payment(node=#1, payee_key='payee', "
722 + "amount=123, state=Submitted, paid=None, "
723 + "submitted=2020-05-01 00:00:00)"])
725 payee = { amount = 123, submitted = 2020-05-01T00:00:00Z }
727 ["Payment(node=#1, payee_key='payee', "
728 + "amount=123, state=Submitted, paid=None, "
729 + "submitted=2020-05-01 00:00:00+00:00)"])
731 payee = { amount = 123, submitted = 2020-05-01T00:00:00-07:23 }
733 ["Payment(node=#1, payee_key='payee', "
734 + "amount=123, state=Submitted, paid=None, "
735 + "submitted=2020-05-01 00:00:00-07:23)"])
737 payee = { amount = 123, paid = 2020-05-01 }
739 ["Payment(node=#1, payee_key='payee', "
740 + "amount=123, state=Paid, paid=2020-05-01, "
741 + "submitted=None)"])
743 payee = { amount = 123, paid = 2020-05-01T00:00:00 }
745 ["Payment(node=#1, payee_key='payee', "
746 + "amount=123, state=Paid, paid=2020-05-01 00:00:00, "
747 + "submitted=None)"])
749 payee = { amount = 123, paid = 2020-05-01T00:00:00Z }
751 ["Payment(node=#1, payee_key='payee', "
752 + "amount=123, state=Paid, paid=2020-05-01 00:00:00+00:00, "
753 + "submitted=None)"])
755 payee = { amount = 123, paid = 2020-05-01T00:00:00-07:23 }
757 ["Payment(node=#1, payee_key='payee', "
758 + "amount=123, state=Paid, paid=2020-05-01 00:00:00-07:23, "
759 + "submitted=None)"])
763 submitted = 2020-05-23
766 ["Payment(node=#1, payee_key='payee', "
767 + "amount=123, state=Paid, paid=2020-05-01, "
768 + "submitted=2020-05-23)"])
772 submitted = 2020-05-23
773 paid = 2020-05-01T00:00:00
775 ["Payment(node=#1, payee_key='payee', "
776 + "amount=123, state=Paid, paid=2020-05-01 00:00:00, "
777 + "submitted=2020-05-23)"])
781 submitted = 2020-05-23
782 paid = 2020-05-01T00:00:00Z
784 ["Payment(node=#1, payee_key='payee', "
785 + "amount=123, state=Paid, paid=2020-05-01 00:00:00+00:00, "
786 + "submitted=2020-05-23)"])
790 submitted = 2020-05-23
791 paid = 2020-05-01T00:00:00-07:23
793 ["Payment(node=#1, payee_key='payee', "
794 + "amount=123, state=Paid, paid=2020-05-01 00:00:00-07:23, "
795 + "submitted=2020-05-23)"])
797 def test_payees_money_mismatch(self
):
800 cf_budget_parent
=None,
802 cf_total_budget
="10",
803 cf_nlnet_milestone
="abc",
804 cf_payees_list
="payee = 5\npayee2 = 10"),
806 errors
= bg
.get_errors()
807 self
.assertErrorTypesMatches(errors
,
808 [BudgetGraphPayeesMoneyMismatch
])
809 self
.assertEqual(errors
[0].bug_id
, 1)
810 self
.assertEqual(errors
[0].root_bug_id
, 1)
811 self
.assertEqual(errors
[0].payees_total
, 15)
813 def test_payees_parse_error(self
):
814 def check_parse_error(cf_payees_list
, expected_msg
):
815 errors
= BudgetGraph([
817 cf_budget_parent
=None,
820 cf_nlnet_milestone
="abc",
821 cf_payees_list
=cf_payees_list
),
823 self
.assertErrorTypesMatches(errors
,
824 [BudgetGraphPayeesParseError
])
825 self
.assertEqual(errors
[0].bug_id
, 1)
826 self
.assertEqual(errors
[0].msg
, expected_msg
)
828 check_parse_error("""
831 "value for key 'payee 1' is invalid -- it should "
832 "either be a monetary value or a table")
834 check_parse_error("""
837 "failed to parse monetary amount for key 'payee': "
838 "invalid Money string: characters after sign and "
839 "before first `.` must be ascii digits")
841 check_parse_error("""
845 "TOML parse error: Duplicate keys! (line 3"
846 " column 1 char 39)")
848 check_parse_error("""
851 "failed to parse monetary amount for key 'payee': "
852 "monetary amount is not a string or integer (to "
853 "use fractional amounts such as 123.45, write "
854 "\"123.45\"): 123.45")
856 check_parse_error("""
859 "value for key 'payee' is missing the `amount` "
860 "field which is required")
862 check_parse_error("""
863 payee = { amount = 123.45 }
865 "failed to parse monetary amount for key 'payee': "
866 "monetary amount is not a string or integer (to "
867 "use fractional amounts such as 123.45, write "
868 "\"123.45\"): 123.45")
870 check_parse_error("""
871 payee = { amount = 123, blah = false }
873 "value for key 'payee' has an unknown field: `blah`")
875 check_parse_error("""
876 payee = { amount = 123, submitted = false }
878 "failed to parse `submitted` field for key "
879 "'payee': invalid date: false")
881 check_parse_error("""
882 payee = { amount = 123, submitted = 123 }
884 "failed to parse `submitted` field for key 'payee':"
885 " invalid date: 123")
889 payee = { amount = 123, paid = 2020-01-01, submitted = "abc" }
891 "failed to parse `submitted` field for key 'payee': "
892 "invalid date: 'abc'")
896 payee = { amount = 123, paid = 12:34:56 }
898 "failed to parse `paid` field for key 'payee': just a time of "
899 "day by itself is not enough, a date must be included: 12:34:56")
903 payee = { amount = 123, submitted = 12:34:56.123456 }
905 "failed to parse `submitted` field for key 'payee': just a time "
906 "of day by itself is not enough, a date must be included: "
909 def test_negative_payee_money(self
):
912 cf_budget_parent
=None,
914 cf_total_budget
="10",
915 cf_nlnet_milestone
="abc",
916 cf_payees_list
="""payee1 = -10"""),
918 errors
= bg
.get_errors()
919 self
.assertErrorTypesMatches(errors
,
920 [BudgetGraphNegativePayeeMoney
,
921 BudgetGraphPayeesMoneyMismatch
])
922 self
.assertEqual(errors
[0].bug_id
, 1)
923 self
.assertEqual(errors
[0].root_bug_id
, 1)
924 self
.assertEqual(errors
[0].payee_key
, "payee1")
925 self
.assertEqual(errors
[1].bug_id
, 1)
926 self
.assertEqual(errors
[1].root_bug_id
, 1)
927 self
.assertEqual(errors
[1].payees_total
, -10)
929 def test_payee_keys(self
):
932 cf_budget_parent
=None,
934 cf_total_budget
="10",
935 cf_nlnet_milestone
="abc",
936 cf_payees_list
="payee2 = 3\npayee1 = 7"),
938 cf_budget_parent
=None,
940 cf_total_budget
="10",
941 cf_nlnet_milestone
="def",
942 cf_payees_list
="""payee3 = 5\npayee2 = 5"""),
944 self
.assertErrorTypesMatches(bg
.get_errors(), [])
945 self
.assertEqual(bg
.payee_keys
, {"payee1", "payee2", "payee3"})
948 if __name__
== "__main__":