Coverage for tests/test_row_labels.py: 100%

72 statements  

« prev     ^ index     » next       coverage.py v7.15.0, created at 2026-07-05 04:36 +0000

1import networkx as nx 

2import pytest 

3 

4from sankey_cashflow import RowLabels 

5 

6 

7class TestInit: 

8 

9 def test_init(self, sample_row_labels): 

10 assert len(sample_row_labels.data) == 2 

11 assert 'House' in sample_row_labels._lookup 

12 assert 'Groceries' in sample_row_labels._lookup 

13 assert isinstance(sample_row_labels._digraph, nx.DiGraph) 

14 

15 def test_missing_required_column_raises(self): 

16 with pytest.raises(Exception): 

17 RowLabels([{'Category Name': 'House', 'Type': 'computed'}]) 

18 

19 def test_duplicate_category_name_raises(self): 

20 row = { 

21 'Category Name': 'House', 'Type': 'computed', 'Source': 'Income', 'Target': 'House', 

22 'Classification': 'Expense', 'Link color': '', 'Node color': '', 'Comments': '' 

23 } 

24 with pytest.raises(Exception): 

25 RowLabels([row, dict(row)]) 

26 

27 def test_empty_category_name_is_skipped(self): 

28 row = { 

29 'Category Name': '', 'Type': '', 'Source': '', 'Target': '', 

30 'Classification': '', 'Link color': '', 'Node color': '', 'Comments': '' 

31 } 

32 labels = RowLabels([row]) 

33 assert labels._lookup == {} 

34 

35 def test_deductions_source_skips_dag_edge(self): 

36 row = { 

37 'Category Name': '401K', 'Type': '', 'Source': 'DEDUCTIONS', 'Target': '401K', 

38 'Classification': '', 'Link color': '', 'Node color': '', 'Comments': '' 

39 } 

40 labels = RowLabels([row]) 

41 assert not labels._digraph.has_edge('DEDUCTIONS', '401K') 

42 assert '401K' in labels._lookup 

43 

44 def test_s_tag_adds_node_not_edge(self): 

45 row = { 

46 'Category Name': 'Joe', 'Type': 's-tag', 'Source': '', 'Target': '', 

47 'Classification': '', 'Link color': '', 'Node color': '', 'Comments': '' 

48 } 

49 labels = RowLabels([row]) 

50 assert labels._digraph.has_node('Joe') 

51 assert labels._digraph.nodes['Joe']['type'] == 's-tag' 

52 

53 def test_tag_type_not_added_to_dag(self): 

54 row = { 

55 'Category Name': 'Ford', 'Type': 'tag', 'Source': 'Automotive', 'Target': 'Ford', 

56 'Classification': '', 'Link color': '', 'Node color': '', 'Comments': '' 

57 } 

58 labels = RowLabels([row]) 

59 assert not labels._digraph.has_node('Ford') 

60 

61 

62class TestPaths: 

63 

64 def test_get_longest_path(self, sample_row_labels): 

65 longest_path = sample_row_labels.get_longest_path() 

66 assert isinstance(longest_path, list) 

67 assert len(longest_path) >= 1 

68 

69 def test_get_path(self, sample_row_labels): 

70 path = sample_row_labels.get_path('Income', 'House') 

71 assert isinstance(path, list) 

72 assert path == ['Income', 'House'] 

73 

74 def test_get_path_no_match_returns_none(self, sample_row_labels): 

75 assert sample_row_labels.get_path('House', 'Income') is None 

76 

77 

78class TestLabelLookup: 

79 

80 def test_get_label(self, sample_row_labels): 

81 label = sample_row_labels.get_label('House') 

82 assert label is not None 

83 assert label['source'] == 'Income' 

84 assert label['target'] == 'House' 

85 

86 def test_get_label_unknown_returns_none(self, sample_row_labels): 

87 assert sample_row_labels.get_label('Nonexistent') is None 

88 

89 def test_get_attribute(self, sample_row_labels): 

90 assert sample_row_labels.get_attribute('House', 'source') == 'Income' 

91 assert sample_row_labels.get_attribute('House', 'target') == 'House' 

92 assert sample_row_labels.get_attribute('House', 'classification') == 'Expense' 

93 

94 def test_get_attribute_unknown_attribute_raises(self, sample_row_labels): 

95 with pytest.raises(Exception): 

96 sample_row_labels.get_attribute('House', 'not_a_real_attribute') 

97 

98 def test_get_attribute_missing_label_uses_default(self, sample_row_labels): 

99 source = sample_row_labels.get_attribute('Nonexistent', 'source') 

100 assert source == 'Uncategorized' 

101 target = sample_row_labels.get_attribute('Nonexistent', 'target') 

102 assert target == 'Nonexistent' 

103 

104 def test_get_attribute_missing_label_no_default_returns_none(self, sample_row_labels): 

105 assert sample_row_labels.get_attribute('Nonexistent', 'source', use_default=False) is None 

106 

107 

108class TestPrintGraph: 

109 

110 def test_print_graph_writes_file(self, sample_row_labels, tmp_path): 

111 out_file = tmp_path / 'test_graph.png' 

112 sample_row_labels.print_graph(str(out_file)) 

113 assert out_file.is_file()