Coverage for tests/test_app_settings.py: 100%

124 statements  

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

1import pandas as pd 

2import pytest 

3 

4from sankey_cashflow import AppSettings 

5 

6 

7class TestDefaultInitialization: 

8 

9 def test_default_initialization(self, default_app_settings): 

10 settings = default_app_settings 

11 assert settings.data_source == 'sample_data/expenses.csv' 

12 assert settings.audit_mode is False 

13 assert settings.data_sheet == 'Transactions_*' 

14 assert settings._labels_source == 'sample_data/labels.csv' 

15 assert settings.filter_dates is None 

16 assert settings.separate_taxes is False 

17 assert settings.verbose is False 

18 assert settings._g_creds == './google_service_account_key.json' 

19 assert settings.distribute_amounts is False 

20 assert settings.all_time is False 

21 assert settings.recurring is False 

22 assert settings.base_title == 'Cashflow' 

23 assert settings._date_filter_start is None 

24 assert settings._date_filter_end is None 

25 assert settings.tags is None 

26 assert settings.feed_in is None 

27 assert settings.exclude_tags is None 

28 assert settings.stores is None 

29 assert settings.tag_override is False 

30 assert settings.hover == 'Category' 

31 assert settings.chart_resolution is None 

32 assert settings.sales_tax_classification == 'Taxes' 

33 assert settings.tip_classification == 'xTips' 

34 assert settings.diagram_type == 'sankey' 

35 assert settings.colors == {} 

36 

37 def test_verbose_logging(self, make_args): 

38 settings = AppSettings(make_args(verbose=True)) 

39 assert settings.verbose is True 

40 

41 

42class TestHoverOption: 

43 

44 @pytest.mark.parametrize('hover_arg', ['desc', 'stores', 'description', 'DESC']) 

45 def test_hover_maps_to_description(self, make_args, hover_arg): 

46 settings = AppSettings(make_args(hover=hover_arg)) 

47 assert settings.hover == 'Description' 

48 

49 @pytest.mark.parametrize('hover_arg', ['none', 'no', 'false', 'NONE']) 

50 def test_hover_disabled(self, make_args, hover_arg): 

51 settings = AppSettings(make_args(hover=hover_arg)) 

52 assert settings.hover is None 

53 

54 def test_hover_unrecognized_keeps_default(self, make_args): 

55 settings = AppSettings(make_args(hover='unknown-option')) 

56 assert settings.hover == 'Category' 

57 

58 

59class TestDiagramType: 

60 

61 @pytest.mark.parametrize('dtype_arg,expected', [('sankey', 'sankey'), ('line', 'line'), ('LINE', 'line')]) 

62 def test_recognized_dtype(self, make_args, dtype_arg, expected): 

63 settings = AppSettings(make_args(dtype=dtype_arg)) 

64 assert settings.diagram_type == expected 

65 

66 def test_unrecognized_dtype_keeps_default(self, make_args): 

67 settings = AppSettings(make_args(dtype='pie')) 

68 assert settings.diagram_type == 'sankey' 

69 

70 

71class TestTagsStoresExclude: 

72 

73 def test_tags_split_and_stripped(self, make_args): 

74 settings = AppSettings(make_args(tags=' Ford , Dodge ')) 

75 assert settings.tags == ['Ford', 'Dodge'] 

76 

77 def test_tag_override_requires_tags(self, make_args): 

78 settings = AppSettings(make_args(tags='Ford', tag_override=True)) 

79 assert settings.tag_override is True 

80 

81 def test_tag_override_ignored_without_tags(self, make_args): 

82 settings = AppSettings(make_args(tags=None, tag_override=True)) 

83 assert settings.tag_override is False 

84 

85 def test_feed_in_requires_tags(self, make_args): 

86 settings = AppSettings(make_args(tags='Ford', feed_in=True)) 

87 assert settings.feed_in is True 

88 

89 def test_feed_in_ignored_without_tags(self, make_args): 

90 settings = AppSettings(make_args(tags=None, feed_in=True)) 

91 assert settings.feed_in is None 

92 

93 def test_exclude_split_and_stripped(self, make_args): 

94 settings = AppSettings(make_args(exclude=' Onetime , Refund ')) 

95 assert settings.exclude_tags == ['Onetime', 'Refund'] 

96 

97 def test_stores_split_and_stripped(self, make_args): 

98 settings = AppSettings(make_args(stores=' Costco , Amazon ')) 

99 assert settings.stores == ['Costco', 'Amazon'] 

100 

101 def test_tags_and_stores_together_raises(self, make_args): 

102 with pytest.raises(Exception): 

103 AppSettings(make_args(tags='Ford', stores='Costco')) 

104 

105 

106class TestDateFilterSetters: 

107 

108 def test_date_filter_start_setter(self, default_app_settings): 

109 default_app_settings.date_filter_start = '2023-01-01' 

110 assert default_app_settings.date_filter_start == pd.to_datetime('2023-01-01') 

111 

112 def test_date_filter_start_setter_empty_is_none(self, default_app_settings): 

113 default_app_settings.date_filter_start = '' 

114 assert default_app_settings.date_filter_start is None 

115 

116 def test_date_filter_end_setter(self, default_app_settings): 

117 default_app_settings.date_filter_end = '2023-12-31' 

118 assert default_app_settings.date_filter_end == pd.to_datetime('2023-12-31') 

119 

120 def test_date_filter_end_setter_empty_is_none(self, default_app_settings): 

121 default_app_settings.date_filter_end = '' 

122 assert default_app_settings.date_filter_end is None 

123 

124 

125class TestCredsAndLabelsSetters: 

126 

127 def test_g_creds_setter_missing_file_raises(self, default_app_settings): 

128 with pytest.raises(Exception): 

129 default_app_settings.g_creds = 'invalid_path.json' 

130 

131 def test_labels_source_setter_missing_file_raises(self, default_app_settings): 

132 with pytest.raises(Exception): 

133 default_app_settings.labels_source = 'invalid_path.csv' 

134 

135 def test_labels_source_setter_valid_csv(self, default_app_settings): 

136 default_app_settings.labels_source = 'sample_data/labels.csv' 

137 assert default_app_settings.labels_source == 'sample_data/labels.csv' 

138 

139 

140class TestSourceValidation: 

141 

142 def test_source_data_location_csv(self, default_app_settings): 

143 assert default_app_settings.source_data_location() == 'sample_data/expenses.csv' 

144 

145 def test_source_data_location_gsheet(self, make_args): 

146 settings = AppSettings(make_args( 

147 source='My Workbook', sheet='Transactions_2023', creds='sample_data/labels.csv' 

148 )) 

149 assert settings.source_data_location() == 'My Workbook: Transactions_2023' 

150 

151 def test_validate_sources_missing_source_raises(self, default_app_settings): 

152 default_app_settings.data_source = None 

153 with pytest.raises(Exception): 

154 default_app_settings.validate_sources() 

155 

156 def test_validate_sources_csv_requires_csv_labels(self, make_args): 

157 with pytest.raises(Exception): 

158 AppSettings(make_args(source='sample_data/expenses.csv', srcmap='Sources-Targets')) 

159 

160 def test_validate_sources_missing_csv_file_raises(self, make_args): 

161 with pytest.raises(Exception): 

162 AppSettings(make_args(source='does_not_exist.csv')) 

163 

164 def test_validate_sources_gsheet_requires_sheet_name(self, make_args): 

165 with pytest.raises(Exception): 

166 AppSettings(make_args(source='My Workbook', sheet=None, srcmap='Sources-Targets')) 

167 

168 def test_validate_sources_gsheet_requires_creds_file(self, make_args): 

169 with pytest.raises(Exception): 

170 AppSettings(make_args( 

171 source='My Workbook', sheet='Transactions_2023', 

172 srcmap='Sources-Targets', creds='missing_creds.json' 

173 ))