Coverage for tests/test_cli.py: 100%

75 statements  

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

1import pytest 

2 

3import sankey_cashflow.cli as cli_module 

4from sankey_cashflow.cli import build_arg_parser 

5 

6 

7class TestBuildArgParser: 

8 

9 def test_defaults(self): 

10 args = build_arg_parser().parse_args([]) 

11 assert args.source is None 

12 assert args.sheet is None 

13 assert args.srcmap is None 

14 assert args.range is False 

15 assert args.separate_tax is False 

16 assert args.verbose is False 

17 assert args.creds is None 

18 assert args.distributions is False 

19 assert args.tags is None 

20 assert args.exclude is None 

21 assert args.tag_override is False 

22 assert args.stores is None 

23 assert args.all_time is False 

24 assert args.feed_in is False 

25 assert args.audit is False 

26 assert args.recurring is False 

27 assert args.hover is None 

28 assert args.dtype is None 

29 

30 def test_parses_source_and_srcmap(self): 

31 args = build_arg_parser().parse_args(['-s', 'data.csv', '--srcmap', 'labels.csv']) 

32 assert args.source == 'data.csv' 

33 assert args.srcmap == 'labels.csv' 

34 

35 def test_parses_store_true_flags(self): 

36 args = build_arg_parser().parse_args([ 

37 '-r', '--separate_tax', '-v', '--distributions', '--tag_override', 

38 '--all_time', '--feed_in', '--audit', '--recurring' 

39 ]) 

40 assert args.range is True 

41 assert args.separate_tax is True 

42 assert args.verbose is True 

43 assert args.distributions is True 

44 assert args.tag_override is True 

45 assert args.all_time is True 

46 assert args.feed_in is True 

47 assert args.audit is True 

48 assert args.recurring is True 

49 

50 def test_parses_string_options(self): 

51 args = build_arg_parser().parse_args([ 

52 '--tags', 'Ford, Dodge', '--exclude', 'Onetime', '--stores', "Trader Joe's", 

53 '--hover', 'Description', '--dtype', 'line', '--creds', 'creds.json', '-t', 'Sheet1' 

54 ]) 

55 assert args.tags == 'Ford, Dodge' 

56 assert args.exclude == 'Onetime' 

57 assert args.stores == "Trader Joe's" 

58 assert args.hover == 'Description' 

59 assert args.dtype == 'line' 

60 assert args.creds == 'creds.json' 

61 assert args.sheet == 'Sheet1' 

62 

63 

64class TestMainEndToEnd: 

65 """ 

66 Integration tests driving cli.main() the same way the installed `sankeyd` command would, 

67 against sample_data/. save_report() and Figure.show() are stubbed out so tests don't write 

68 report files into the repo or try to open a browser. 

69 """ 

70 

71 def test_main_runs_sankey_pipeline(self, monkeypatch): 

72 monkeypatch.setattr(cli_module, 'save_report', lambda *a, **kw: None) 

73 shown = [] 

74 monkeypatch.setattr('plotly.graph_objects.Figure.show', lambda self, *a, **kw: shown.append(True)) 

75 cli_module.main([ 

76 '--source', 'sample_data/expenses.csv', '--srcmap', 'sample_data/labels.csv', '--all_time' 

77 ]) 

78 assert shown == [True] 

79 

80 def test_main_runs_line_pipeline(self, monkeypatch): 

81 monkeypatch.setattr(cli_module, 'save_report', lambda *a, **kw: None) 

82 monkeypatch.setattr('builtins.input', lambda *a, **kw: 'week') 

83 shown = [] 

84 monkeypatch.setattr('plotly.graph_objects.Figure.show', lambda self, *a, **kw: shown.append(True)) 

85 cli_module.main([ 

86 '--source', 'sample_data/expenses.csv', '--srcmap', 'sample_data/labels.csv', '--all_time', 

87 '--dtype', 'line' 

88 ]) 

89 assert shown == [True] 

90 

91 def test_main_audit_mode_exits_without_generating_diagram(self, monkeypatch, tmp_path): 

92 monkeypatch.setattr(cli_module, 'save_report', lambda *a, **kw: None) 

93 audit_csv = tmp_path / 'bank.csv' 

94 audit_csv.write_text('Date,Amount,Description\n1/1/23,-4000.00,Boeing\n') 

95 monkeypatch.setattr('builtins.input', lambda *a, **kw: str(audit_csv)) 

96 shown = [] 

97 monkeypatch.setattr('plotly.graph_objects.Figure.show', lambda self, *a, **kw: shown.append(True)) 

98 with pytest.raises(SystemExit): 

99 cli_module.main([ 

100 '--source', 'sample_data/expenses.csv', '--srcmap', 'sample_data/labels.csv', 

101 '--all_time', '--audit' 

102 ]) 

103 assert shown == [] 

104 

105 def test_main_invalid_source_raises(self): 

106 # Fails during AppSettings construction/validation, before any fetch_data() call - 

107 # this is a plain Exception, not the SystemExit that fetch_data()'s own failure path raises. 

108 with pytest.raises(Exception): 

109 cli_module.main(['--source', 'does_not_exist.csv'])