simplecov-mcp 1.0.1 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +98 -50
- data/docs/{ARCHITECTURE.md → dev/ARCHITECTURE.md} +11 -10
- data/docs/dev/BRANCH_ONLY_COVERAGE.md +158 -0
- data/docs/{DEVELOPMENT.md → dev/DEVELOPMENT.md} +2 -1
- data/docs/dev/README.md +10 -0
- data/docs/dev/RELEASING.md +146 -0
- data/docs/{arch-decisions → dev/arch-decisions}/001-x-arch-decision.md +3 -1
- data/docs/{arch-decisions → dev/arch-decisions}/002-x-arch-decision.md +7 -5
- data/docs/{arch-decisions → dev/arch-decisions}/003-x-arch-decision.md +2 -0
- data/docs/{arch-decisions → dev/arch-decisions}/004-x-arch-decision.md +6 -2
- data/docs/{arch-decisions → dev/arch-decisions}/005-x-arch-decision.md +4 -2
- data/docs/{arch-decisions → dev/arch-decisions}/README.md +3 -3
- data/docs/{presentations → dev/presentations}/simplecov-mcp-presentation.md +28 -22
- data/docs/fixtures/demo_project/README.md +9 -0
- data/docs/{ADVANCED_USAGE.md → user/ADVANCED_USAGE.md} +129 -319
- data/docs/user/CLI_FALLBACK_FOR_LLMS.md +34 -0
- data/docs/user/CLI_USAGE.md +750 -0
- data/docs/{ERROR_HANDLING.md → user/ERROR_HANDLING.md} +12 -12
- data/docs/user/EXAMPLES.md +588 -0
- data/docs/user/INSTALLATION.md +130 -0
- data/docs/{LIBRARY_API.md → user/LIBRARY_API.md} +90 -32
- data/docs/{MCP_INTEGRATION.md → user/MCP_INTEGRATION.md} +36 -34
- data/docs/user/README.md +14 -0
- data/docs/{TROUBLESHOOTING.md → user/TROUBLESHOOTING.md} +21 -100
- data/docs/user/V2-BREAKING-CHANGES.md +472 -0
- data/exe/simplecov-mcp +1 -1
- data/lib/simplecov_mcp/{cli_config.rb → app_config.rb} +12 -12
- data/lib/simplecov_mcp/app_context.rb +1 -1
- data/lib/simplecov_mcp/base_tool.rb +66 -38
- data/lib/simplecov_mcp/cli.rb +67 -123
- data/lib/simplecov_mcp/commands/base_command.rb +16 -27
- data/lib/simplecov_mcp/commands/command_factory.rb +8 -2
- data/lib/simplecov_mcp/commands/detailed_command.rb +16 -2
- data/lib/simplecov_mcp/commands/list_command.rb +1 -1
- data/lib/simplecov_mcp/commands/raw_command.rb +18 -2
- data/lib/simplecov_mcp/commands/summary_command.rb +20 -3
- data/lib/simplecov_mcp/commands/totals_command.rb +53 -0
- data/lib/simplecov_mcp/commands/uncovered_command.rb +24 -5
- data/lib/simplecov_mcp/commands/validate_command.rb +60 -0
- data/lib/simplecov_mcp/commands/version_command.rb +19 -4
- data/lib/simplecov_mcp/config_parser.rb +32 -0
- data/lib/simplecov_mcp/constants.rb +3 -3
- data/lib/simplecov_mcp/coverage_reporter.rb +31 -0
- data/lib/simplecov_mcp/error_handler.rb +81 -40
- data/lib/simplecov_mcp/error_handler_factory.rb +2 -2
- data/lib/simplecov_mcp/errors.rb +12 -19
- data/lib/simplecov_mcp/formatters/source_formatter.rb +23 -19
- data/lib/simplecov_mcp/formatters.rb +51 -0
- data/lib/simplecov_mcp/mcp_server.rb +9 -7
- data/lib/simplecov_mcp/mode_detector.rb +6 -5
- data/lib/simplecov_mcp/model.rb +122 -88
- data/lib/simplecov_mcp/option_normalizers.rb +39 -18
- data/lib/simplecov_mcp/option_parser_builder.rb +85 -72
- data/lib/simplecov_mcp/option_parsers/env_options_parser.rb +3 -5
- data/lib/simplecov_mcp/option_parsers/error_helper.rb +18 -17
- data/lib/simplecov_mcp/path_relativizer.rb +17 -14
- data/lib/simplecov_mcp/predicate_evaluator.rb +72 -0
- data/lib/simplecov_mcp/presenters/base_coverage_presenter.rb +1 -3
- data/lib/simplecov_mcp/presenters/coverage_detailed_presenter.rb +1 -3
- data/lib/simplecov_mcp/presenters/coverage_raw_presenter.rb +1 -3
- data/lib/simplecov_mcp/presenters/coverage_summary_presenter.rb +1 -3
- data/lib/simplecov_mcp/presenters/coverage_uncovered_presenter.rb +1 -3
- data/lib/simplecov_mcp/presenters/project_coverage_presenter.rb +1 -3
- data/lib/simplecov_mcp/presenters/project_totals_presenter.rb +27 -0
- data/lib/simplecov_mcp/resolvers/coverage_line_resolver.rb +14 -18
- data/lib/simplecov_mcp/resolvers/resultset_path_resolver.rb +7 -9
- data/lib/simplecov_mcp/resultset_loader.rb +20 -25
- data/lib/simplecov_mcp/staleness_checker.rb +50 -46
- data/lib/simplecov_mcp/table_formatter.rb +64 -0
- data/lib/simplecov_mcp/tools/all_files_coverage_tool.rb +20 -50
- data/lib/simplecov_mcp/tools/coverage_detailed_tool.rb +13 -7
- data/lib/simplecov_mcp/tools/coverage_raw_tool.rb +12 -7
- data/lib/simplecov_mcp/tools/coverage_summary_tool.rb +13 -8
- data/lib/simplecov_mcp/tools/coverage_table_tool.rb +20 -60
- data/lib/simplecov_mcp/tools/coverage_totals_tool.rb +44 -0
- data/lib/simplecov_mcp/tools/help_tool.rb +38 -66
- data/lib/simplecov_mcp/tools/uncovered_lines_tool.rb +13 -8
- data/lib/simplecov_mcp/tools/validate_tool.rb +72 -0
- data/lib/simplecov_mcp/tools/version_tool.rb +7 -14
- data/lib/simplecov_mcp/util.rb +18 -12
- data/lib/simplecov_mcp/version.rb +1 -1
- data/lib/simplecov_mcp.rb +23 -29
- data/spec/all_files_coverage_tool_spec.rb +4 -3
- data/spec/{cli_config_spec.rb → app_config_spec.rb} +31 -26
- data/spec/base_tool_spec.rb +17 -14
- data/spec/cli/show_default_report_spec.rb +2 -2
- data/spec/cli_enumerated_options_spec.rb +31 -9
- data/spec/cli_error_spec.rb +46 -23
- data/spec/cli_format_spec.rb +123 -0
- data/spec/cli_json_options_spec.rb +50 -0
- data/spec/cli_source_spec.rb +11 -63
- data/spec/cli_spec.rb +82 -97
- data/spec/cli_usage_spec.rb +15 -15
- data/spec/commands/base_command_spec.rb +12 -92
- data/spec/commands/command_factory_spec.rb +7 -3
- data/spec/commands/detailed_command_spec.rb +10 -24
- data/spec/commands/list_command_spec.rb +28 -0
- data/spec/commands/raw_command_spec.rb +43 -20
- data/spec/commands/summary_command_spec.rb +10 -23
- data/spec/commands/totals_command_spec.rb +34 -0
- data/spec/commands/uncovered_command_spec.rb +29 -23
- data/spec/commands/validate_command_spec.rb +213 -0
- data/spec/commands/version_command_spec.rb +38 -0
- data/spec/constants_spec.rb +3 -3
- data/spec/coverage_reporter_spec.rb +102 -0
- data/spec/coverage_table_tool_spec.rb +21 -10
- data/spec/coverage_totals_tool_spec.rb +37 -0
- data/spec/error_handler_spec.rb +120 -4
- data/spec/error_mode_spec.rb +18 -22
- data/spec/errors_edge_cases_spec.rb +101 -28
- data/spec/errors_stale_spec.rb +34 -0
- data/spec/file_based_mcp_tools_spec.rb +6 -6
- data/spec/fixtures/project1/lib/bar.rb +2 -0
- data/spec/fixtures/project1/lib/foo.rb +2 -0
- data/spec/help_tool_spec.rb +2 -18
- data/spec/integration_spec.rb +103 -161
- data/spec/logging_fallback_spec.rb +3 -3
- data/spec/mcp_server_integration_spec.rb +1 -1
- data/spec/mcp_server_spec.rb +70 -53
- data/spec/mode_detector_spec.rb +46 -41
- data/spec/model_error_handling_spec.rb +139 -78
- data/spec/model_staleness_spec.rb +13 -13
- data/spec/option_normalizers_spec.rb +111 -112
- data/spec/option_parsers/env_options_parser_spec.rb +25 -37
- data/spec/option_parsers/error_helper_spec.rb +56 -56
- data/spec/path_relativizer_spec.rb +15 -0
- data/spec/presenters/coverage_detailed_presenter_spec.rb +1 -1
- data/spec/presenters/coverage_summary_presenter_spec.rb +1 -1
- data/spec/presenters/coverage_uncovered_presenter_spec.rb +1 -1
- data/spec/presenters/project_coverage_presenter_spec.rb +9 -8
- data/spec/presenters/project_totals_presenter_spec.rb +144 -0
- data/spec/resolvers/coverage_line_resolver_spec.rb +261 -36
- data/spec/resolvers/resultset_path_resolver_spec.rb +13 -8
- data/spec/shared_examples/file_based_mcp_tools.rb +23 -18
- data/spec/shared_examples/formatted_command_examples.rb +64 -0
- data/spec/simple_cov_mcp_module_spec.rb +24 -3
- data/spec/simplecov_mcp/formatters/source_formatter_spec.rb +267 -0
- data/spec/simplecov_mcp/formatters_spec.rb +76 -0
- data/spec/simplecov_mcp/presenters/base_coverage_presenter_spec.rb +79 -0
- data/spec/simplecov_mcp_model_spec.rb +97 -47
- data/spec/simplecov_mcp_opts_spec.rb +42 -39
- data/spec/spec_helper.rb +27 -92
- data/spec/staleness_checker_spec.rb +10 -9
- data/spec/staleness_more_spec.rb +4 -4
- data/spec/support/cli_helpers.rb +22 -0
- data/spec/support/control_flow_helpers.rb +20 -0
- data/spec/support/fake_mcp.rb +40 -0
- data/spec/support/io_helpers.rb +29 -0
- data/spec/support/mcp_helpers.rb +35 -0
- data/spec/support/mcp_runner.rb +10 -8
- data/spec/support/mocking_helpers.rb +30 -0
- data/spec/table_format_spec.rb +70 -0
- data/spec/tools/validate_tool_spec.rb +132 -0
- data/spec/tools_error_handling_spec.rb +34 -48
- data/spec/util_spec.rb +5 -4
- data/spec/version_spec.rb +7 -7
- data/spec/version_tool_spec.rb +20 -22
- metadata +90 -23
- data/docs/BRANCH_ONLY_COVERAGE.md +0 -81
- data/docs/CLI_USAGE.md +0 -637
- data/docs/EXAMPLES.md +0 -430
- data/docs/INSTALLATION.md +0 -352
- data/spec/cli_success_predicate_spec.rb +0 -141
|
@@ -5,25 +5,17 @@ require 'spec_helper'
|
|
|
5
5
|
RSpec.describe SimpleCovMcp::OptionNormalizers do
|
|
6
6
|
describe '.normalize_sort_order' do
|
|
7
7
|
context 'with strict mode (default)' do
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
it 'normalizes "descending" to :descending' do
|
|
21
|
-
expect(described_class.normalize_sort_order('descending')).to eq(:descending)
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
it 'is case-insensitive' do
|
|
25
|
-
expect(described_class.normalize_sort_order('ASCENDING')).to eq(:ascending)
|
|
26
|
-
expect(described_class.normalize_sort_order('Descending')).to eq(:descending)
|
|
8
|
+
[
|
|
9
|
+
['a', :ascending],
|
|
10
|
+
['ascending', :ascending],
|
|
11
|
+
['d', :descending],
|
|
12
|
+
['descending', :descending],
|
|
13
|
+
['ASCENDING', :ascending],
|
|
14
|
+
['Descending', :descending]
|
|
15
|
+
].each do |input, expected|
|
|
16
|
+
it "normalizes '#{input}' to #{expected}" do
|
|
17
|
+
expect(described_class.normalize_sort_order(input)).to eq(expected)
|
|
18
|
+
end
|
|
27
19
|
end
|
|
28
20
|
|
|
29
21
|
it 'raises OptionParser::InvalidArgument for invalid values' do
|
|
@@ -45,33 +37,24 @@ RSpec.describe SimpleCovMcp::OptionNormalizers do
|
|
|
45
37
|
|
|
46
38
|
describe '.normalize_source_mode' do
|
|
47
39
|
context 'with strict mode (default)' do
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
it 'normalizes "uncovered" to :uncovered' do
|
|
69
|
-
expect(described_class.normalize_source_mode('uncovered')).to eq(:uncovered)
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
it 'is case-insensitive' do
|
|
73
|
-
expect(described_class.normalize_source_mode('FULL')).to eq(:full)
|
|
74
|
-
expect(described_class.normalize_source_mode('Uncovered')).to eq(:uncovered)
|
|
40
|
+
[nil, ''].each do |input|
|
|
41
|
+
it "raises OptionParser::InvalidArgument for #{input.inspect}" do
|
|
42
|
+
expect { described_class.normalize_source_mode(input) }
|
|
43
|
+
.to raise_error(OptionParser::InvalidArgument, /invalid argument/)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
[
|
|
48
|
+
['f', :full],
|
|
49
|
+
['full', :full],
|
|
50
|
+
['u', :uncovered],
|
|
51
|
+
['uncovered', :uncovered],
|
|
52
|
+
['FULL', :full],
|
|
53
|
+
['Uncovered', :uncovered]
|
|
54
|
+
].each do |input, expected|
|
|
55
|
+
it "normalizes '#{input}' to #{expected}" do
|
|
56
|
+
expect(described_class.normalize_source_mode(input)).to eq(expected)
|
|
57
|
+
end
|
|
75
58
|
end
|
|
76
59
|
|
|
77
60
|
it 'raises OptionParser::InvalidArgument for invalid values' do
|
|
@@ -91,84 +74,70 @@ RSpec.describe SimpleCovMcp::OptionNormalizers do
|
|
|
91
74
|
end
|
|
92
75
|
end
|
|
93
76
|
|
|
94
|
-
describe '.
|
|
77
|
+
describe '.normalize_staleness' do
|
|
95
78
|
context 'with strict mode (default)' do
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
it 'normalizes "error" to :error' do
|
|
109
|
-
expect(described_class.normalize_stale_mode('error')).to eq(:error)
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
it 'is case-insensitive' do
|
|
113
|
-
expect(described_class.normalize_stale_mode('OFF')).to eq(:off)
|
|
114
|
-
expect(described_class.normalize_stale_mode('Error')).to eq(:error)
|
|
79
|
+
[
|
|
80
|
+
['o', :off],
|
|
81
|
+
['off', :off],
|
|
82
|
+
['e', :error],
|
|
83
|
+
['error', :error],
|
|
84
|
+
['OFF', :off],
|
|
85
|
+
['Error', :error]
|
|
86
|
+
].each do |input, expected|
|
|
87
|
+
it "normalizes '#{input}' to #{expected}" do
|
|
88
|
+
expect(described_class.normalize_staleness(input)).to eq(expected)
|
|
89
|
+
end
|
|
115
90
|
end
|
|
116
91
|
|
|
117
92
|
it 'raises OptionParser::InvalidArgument for invalid values' do
|
|
118
|
-
expect { described_class.
|
|
93
|
+
expect { described_class.normalize_staleness('invalid') }
|
|
119
94
|
.to raise_error(OptionParser::InvalidArgument, /invalid argument: invalid/)
|
|
120
95
|
end
|
|
121
96
|
end
|
|
122
97
|
|
|
123
98
|
context 'with strict: false' do
|
|
124
99
|
it 'returns nil for invalid values' do
|
|
125
|
-
expect(described_class.
|
|
100
|
+
expect(described_class.normalize_staleness('invalid', strict: false)).to be_nil
|
|
126
101
|
end
|
|
127
102
|
|
|
128
103
|
it 'still normalizes valid values' do
|
|
129
|
-
expect(described_class.
|
|
104
|
+
expect(described_class.normalize_staleness('e', strict: false)).to eq(:error)
|
|
130
105
|
end
|
|
131
106
|
end
|
|
132
107
|
end
|
|
133
108
|
|
|
134
109
|
describe '.normalize_error_mode' do
|
|
135
110
|
context 'with strict mode (default)' do
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
it 'raises OptionParser::InvalidArgument for invalid values' do
|
|
159
|
-
expect { described_class.normalize_error_mode('invalid') }
|
|
160
|
-
.to raise_error(OptionParser::InvalidArgument, /invalid argument: invalid/)
|
|
111
|
+
[
|
|
112
|
+
['off', :off],
|
|
113
|
+
['o', :off],
|
|
114
|
+
['log', :log],
|
|
115
|
+
['l', :log],
|
|
116
|
+
['debug', :debug],
|
|
117
|
+
['d', :debug],
|
|
118
|
+
['OFF', :off],
|
|
119
|
+
['Log', :log],
|
|
120
|
+
['DEBUG', :debug]
|
|
121
|
+
].each do |input, expected|
|
|
122
|
+
it "normalizes '#{input}' to #{expected}" do
|
|
123
|
+
expect(described_class.normalize_error_mode(input)).to eq(expected)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
['invalid', 'on', 'trace'].each do |input|
|
|
128
|
+
it "raises OptionParser::InvalidArgument for '#{input}'" do
|
|
129
|
+
expect { described_class.normalize_error_mode(input) }
|
|
130
|
+
.to raise_error(OptionParser::InvalidArgument, /invalid argument: #{input}/)
|
|
131
|
+
end
|
|
161
132
|
end
|
|
162
133
|
end
|
|
163
134
|
|
|
164
|
-
context 'with strict: false and default: :
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
it 'returns default for nil' do
|
|
171
|
-
expect(described_class.normalize_error_mode(nil, strict: false, default: :on)).to eq(:on)
|
|
135
|
+
context 'with strict: false and default: :log' do
|
|
136
|
+
[['invalid', :log], [nil, :log]].each do |input, expected|
|
|
137
|
+
it "returns default #{expected} for #{input.inspect}" do
|
|
138
|
+
expect(described_class.normalize_error_mode(input, strict: false,
|
|
139
|
+
default: :log)).to eq(expected)
|
|
140
|
+
end
|
|
172
141
|
end
|
|
173
142
|
|
|
174
143
|
it 'still normalizes valid values' do
|
|
@@ -184,21 +153,51 @@ RSpec.describe SimpleCovMcp::OptionNormalizers do
|
|
|
184
153
|
end
|
|
185
154
|
end
|
|
186
155
|
|
|
187
|
-
describe '
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
156
|
+
describe '.normalize_format' do
|
|
157
|
+
context 'with strict mode (default)' do
|
|
158
|
+
[
|
|
159
|
+
['t', :table],
|
|
160
|
+
['table', :table],
|
|
161
|
+
['j', :json],
|
|
162
|
+
['json', :json],
|
|
163
|
+
['pretty_json', :pretty_json],
|
|
164
|
+
['pretty-json', :pretty_json],
|
|
165
|
+
['y', :yaml],
|
|
166
|
+
['yaml', :yaml],
|
|
167
|
+
['a', :awesome_print],
|
|
168
|
+
['awesome_print', :awesome_print],
|
|
169
|
+
['ap', :awesome_print],
|
|
170
|
+
['TABLE', :table],
|
|
171
|
+
['Json', :json]
|
|
172
|
+
].each do |input, expected|
|
|
173
|
+
it "normalizes '#{input}' to #{expected}" do
|
|
174
|
+
expect(described_class.normalize_format(input)).to eq(expected)
|
|
175
|
+
end
|
|
176
|
+
end
|
|
191
177
|
|
|
192
|
-
|
|
193
|
-
|
|
178
|
+
it 'raises OptionParser::InvalidArgument for invalid values' do
|
|
179
|
+
expect { described_class.normalize_format('invalid') }
|
|
180
|
+
.to raise_error(OptionParser::InvalidArgument, /invalid argument: invalid/)
|
|
181
|
+
end
|
|
194
182
|
end
|
|
195
183
|
|
|
196
|
-
|
|
197
|
-
|
|
184
|
+
context 'with strict: false' do
|
|
185
|
+
it 'returns nil for invalid values' do
|
|
186
|
+
expect(described_class.normalize_format('invalid', strict: false)).to be_nil
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
it 'still normalizes valid values' do
|
|
190
|
+
expect(described_class.normalize_format('json', strict: false)).to eq(:json)
|
|
191
|
+
end
|
|
198
192
|
end
|
|
193
|
+
end
|
|
199
194
|
|
|
200
|
-
|
|
201
|
-
|
|
195
|
+
describe 'constant maps' do
|
|
196
|
+
[:SORT_ORDER_MAP, :SOURCE_MODE_MAP, :STALENESS_MAP, :ERROR_MODE_MAP,
|
|
197
|
+
:FORMAT_MAP].each do |const|
|
|
198
|
+
it "has frozen #{const}" do
|
|
199
|
+
expect(described_class.const_get(const)).to be_frozen
|
|
200
|
+
end
|
|
202
201
|
end
|
|
203
202
|
end
|
|
204
203
|
end
|
|
@@ -30,8 +30,8 @@ RSpec.describe SimpleCovMcp::OptionParsers::EnvOptionsParser do
|
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
it 'parses simple options correctly' do
|
|
33
|
-
ENV['SIMPLECOV_MCP_OPTS'] = '--error-mode off --json'
|
|
34
|
-
expect(parser.parse_env_opts).to eq(['--error-mode', 'off', '--json'])
|
|
33
|
+
ENV['SIMPLECOV_MCP_OPTS'] = '--error-mode off --format json'
|
|
34
|
+
expect(parser.parse_env_opts).to eq(['--error-mode', 'off', '--format', 'json'])
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
it 'handles quoted strings with spaces' do
|
|
@@ -106,9 +106,9 @@ RSpec.describe SimpleCovMcp::OptionParsers::EnvOptionsParser do
|
|
|
106
106
|
|
|
107
107
|
context 'when error-mode is found' do
|
|
108
108
|
it 'extracts error-mode with space separator' do
|
|
109
|
-
argv = ['--error-mode', '
|
|
109
|
+
argv = ['--error-mode', 'debug', '--other-option']
|
|
110
110
|
result = parser.pre_scan_error_mode(argv, error_mode_normalizer: error_mode_normalizer)
|
|
111
|
-
expect(result).to eq(:
|
|
111
|
+
expect(result).to eq(:debug)
|
|
112
112
|
end
|
|
113
113
|
|
|
114
114
|
it 'extracts error-mode with equals separator' do
|
|
@@ -125,9 +125,9 @@ RSpec.describe SimpleCovMcp::OptionParsers::EnvOptionsParser do
|
|
|
125
125
|
end
|
|
126
126
|
|
|
127
127
|
it 'returns first error-mode when multiple are present' do
|
|
128
|
-
argv = ['--error-mode', '
|
|
128
|
+
argv = ['--error-mode', 'log', '--error-mode', 'off']
|
|
129
129
|
result = parser.pre_scan_error_mode(argv, error_mode_normalizer: error_mode_normalizer)
|
|
130
|
-
expect(result).to eq(:
|
|
130
|
+
expect(result).to eq(:log)
|
|
131
131
|
end
|
|
132
132
|
end
|
|
133
133
|
|
|
@@ -145,25 +145,25 @@ RSpec.describe SimpleCovMcp::OptionParsers::EnvOptionsParser do
|
|
|
145
145
|
end
|
|
146
146
|
end
|
|
147
147
|
|
|
148
|
-
context '
|
|
148
|
+
context 'when handling errors during pre-scan' do
|
|
149
149
|
it 'returns nil when normalizer raises an error' do
|
|
150
|
-
faulty_normalizer = ->(
|
|
151
|
-
argv = ['--error-mode', '
|
|
150
|
+
faulty_normalizer = ->(_) { raise StandardError, 'Intentional error' }
|
|
151
|
+
argv = ['--error-mode', 'log']
|
|
152
152
|
|
|
153
153
|
result = parser.pre_scan_error_mode(argv, error_mode_normalizer: faulty_normalizer)
|
|
154
154
|
expect(result).to be_nil
|
|
155
155
|
end
|
|
156
156
|
|
|
157
157
|
it 'returns nil when normalizer raises ArgumentError' do
|
|
158
|
-
faulty_normalizer = ->(
|
|
159
|
-
argv = ['--error-mode', '
|
|
158
|
+
faulty_normalizer = ->(_) { raise ArgumentError, 'Bad argument' }
|
|
159
|
+
argv = ['--error-mode', 'log']
|
|
160
160
|
|
|
161
161
|
result = parser.pre_scan_error_mode(argv, error_mode_normalizer: faulty_normalizer)
|
|
162
162
|
expect(result).to be_nil
|
|
163
163
|
end
|
|
164
164
|
|
|
165
165
|
it 'returns nil when normalizer raises RuntimeError' do
|
|
166
|
-
faulty_normalizer = ->(
|
|
166
|
+
faulty_normalizer = ->(_) { raise 'Runtime problem' }
|
|
167
167
|
argv = ['--error-mode=off']
|
|
168
168
|
|
|
169
169
|
result = parser.pre_scan_error_mode(argv, error_mode_normalizer: faulty_normalizer)
|
|
@@ -172,14 +172,6 @@ RSpec.describe SimpleCovMcp::OptionParsers::EnvOptionsParser do
|
|
|
172
172
|
end
|
|
173
173
|
end
|
|
174
174
|
|
|
175
|
-
describe 'integration with ErrorHandlerFactory' do
|
|
176
|
-
it 'maps trace alias to an accepted error_mode' do
|
|
177
|
-
mode = parser.pre_scan_error_mode(['--error-mode', 'trace'])
|
|
178
|
-
expect { SimpleCovMcp::ErrorHandlerFactory.for_cli(error_mode: mode) }.not_to raise_error
|
|
179
|
-
expect(mode).to eq(:trace)
|
|
180
|
-
end
|
|
181
|
-
end
|
|
182
|
-
|
|
183
175
|
describe '#normalize_error_mode (private)' do
|
|
184
176
|
it 'normalizes "off" to :off' do
|
|
185
177
|
expect(parser.send(:normalize_error_mode, 'off')).to eq(:off)
|
|
@@ -187,29 +179,25 @@ RSpec.describe SimpleCovMcp::OptionParsers::EnvOptionsParser do
|
|
|
187
179
|
expect(parser.send(:normalize_error_mode, 'Off')).to eq(:off)
|
|
188
180
|
end
|
|
189
181
|
|
|
190
|
-
it 'normalizes "
|
|
191
|
-
expect(parser.send(:normalize_error_mode, '
|
|
192
|
-
expect(parser.send(:normalize_error_mode, '
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
it 'normalizes "trace" to :trace' do
|
|
196
|
-
expect(parser.send(:normalize_error_mode, 'trace')).to eq(:trace)
|
|
197
|
-
expect(parser.send(:normalize_error_mode, 'TRACE')).to eq(:trace)
|
|
182
|
+
it 'normalizes "log" to :log' do
|
|
183
|
+
expect(parser.send(:normalize_error_mode, 'log')).to eq(:log)
|
|
184
|
+
expect(parser.send(:normalize_error_mode, 'LOG')).to eq(:log)
|
|
185
|
+
expect(parser.send(:normalize_error_mode, 'Log')).to eq(:log)
|
|
198
186
|
end
|
|
199
187
|
|
|
200
|
-
it 'normalizes "
|
|
201
|
-
expect(parser.send(:normalize_error_mode, '
|
|
202
|
-
expect(parser.send(:normalize_error_mode, '
|
|
188
|
+
it 'normalizes "debug" to :debug' do
|
|
189
|
+
expect(parser.send(:normalize_error_mode, 'debug')).to eq(:debug)
|
|
190
|
+
expect(parser.send(:normalize_error_mode, 'DEBUG')).to eq(:debug)
|
|
203
191
|
end
|
|
204
192
|
|
|
205
|
-
it 'defaults unknown values to :
|
|
206
|
-
expect(parser.send(:normalize_error_mode, 'unknown')).to eq(:
|
|
207
|
-
expect(parser.send(:normalize_error_mode, 'invalid')).to eq(:
|
|
208
|
-
expect(parser.send(:normalize_error_mode, '')).to eq(:
|
|
193
|
+
it 'defaults unknown values to :log' do
|
|
194
|
+
expect(parser.send(:normalize_error_mode, 'unknown')).to eq(:log)
|
|
195
|
+
expect(parser.send(:normalize_error_mode, 'invalid')).to eq(:log)
|
|
196
|
+
expect(parser.send(:normalize_error_mode, '')).to eq(:log)
|
|
209
197
|
end
|
|
210
198
|
|
|
211
|
-
it 'handles nil by defaulting to :
|
|
212
|
-
expect(parser.send(:normalize_error_mode, nil)).to eq(:
|
|
199
|
+
it 'handles nil by defaulting to :log' do
|
|
200
|
+
expect(parser.send(:normalize_error_mode, nil)).to eq(:log)
|
|
213
201
|
end
|
|
214
202
|
end
|
|
215
203
|
|
|
@@ -2,6 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
require 'spec_helper'
|
|
4
4
|
|
|
5
|
+
OPTION_TESTS = {
|
|
6
|
+
staleness: {
|
|
7
|
+
long: '--staleness',
|
|
8
|
+
short: '-S',
|
|
9
|
+
pattern: /Valid values for --staleness: o\[ff\]|e\[rror\]/
|
|
10
|
+
},
|
|
11
|
+
source: {
|
|
12
|
+
long: '--source',
|
|
13
|
+
short: '-s',
|
|
14
|
+
pattern: /Valid values for --source: f\[ull\]|u\[ncovered\]/
|
|
15
|
+
},
|
|
16
|
+
error_mode: {
|
|
17
|
+
long: '--error-mode',
|
|
18
|
+
short: nil,
|
|
19
|
+
pattern: /Valid values for --error-mode: o\[ff\]|l\[og\]|d\[ebug\]/
|
|
20
|
+
},
|
|
21
|
+
sort_order: {
|
|
22
|
+
long: '--sort-order',
|
|
23
|
+
short: '-o',
|
|
24
|
+
pattern: /Valid values for --sort-order: a\[scending\]|d\[escending\]/
|
|
25
|
+
}
|
|
26
|
+
}.freeze
|
|
27
|
+
|
|
5
28
|
RSpec.describe SimpleCovMcp::OptionParsers::ErrorHelper do
|
|
6
29
|
subject(:helper) { described_class.new }
|
|
7
30
|
|
|
@@ -23,42 +46,16 @@ RSpec.describe SimpleCovMcp::OptionParsers::ErrorHelper do
|
|
|
23
46
|
# Helper method to test error output matches expected pattern
|
|
24
47
|
def expect_error_output(error:, argv:, pattern:)
|
|
25
48
|
expect do
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
# Ignore exit call
|
|
30
|
-
end
|
|
49
|
+
helper.handle_option_parser_error(error, argv: argv)
|
|
50
|
+
rescue SystemExit
|
|
51
|
+
# Ignore exit call
|
|
31
52
|
end.to output(pattern).to_stderr
|
|
32
53
|
end
|
|
33
54
|
|
|
34
|
-
# Test data for enumerated options
|
|
35
|
-
OPTION_TESTS = {
|
|
36
|
-
stale: {
|
|
37
|
-
long: '--stale',
|
|
38
|
-
short: '-S',
|
|
39
|
-
pattern: /Valid values for --stale: o\[ff\]|e\[rror\]/
|
|
40
|
-
},
|
|
41
|
-
source: {
|
|
42
|
-
long: '--source',
|
|
43
|
-
short: '-s',
|
|
44
|
-
pattern: /Valid values for --source: f\[ull\]|u\[ncovered\]/
|
|
45
|
-
},
|
|
46
|
-
error_mode: {
|
|
47
|
-
long: '--error-mode',
|
|
48
|
-
short: nil,
|
|
49
|
-
pattern: /Valid values for --error-mode: off\|on\|t\[race\]/
|
|
50
|
-
},
|
|
51
|
-
sort_order: {
|
|
52
|
-
long: '--sort-order',
|
|
53
|
-
short: '-o',
|
|
54
|
-
pattern: /Valid values for --sort-order: a\[scending\]|d\[escending\]/
|
|
55
|
-
}
|
|
56
|
-
}.freeze
|
|
57
|
-
|
|
58
55
|
describe '#handle_option_parser_error' do
|
|
59
56
|
context 'with invalid enumerated option values' do
|
|
60
|
-
OPTION_TESTS.
|
|
61
|
-
context "
|
|
57
|
+
OPTION_TESTS.each_value do |config|
|
|
58
|
+
context "when parsing #{config[:long]} option" do
|
|
62
59
|
let(:error) { OptionParser::InvalidArgument.new('invalid argument: xyz') }
|
|
63
60
|
|
|
64
61
|
it 'suggests valid values for space-separated form with invalid value' do
|
|
@@ -89,13 +86,13 @@ RSpec.describe SimpleCovMcp::OptionParsers::ErrorHelper do
|
|
|
89
86
|
end
|
|
90
87
|
end
|
|
91
88
|
|
|
92
|
-
context '
|
|
89
|
+
context 'when handling --staleness option edge cases' do
|
|
93
90
|
it 'suggests valid values when value is missing' do
|
|
94
|
-
error = OptionParser::InvalidArgument.new('missing argument: --
|
|
91
|
+
error = OptionParser::InvalidArgument.new('missing argument: --staleness')
|
|
95
92
|
expect_error_output(
|
|
96
93
|
error: error,
|
|
97
|
-
argv: ['--
|
|
98
|
-
pattern: /Valid values for --
|
|
94
|
+
argv: ['--staleness'],
|
|
95
|
+
pattern: /Valid values for --staleness: o\[ff\]|e\[rror\]/
|
|
99
96
|
)
|
|
100
97
|
end
|
|
101
98
|
|
|
@@ -103,8 +100,8 @@ RSpec.describe SimpleCovMcp::OptionParsers::ErrorHelper do
|
|
|
103
100
|
error = OptionParser::InvalidArgument.new('invalid argument: --other')
|
|
104
101
|
expect_error_output(
|
|
105
102
|
error: error,
|
|
106
|
-
argv: ['--
|
|
107
|
-
pattern: /Valid values for --
|
|
103
|
+
argv: ['--staleness', '--other-option'],
|
|
104
|
+
pattern: /Valid values for --staleness: o\[ff\]|e\[rror\]/
|
|
108
105
|
)
|
|
109
106
|
end
|
|
110
107
|
end
|
|
@@ -115,8 +112,8 @@ RSpec.describe SimpleCovMcp::OptionParsers::ErrorHelper do
|
|
|
115
112
|
error = OptionParser::InvalidArgument.new('invalid argument: bad')
|
|
116
113
|
expect_error_output(
|
|
117
114
|
error: error,
|
|
118
|
-
argv: ['--resultset', 'coverage', '--
|
|
119
|
-
pattern: /Valid values for --
|
|
115
|
+
argv: ['--resultset', 'coverage', '--staleness', 'bad', '--format', 'json'],
|
|
116
|
+
pattern: /Valid values for --staleness: o\[ff\]|e\[rror\]/
|
|
120
117
|
)
|
|
121
118
|
end
|
|
122
119
|
|
|
@@ -124,7 +121,7 @@ RSpec.describe SimpleCovMcp::OptionParsers::ErrorHelper do
|
|
|
124
121
|
error = OptionParser::InvalidArgument.new('invalid argument: invalid')
|
|
125
122
|
expect_error_output(
|
|
126
123
|
error: error,
|
|
127
|
-
argv: ['--json', '--sort-order=invalid', '--resultset', 'coverage'],
|
|
124
|
+
argv: ['--format', 'json', '--sort-order=invalid', '--resultset', 'coverage'],
|
|
128
125
|
pattern: /Valid values for --sort-order: a\[scending\]|d\[escending\]/
|
|
129
126
|
)
|
|
130
127
|
end
|
|
@@ -152,42 +149,44 @@ RSpec.describe SimpleCovMcp::OptionParsers::ErrorHelper do
|
|
|
152
149
|
helper.handle_option_parser_error(error, argv: ['--summary'])
|
|
153
150
|
end
|
|
154
151
|
|
|
155
|
-
#
|
|
152
|
+
# NOTE: The subcommand detection logic isn't fully working as expected
|
|
156
153
|
# because extract_invalid_option doesn't properly parse the error message
|
|
157
154
|
expect(stderr_output).to match(/Error:.*--summary/)
|
|
158
155
|
expect(stderr_output).to match(/Run 'simplecov-mcp --help'/)
|
|
159
156
|
end
|
|
160
157
|
end
|
|
161
158
|
|
|
162
|
-
context '
|
|
159
|
+
context 'when exiting after invalid option' do
|
|
163
160
|
it 'exits with status 1' do
|
|
164
161
|
error = OptionParser::InvalidArgument.new('invalid argument: xyz')
|
|
165
162
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
163
|
+
stderr_output = capture_stderr do
|
|
164
|
+
expect do
|
|
165
|
+
helper.handle_option_parser_error(error, argv: ['--staleness', 'xyz'])
|
|
166
|
+
end.to raise_error(SystemExit) do |e|
|
|
167
|
+
expect(e.status).to eq(1)
|
|
168
|
+
end
|
|
170
169
|
end
|
|
170
|
+
|
|
171
|
+
expect(stderr_output).to include('invalid argument: xyz')
|
|
171
172
|
end
|
|
172
173
|
end
|
|
173
174
|
|
|
174
|
-
context 'usage hint
|
|
175
|
+
context 'when customizing usage hint' do
|
|
175
176
|
it 'uses custom usage hint when provided' do
|
|
176
177
|
error = OptionParser::InvalidArgument.new('invalid argument: xyz')
|
|
177
178
|
|
|
178
179
|
expect do
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
# Ignore exit call
|
|
184
|
-
end
|
|
180
|
+
helper.handle_option_parser_error(error, argv: ['--staleness', 'xyz'],
|
|
181
|
+
usage_hint: 'Custom hint message')
|
|
182
|
+
rescue SystemExit
|
|
183
|
+
# Ignore exit call
|
|
185
184
|
end.to output(/Custom hint message/).to_stderr
|
|
186
185
|
end
|
|
187
186
|
end
|
|
188
187
|
end
|
|
189
188
|
|
|
190
|
-
describe 'edge cases' do
|
|
189
|
+
describe 'when handling edge cases' do
|
|
191
190
|
it 'handles empty argv gracefully' do
|
|
192
191
|
error = OptionParser::InvalidArgument.new('some error')
|
|
193
192
|
expect_error_output(
|
|
@@ -201,7 +200,8 @@ RSpec.describe SimpleCovMcp::OptionParsers::ErrorHelper do
|
|
|
201
200
|
error = OptionParser::InvalidArgument.new('some error')
|
|
202
201
|
|
|
203
202
|
stderr_output = capture_stderr do
|
|
204
|
-
helper.handle_option_parser_error(error,
|
|
203
|
+
helper.handle_option_parser_error(error,
|
|
204
|
+
argv: ['--format', 'json', '--resultset', 'coverage'])
|
|
205
205
|
end
|
|
206
206
|
|
|
207
207
|
expect(stderr_output).to match(/Error: invalid argument: some error/)
|
|
@@ -212,7 +212,7 @@ RSpec.describe SimpleCovMcp::OptionParsers::ErrorHelper do
|
|
|
212
212
|
error = OptionParser::MissingArgument.new('missing argument: --resultset')
|
|
213
213
|
|
|
214
214
|
stderr_output = capture_stderr do
|
|
215
|
-
helper.handle_option_parser_error(error, argv: ['--
|
|
215
|
+
helper.handle_option_parser_error(error, argv: ['--staleness', 'off', '--resultset'])
|
|
216
216
|
end
|
|
217
217
|
|
|
218
218
|
expect(stderr_output).to match(/Error:.*missing argument.*--resultset/)
|
|
@@ -79,5 +79,20 @@ RSpec.describe SimpleCovMcp::PathRelativizer do
|
|
|
79
79
|
ensure
|
|
80
80
|
FileUtils.rm_f(file_with_space)
|
|
81
81
|
end
|
|
82
|
+
|
|
83
|
+
# On Windows, relative_path_from raises ArgumentError for paths on different
|
|
84
|
+
# drives (e.g., C: vs D:). The rescue block returns the original path.
|
|
85
|
+
it 'returns original path when relative_path_from raises ArgumentError' do
|
|
86
|
+
fake_pathname = instance_double(Pathname)
|
|
87
|
+
allow(fake_pathname).to receive(:relative_path_from)
|
|
88
|
+
.and_raise(ArgumentError, 'different prefix')
|
|
89
|
+
allow(Pathname).to receive(:new).and_call_original
|
|
90
|
+
allow(Pathname).to receive(:new).with(File.absolute_path('lib/foo.rb', root))
|
|
91
|
+
.and_return(fake_pathname)
|
|
92
|
+
|
|
93
|
+
result = relativizer.relativize_path(File.join(root, 'lib/foo.rb'))
|
|
94
|
+
|
|
95
|
+
expect(result).to eq(File.join(root, 'lib/foo.rb'))
|
|
96
|
+
end
|
|
82
97
|
end
|
|
83
98
|
end
|
|
@@ -12,7 +12,7 @@ RSpec.describe SimpleCovMcp::Presenters::CoverageDetailedPresenter do
|
|
|
12
12
|
{ 'line' => 1, 'hits' => 1, 'covered' => true },
|
|
13
13
|
{ 'line' => 2, 'hits' => 0, 'covered' => false }
|
|
14
14
|
],
|
|
15
|
-
'summary' => { 'covered' => 1, 'total' => 2, '
|
|
15
|
+
'summary' => { 'covered' => 1, 'total' => 2, 'percentage' => 50.0 }
|
|
16
16
|
},
|
|
17
17
|
stale: 'L',
|
|
18
18
|
expected_keys: ['lines', 'summary']
|
|
@@ -8,7 +8,7 @@ RSpec.describe SimpleCovMcp::Presenters::CoverageSummaryPresenter do
|
|
|
8
8
|
model_method: :summary_for,
|
|
9
9
|
payload: {
|
|
10
10
|
'file' => '/abs/path/lib/foo.rb',
|
|
11
|
-
'summary' => { 'covered' => 8, 'total' => 10, '
|
|
11
|
+
'summary' => { 'covered' => 8, 'total' => 10, 'percentage' => 80.0 }
|
|
12
12
|
},
|
|
13
13
|
stale: false,
|
|
14
14
|
expected_keys: ['summary']
|
|
@@ -9,7 +9,7 @@ RSpec.describe SimpleCovMcp::Presenters::CoverageUncoveredPresenter do
|
|
|
9
9
|
payload: {
|
|
10
10
|
'file' => '/abs/path/lib/foo.rb',
|
|
11
11
|
'uncovered' => [2, 4],
|
|
12
|
-
'summary' => { 'covered' => 2, 'total' => 4, '
|
|
12
|
+
'summary' => { 'covered' => 2, 'total' => 4, 'percentage' => 50.0 }
|
|
13
13
|
},
|
|
14
14
|
stale: 'M',
|
|
15
15
|
expected_keys: ['uncovered', 'summary']
|