@@ -78,6 +78,106 @@ def test_decode_lenient_mode(self):
7878 assert data ["name" ] == "Alice"
7979
8080
81+ class TestStatsFlag :
82+ """Tests for the --stats CLI flag."""
83+
84+ def test_stats_flag_in_help (self , tmp_path ):
85+ """Test that --stats appears in help text."""
86+ with patch ("sys.argv" , ["toon" , "--help" ]):
87+ with pytest .raises (SystemExit ):
88+ main ()
89+
90+ def test_stats_with_file_input (self , tmp_path ):
91+ """Test --stats with file input."""
92+ input_file = tmp_path / "test.json"
93+ input_file .write_text ('{"users": [{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}]}' )
94+
95+ with patch ("sys.stdout" , new_callable = StringIO ) as mock_stdout :
96+ with patch ("sys.argv" , ["toon" , str (input_file ), "--stats" ]):
97+ result = main ()
98+ assert result == 0
99+ output = mock_stdout .getvalue ()
100+ assert "users[2" in output
101+ assert "Format Comparison" in output or "Savings" in output
102+
103+ def test_stats_with_stdin (self ):
104+ """Test --stats with stdin input."""
105+ input_data = '{"items": ["a", "b", "c"]}'
106+
107+ with patch ("sys.stdin" , StringIO (input_data )):
108+ with patch ("sys.stdout" , new_callable = StringIO ) as mock_stdout :
109+ with patch ("sys.argv" , ["toon" , "-" , "--stats" ]):
110+ result = main ()
111+ assert result == 0
112+ output = mock_stdout .getvalue ()
113+ assert "items[3" in output
114+
115+ def test_stats_ignored_in_decode_mode (self , tmp_path ):
116+ """Test that --stats is ignored when decoding."""
117+ input_file = tmp_path / "test.toon"
118+ input_file .write_text ("items[2]: a,b" )
119+
120+ with patch ("sys.stdout" , new_callable = StringIO ) as mock_stdout :
121+ with patch ("sys.stderr" , new_callable = StringIO ) as mock_stderr :
122+ with patch ("sys.argv" , ["toon" , str (input_file ), "--decode" , "--stats" ]):
123+ result = main ()
124+ assert result == 0
125+ output = mock_stdout .getvalue ()
126+ assert '"items"' in output
127+ if mock_stderr .getvalue ():
128+ assert "warning" in mock_stderr .getvalue ().lower ()
129+
130+ def test_stats_with_different_delimiters (self , tmp_path ):
131+ """Test that --stats works with alternative delimiters."""
132+ input_file = tmp_path / "test.json"
133+ input_file .write_text ('{"data": [{"a": 1, "b": 2}]}' )
134+
135+ # Test with tab delimiter
136+ with patch ("sys.stdout" , new_callable = StringIO ):
137+ with patch ("sys.argv" , ["toon" , str (input_file ), "--delimiter" , "\t " , "--stats" ]):
138+ result = main ()
139+ assert result == 0
140+
141+ # Test with pipe delimiter
142+ with patch ("sys.stdout" , new_callable = StringIO ):
143+ with patch ("sys.argv" , ["toon" , str (input_file ), "--delimiter" , "|" , "--stats" ]):
144+ result = main ()
145+ assert result == 0
146+
147+ def test_stats_without_tiktoken (self , tmp_path , monkeypatch ):
148+ """Test graceful handling when tiktoken is not available."""
149+ input_file = tmp_path / "test.json"
150+ input_file .write_text ('{"test": 123}' )
151+
152+ # Mock compare_formats to raise RuntimeError (simulating missing tiktoken)
153+ def mock_compare_formats (data ):
154+ raise RuntimeError ("tiktoken is required" )
155+
156+ with patch ("toon_format.cli.compare_formats" , side_effect = mock_compare_formats ):
157+ with patch ("sys.stdout" , new_callable = StringIO ):
158+ with patch ("sys.stderr" , new_callable = StringIO ) as mock_stderr :
159+ with patch ("sys.argv" , ["toon" , str (input_file ), "--stats" ]):
160+ result = main ()
161+ assert result == 0
162+ assert "tiktoken" in mock_stderr .getvalue ()
163+
164+ def test_stats_with_output_file (self , tmp_path ):
165+ """Test --stats with -o output option."""
166+ input_file = tmp_path / "test.json"
167+ input_file .write_text ('{"test": 123}' )
168+ output_file = tmp_path / "output.toon"
169+
170+ with patch ("sys.stdout" , new_callable = StringIO ) as mock_stdout :
171+ with patch ("sys.argv" , ["toon" , str (input_file ), "-o" , str (output_file ), "--stats" ]):
172+ result = main ()
173+ assert result == 0
174+ assert output_file .exists ()
175+ assert (
176+ "Format Comparison" in mock_stdout .getvalue ()
177+ or "Savings" in mock_stdout .getvalue ()
178+ )
179+
180+
81181class TestCLIMain :
82182 """Integration tests for the main CLI function."""
83183
0 commit comments