BudgetGraphBaseError,
BudgetGraphMoneyMismatch,
BudgetGraphNegativeMoney,
- BudgetGraphMilestoneMismatch)
+ BudgetGraphMilestoneMismatch,
+ BudgetGraphNegativePayeeMoney,
+ BudgetGraphPayeesParseError,
+ BudgetGraphPayeesMoneyMismatch)
from budget_sync.money import Money
from typing import List, Type
import unittest
self.assertEqual(str(BudgetGraphNegativeMoney(1, 5)),
"Budget assigned to task is less than zero: bug #1")
+ def test_budget_graph_negative_payee_money(self):
+ self.assertEqual(str(BudgetGraphNegativePayeeMoney(1, 5, "payee1")),
+ "Budget assigned to payee for task is less than "
+ "zero: bug #1, payee 'payee1'")
+
+ def test_budget_graph_payees_parse_error(self):
+ self.assertEqual(str(
+ BudgetGraphPayeesParseError(1, "my fake parse error")),
+ "Failed to parse cf_payees_list field of bug #1: "
+ "my fake parse error")
+
+ def test_budget_graph_payees_money_mismatch(self):
+ self.assertEqual(str(
+ BudgetGraphPayeesMoneyMismatch(1, 5, Money(123))),
+ "Budget assigned to task excluding subtasks (cf_budget field) "
+ "doesn't match total value assigned to payees (cf_payees_list):"
+ " bug #1, calculated total 123")
+
EXAMPLE_BUG1 = MockBug(bug_id=1,
cf_budget_parent=None,
cf_budget="0",
cf_total_budget="0",
- cf_nlnet_milestone=None)
+ cf_nlnet_milestone=None,
+ cf_payees_list="")
EXAMPLE_LOOP1_BUG1 = MockBug(bug_id=1,
cf_budget_parent=1,
cf_budget="0",
cf_total_budget="0",
- cf_nlnet_milestone=None)
+ cf_nlnet_milestone=None,
+ cf_payees_list="")
EXAMPLE_LOOP2_BUG1 = MockBug(bug_id=1,
cf_budget_parent=2,
cf_budget="0",
cf_total_budget="0",
- cf_nlnet_milestone=None)
+ cf_nlnet_milestone=None,
+ cf_payees_list="")
EXAMPLE_LOOP2_BUG2 = MockBug(bug_id=2,
cf_budget_parent=1,
cf_budget="0",
cf_total_budget="0",
- cf_nlnet_milestone=None)
+ cf_nlnet_milestone=None,
+ cf_payees_list="")
EXAMPLE_PARENT_BUG1 = MockBug(bug_id=1,
cf_budget_parent=None,
cf_budget="10",
cf_total_budget="20",
- cf_nlnet_milestone="abc")
+ cf_nlnet_milestone="abc",
+ cf_payees_list="")
EXAMPLE_CHILD_BUG2 = MockBug(bug_id=2,
cf_budget_parent=1,
cf_budget="10",
cf_total_budget="10",
- cf_nlnet_milestone="abc")
+ cf_nlnet_milestone="abc",
+ cf_payees_list="")
class TestBudgetGraph(unittest.TestCase):
self.assertEqual(node.budget_excluding_subtasks, Money(cents=0))
self.assertEqual(node.budget_including_subtasks, Money(cents=0))
self.assertIsNone(node.nlnet_milestone)
+ self.assertEqual(node.payees, {})
def test_loop1(self):
with self.assertRaises(BudgetGraphLoopError) as cm:
self.assertEqual(node1.budget_including_subtasks, Money(cents=2000))
self.assertEqual(node1.nlnet_milestone, "abc")
self.assertEqual(list(node1.children()), [node2])
+ self.assertEqual(node1.payees, {})
self.assertIsInstance(node2, Node)
self.assertIs(node2.graph, bg)
self.assertIs(node2.bug, EXAMPLE_CHILD_BUG2)
self.assertEqual(node2.budget_excluding_subtasks, Money(cents=1000))
self.assertEqual(node2.budget_including_subtasks, Money(cents=1000))
self.assertEqual(node2.nlnet_milestone, "abc")
+ self.assertEqual(node2.payees, {})
def test_money_with_no_milestone(self):
bg = BudgetGraph([
cf_budget_parent=None,
cf_budget="0",
cf_total_budget="10",
- cf_nlnet_milestone=None),
+ cf_nlnet_milestone=None,
+ cf_payees_list=""),
])
errors = bg.get_errors()
self.assertErrorTypesMatches(errors,
cf_budget_parent=None,
cf_budget="10",
cf_total_budget="0",
- cf_nlnet_milestone=None),
+ cf_nlnet_milestone=None,
+ cf_payees_list=""),
])
errors = bg.get_errors()
self.assertErrorTypesMatches(errors,
cf_budget_parent=None,
cf_budget="10",
cf_total_budget="10",
- cf_nlnet_milestone=None),
+ cf_nlnet_milestone=None,
+ cf_payees_list=""),
])
errors = bg.get_errors()
self.assertErrorTypesMatches(errors, [BudgetGraphMoneyWithNoMilestone])
cf_budget_parent=None,
cf_budget="0",
cf_total_budget="10",
- cf_nlnet_milestone="abc"),
+ cf_nlnet_milestone="abc",
+ cf_payees_list=""),
])
errors = bg.get_errors()
self.assertErrorTypesMatches(errors,
cf_budget_parent=None,
cf_budget="10",
cf_total_budget="0",
- cf_nlnet_milestone="abc"),
+ cf_nlnet_milestone="abc",
+ cf_payees_list=""),
])
errors = bg.get_errors()
self.assertErrorTypesMatches(errors,
cf_budget_parent=None,
cf_budget="10",
cf_total_budget="10",
- cf_nlnet_milestone="abc"),
+ cf_nlnet_milestone="abc",
+ cf_payees_list=""),
])
errors = bg.get_errors()
self.assertEqual(errors, [])
cf_budget_parent=None,
cf_budget="10",
cf_total_budget="10",
- cf_nlnet_milestone="abc"),
+ cf_nlnet_milestone="abc",
+ cf_payees_list=""),
MockBug(bug_id=2,
cf_budget_parent=1,
cf_budget="10",
cf_total_budget="10",
- cf_nlnet_milestone="abc"),
+ cf_nlnet_milestone="abc",
+ cf_payees_list=""),
MockBug(bug_id=3,
cf_budget_parent=1,
cf_budget="1",
cf_total_budget="10",
- cf_nlnet_milestone="abc"),
+ cf_nlnet_milestone="abc",
+ cf_payees_list=""),
])
errors = bg.get_errors()
self.assertErrorTypesMatches(errors,
cf_budget_parent=None,
cf_budget="0",
cf_total_budget="-10",
- cf_nlnet_milestone="abc"),
+ cf_nlnet_milestone="abc",
+ cf_payees_list=""),
])
errors = bg.get_errors()
self.assertErrorTypesMatches(errors,
cf_budget_parent=None,
cf_budget="-10",
cf_total_budget="0",
- cf_nlnet_milestone="abc"),
+ cf_nlnet_milestone="abc",
+ cf_payees_list=""),
])
errors = bg.get_errors()
self.assertErrorTypesMatches(errors,
cf_budget_parent=None,
cf_budget="-10",
cf_total_budget="-10",
- cf_nlnet_milestone="abc"),
+ cf_nlnet_milestone="abc",
+ cf_payees_list=""),
])
errors = bg.get_errors()
self.assertErrorTypesMatches(errors,
self.assertEqual(errors[0].bug_id, 1)
self.assertEqual(errors[0].root_bug_id, 1)
+ def test_payees_parse(self):
+ def check(cf_payees_list, expected_payees):
+ bg = BudgetGraph([MockBug(bug_id=1,
+ cf_budget_parent=None,
+ cf_budget="0",
+ cf_total_budget="0",
+ cf_nlnet_milestone="abc",
+ cf_payees_list=cf_payees_list),
+ ])
+ self.assertEqual(len(bg.nodes), 1)
+ node: Node = bg.nodes[1]
+ self.assertEqual(node.payees, expected_payees)
+
+ check("""
+ abc = 123
+ """,
+ {"abc": Money(123)})
+ check("""
+ abc = "123"
+ """,
+ {"abc": Money(123)})
+ check("""
+ abc = "123.45"
+ """,
+ {"abc": Money("123.45")})
+ check("""
+ abc = "123.45"
+ "d e f" = "21.35"
+ """,
+ {
+ "abc": Money("123.45"),
+ "d e f": Money("21.35"),
+ })
+ check("""
+ abc = "123.45"
+ # my comments
+ "AAA" = "-21.35"
+ """,
+ {
+ "abc": Money("123.45"),
+ "AAA": Money("-21.35"),
+ })
+ check("""
+ "not-an-email@example.com" = "-2345"
+ """,
+ {
+ "not-an-email@example.com": Money(-2345),
+ })
+
+ def test_payees_money_mismatch(self):
+ bg = BudgetGraph([
+ MockBug(bug_id=1,
+ cf_budget_parent=None,
+ cf_budget="10",
+ cf_total_budget="10",
+ cf_nlnet_milestone="abc",
+ cf_payees_list="payee = 5\npayee2 = 10"),
+ ])
+ errors = bg.get_errors()
+ self.assertErrorTypesMatches(errors,
+ [BudgetGraphPayeesMoneyMismatch])
+ self.assertEqual(errors[0].bug_id, 1)
+ self.assertEqual(errors[0].root_bug_id, 1)
+ self.assertEqual(errors[0].payees_total, 15)
+ bg = BudgetGraph([
+ MockBug(bug_id=1,
+ cf_budget_parent=None,
+ cf_budget="0",
+ cf_total_budget="0",
+ cf_nlnet_milestone=None,
+ cf_payees_list="payee = 5\npayee2 = 10"),
+ ])
+ errors = bg.get_errors()
+ self.assertErrorTypesMatches(errors,
+ [BudgetGraphPayeesMoneyMismatch])
+ self.assertEqual(errors[0].bug_id, 1)
+ self.assertEqual(errors[0].root_bug_id, 1)
+ self.assertEqual(errors[0].payees_total, 15)
+
+ def test_payees_parse_error(self):
+ def check_parse_error(cf_payees_list, expected_msg):
+ errors = BudgetGraph([
+ MockBug(bug_id=1,
+ cf_budget_parent=None,
+ cf_budget="0",
+ cf_total_budget="0",
+ cf_nlnet_milestone="abc",
+ cf_payees_list=cf_payees_list),
+ ]).get_errors()
+ self.assertErrorTypesMatches(errors,
+ [BudgetGraphPayeesParseError])
+ self.assertEqual(errors[0].bug_id, 1)
+ self.assertEqual(errors[0].msg, expected_msg)
+
+ check_parse_error("""
+ "payee 1" = {}
+ """,
+ "value for key 'payee 1' is not a string or integer "
+ "(to use fractional values such as 123.45, write "
+ "\"123.45\"): {}")
+
+ check_parse_error("""
+ payee = "ashjkf"
+ """,
+ "failed to parse Money value for key 'payee': "
+ "invalid Money string: characters after sign and "
+ "before first `.` must be ascii digits")
+
+ check_parse_error("""
+ payee = "1"
+ payee = "1"
+ """,
+ "TOML parse error: Duplicate keys! (line 3"
+ " column 1 char 39)")
+
+ check_parse_error("""
+ payee = 123.45
+ """,
+ "value for key 'payee' is not a string or "
+ "integer (to use fractional values such as "
+ "123.45, write \"123.45\"): 123.45")
+
+ def test_negative_payee_money(self):
+ bg = BudgetGraph([
+ MockBug(bug_id=1,
+ cf_budget_parent=None,
+ cf_budget="10",
+ cf_total_budget="10",
+ cf_nlnet_milestone="abc",
+ cf_payees_list="""payee1 = -10"""),
+ ])
+ errors = bg.get_errors()
+ self.assertErrorTypesMatches(errors,
+ [BudgetGraphNegativePayeeMoney,
+ BudgetGraphPayeesMoneyMismatch])
+ self.assertEqual(errors[0].bug_id, 1)
+ self.assertEqual(errors[0].root_bug_id, 1)
+ self.assertEqual(errors[0].payee_key, "payee1")
+ self.assertEqual(errors[1].bug_id, 1)
+ self.assertEqual(errors[1].root_bug_id, 1)
+ self.assertEqual(errors[1].payees_total, -10)
+
+ def test_payee_keys(self):
+ bg = BudgetGraph([
+ MockBug(bug_id=1,
+ cf_budget_parent=None,
+ cf_budget="10",
+ cf_total_budget="10",
+ cf_nlnet_milestone="abc",
+ cf_payees_list="payee2 = 3\npayee1 = 7"),
+ MockBug(bug_id=2,
+ cf_budget_parent=None,
+ cf_budget="10",
+ cf_total_budget="10",
+ cf_nlnet_milestone="def",
+ cf_payees_list="""payee3 = 5\npayee2 = 5"""),
+ ])
+ self.assertErrorTypesMatches(bg.get_errors(), [])
+ self.assertEqual(bg.payee_keys, {"payee1", "payee2", "payee3"})
+
if __name__ == "__main__":
unittest.main()