Coverage for tests/test_helpers.py: 100%

92 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 ( 

5 is_null, 

6 is_empty, 

7 validate_date_string, 

8 normalize_amounts, 

9 df_date_filter, 

10) 

11 

12 

13class TestIsNull: 

14 

15 @pytest.mark.parametrize('value', [None, float('nan'), pd.NaT, 'None', 'NaN', 'null']) 

16 def test_null_values(self, value): 

17 assert is_null(value) is True 

18 

19 @pytest.mark.parametrize('value', ['', 0, 'valid_string', 123, pd.Timestamp('2023-01-01')]) 

20 def test_non_null_values(self, value): 

21 assert is_null(value) is False 

22 

23 

24class TestIsEmpty: 

25 

26 @pytest.mark.parametrize('value', [None, float('nan'), pd.NaT, 'None', 'NaN', 'null', '']) 

27 def test_empty_values(self, value): 

28 assert is_empty(value) is True 

29 

30 @pytest.mark.parametrize('value', [0, 'valid_string', 123, pd.Timestamp('2023-01-01'), [], {}, (), set()]) 

31 def test_non_empty_values(self, value): 

32 assert is_empty(value) is False 

33 

34 def test_zero_is_empty_when_nonzero_flag_set(self): 

35 assert is_empty(0, nonzero=True) is True 

36 assert is_empty(0.0, nonzero=True) is True 

37 

38 def test_nonzero_number_not_empty_with_flag(self): 

39 assert is_empty(1, nonzero=True) is False 

40 assert is_empty(0.25, nonzero=True) is False 

41 

42 def test_nonzero_flag_does_not_affect_non_numeric(self): 

43 assert is_empty('valid_string', nonzero=True) is False 

44 

45 

46class TestValidateDateString: 

47 

48 @pytest.mark.parametrize('value', ['2023-01-01', '1901-12-31', '2099-01-01']) 

49 def test_valid_yyyy_mm_dd(self, value): 

50 assert validate_date_string(value) is True 

51 

52 @pytest.mark.parametrize('value', ['01/01/2023', '12/31/1901', '01/01/2099']) 

53 def test_valid_mm_dd_yyyy(self, value): 

54 assert validate_date_string(value) is True 

55 

56 @pytest.mark.parametrize('value', ['1899-12-31', '2100-01-01', '12/31/1899', '01/01/2100']) 

57 def test_out_of_range_year(self, value): 

58 assert validate_date_string(value) is False 

59 

60 @pytest.mark.parametrize('value', ['2023-00-01', '2023-13-01', '00/01/2023', '13/01/2023']) 

61 def test_invalid_month(self, value): 

62 assert validate_date_string(value) is False 

63 

64 @pytest.mark.parametrize('value', ['2023-01-00', '2023-01-32', '01/00/2023', '01/32/2023']) 

65 def test_invalid_day(self, value): 

66 assert validate_date_string(value) is False 

67 

68 @pytest.mark.parametrize('value', ['2023/01/01', '01-01-2023', '2023.01.01', '01.01.2023', 'invalid_date']) 

69 def test_unrecognized_format(self, value): 

70 assert validate_date_string(value) is False 

71 

72 def test_allow_empty_true(self): 

73 assert validate_date_string('', allow_empty=True) is True 

74 assert validate_date_string(None, allow_empty=True) is True 

75 

76 def test_allow_empty_false_raises_on_empty_string(self): 

77 with pytest.raises(Exception): 

78 validate_date_string('', allow_empty=False) 

79 

80 def test_allow_empty_false_raises_on_none(self): 

81 with pytest.raises(Exception): 

82 validate_date_string(None, allow_empty=False) 

83 

84 

85class TestNormalizeAmounts: 

86 

87 def test_dollar_and_comma_stripped(self): 

88 row = pd.Series({'Amount': '$4,000.00', 'Sales Tax': '', 'Tips': ''}) 

89 result = normalize_amounts(row) 

90 assert result['Amount'] == 4000.0 

91 

92 def test_plain_float_string_converted(self): 

93 row = pd.Series({'Amount': '40.00', 'Sales Tax': '1.50', 'Tips': ''}) 

94 result = normalize_amounts(row) 

95 assert result['Amount'] == 40.0 

96 assert result['Sales Tax'] == 1.5 

97 

98 def test_already_numeric_left_alone(self): 

99 row = pd.Series({'Amount': 40.0, 'Sales Tax': 1.5, 'Tips': 0.0}) 

100 result = normalize_amounts(row) 

101 assert result['Amount'] == 40.0 

102 

103 def test_empty_values_untouched(self): 

104 row = pd.Series({'Amount': '40.00', 'Sales Tax': '', 'Tips': None}) 

105 result = normalize_amounts(row) 

106 assert result['Sales Tax'] == '' 

107 assert result['Tips'] is None 

108 

109 

110class TestDfDateFilter: 

111 

112 @pytest.fixture 

113 def dates_df(self): 

114 return pd.DataFrame({ 

115 'Date': pd.to_datetime(['2023-01-01', '2023-02-01', '2023-03-01']), 

116 'Amount': [10.0, 20.0, 30.0], 

117 }) 

118 

119 def test_filters_inclusive_range(self, dates_df): 

120 result = df_date_filter(dates_df, pd.to_datetime('2023-01-15'), pd.to_datetime('2023-02-15')) 

121 assert len(result) == 1 

122 assert result.iloc[0]['Amount'] == 20.0 

123 

124 def test_no_bounds_returns_unchanged(self, dates_df): 

125 result = df_date_filter(dates_df, None, None) 

126 assert len(result) == 3 

127 

128 def test_only_start_bound(self, dates_df): 

129 result = df_date_filter(dates_df, pd.to_datetime('2023-02-01'), None) 

130 assert len(result) == 2 

131 

132 def test_only_end_bound(self, dates_df): 

133 result = df_date_filter(dates_df, None, pd.to_datetime('2023-02-01')) 

134 assert len(result) == 2 

135 

136 def test_empty_result_raises(self, dates_df): 

137 with pytest.raises(Exception): 

138 df_date_filter(dates_df, pd.to_datetime('2024-01-01'), pd.to_datetime('2024-02-01'))