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.
Files changed (164) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +98 -50
  3. data/docs/{ARCHITECTURE.md → dev/ARCHITECTURE.md} +11 -10
  4. data/docs/dev/BRANCH_ONLY_COVERAGE.md +158 -0
  5. data/docs/{DEVELOPMENT.md → dev/DEVELOPMENT.md} +2 -1
  6. data/docs/dev/README.md +10 -0
  7. data/docs/dev/RELEASING.md +146 -0
  8. data/docs/{arch-decisions → dev/arch-decisions}/001-x-arch-decision.md +3 -1
  9. data/docs/{arch-decisions → dev/arch-decisions}/002-x-arch-decision.md +7 -5
  10. data/docs/{arch-decisions → dev/arch-decisions}/003-x-arch-decision.md +2 -0
  11. data/docs/{arch-decisions → dev/arch-decisions}/004-x-arch-decision.md +6 -2
  12. data/docs/{arch-decisions → dev/arch-decisions}/005-x-arch-decision.md +4 -2
  13. data/docs/{arch-decisions → dev/arch-decisions}/README.md +3 -3
  14. data/docs/{presentations → dev/presentations}/simplecov-mcp-presentation.md +28 -22
  15. data/docs/fixtures/demo_project/README.md +9 -0
  16. data/docs/{ADVANCED_USAGE.md → user/ADVANCED_USAGE.md} +129 -319
  17. data/docs/user/CLI_FALLBACK_FOR_LLMS.md +34 -0
  18. data/docs/user/CLI_USAGE.md +750 -0
  19. data/docs/{ERROR_HANDLING.md → user/ERROR_HANDLING.md} +12 -12
  20. data/docs/user/EXAMPLES.md +588 -0
  21. data/docs/user/INSTALLATION.md +130 -0
  22. data/docs/{LIBRARY_API.md → user/LIBRARY_API.md} +90 -32
  23. data/docs/{MCP_INTEGRATION.md → user/MCP_INTEGRATION.md} +36 -34
  24. data/docs/user/README.md +14 -0
  25. data/docs/{TROUBLESHOOTING.md → user/TROUBLESHOOTING.md} +21 -100
  26. data/docs/user/V2-BREAKING-CHANGES.md +472 -0
  27. data/exe/simplecov-mcp +1 -1
  28. data/lib/simplecov_mcp/{cli_config.rb → app_config.rb} +12 -12
  29. data/lib/simplecov_mcp/app_context.rb +1 -1
  30. data/lib/simplecov_mcp/base_tool.rb +66 -38
  31. data/lib/simplecov_mcp/cli.rb +67 -123
  32. data/lib/simplecov_mcp/commands/base_command.rb +16 -27
  33. data/lib/simplecov_mcp/commands/command_factory.rb +8 -2
  34. data/lib/simplecov_mcp/commands/detailed_command.rb +16 -2
  35. data/lib/simplecov_mcp/commands/list_command.rb +1 -1
  36. data/lib/simplecov_mcp/commands/raw_command.rb +18 -2
  37. data/lib/simplecov_mcp/commands/summary_command.rb +20 -3
  38. data/lib/simplecov_mcp/commands/totals_command.rb +53 -0
  39. data/lib/simplecov_mcp/commands/uncovered_command.rb +24 -5
  40. data/lib/simplecov_mcp/commands/validate_command.rb +60 -0
  41. data/lib/simplecov_mcp/commands/version_command.rb +19 -4
  42. data/lib/simplecov_mcp/config_parser.rb +32 -0
  43. data/lib/simplecov_mcp/constants.rb +3 -3
  44. data/lib/simplecov_mcp/coverage_reporter.rb +31 -0
  45. data/lib/simplecov_mcp/error_handler.rb +81 -40
  46. data/lib/simplecov_mcp/error_handler_factory.rb +2 -2
  47. data/lib/simplecov_mcp/errors.rb +12 -19
  48. data/lib/simplecov_mcp/formatters/source_formatter.rb +23 -19
  49. data/lib/simplecov_mcp/formatters.rb +51 -0
  50. data/lib/simplecov_mcp/mcp_server.rb +9 -7
  51. data/lib/simplecov_mcp/mode_detector.rb +6 -5
  52. data/lib/simplecov_mcp/model.rb +122 -88
  53. data/lib/simplecov_mcp/option_normalizers.rb +39 -18
  54. data/lib/simplecov_mcp/option_parser_builder.rb +85 -72
  55. data/lib/simplecov_mcp/option_parsers/env_options_parser.rb +3 -5
  56. data/lib/simplecov_mcp/option_parsers/error_helper.rb +18 -17
  57. data/lib/simplecov_mcp/path_relativizer.rb +17 -14
  58. data/lib/simplecov_mcp/predicate_evaluator.rb +72 -0
  59. data/lib/simplecov_mcp/presenters/base_coverage_presenter.rb +1 -3
  60. data/lib/simplecov_mcp/presenters/coverage_detailed_presenter.rb +1 -3
  61. data/lib/simplecov_mcp/presenters/coverage_raw_presenter.rb +1 -3
  62. data/lib/simplecov_mcp/presenters/coverage_summary_presenter.rb +1 -3
  63. data/lib/simplecov_mcp/presenters/coverage_uncovered_presenter.rb +1 -3
  64. data/lib/simplecov_mcp/presenters/project_coverage_presenter.rb +1 -3
  65. data/lib/simplecov_mcp/presenters/project_totals_presenter.rb +27 -0
  66. data/lib/simplecov_mcp/resolvers/coverage_line_resolver.rb +14 -18
  67. data/lib/simplecov_mcp/resolvers/resultset_path_resolver.rb +7 -9
  68. data/lib/simplecov_mcp/resultset_loader.rb +20 -25
  69. data/lib/simplecov_mcp/staleness_checker.rb +50 -46
  70. data/lib/simplecov_mcp/table_formatter.rb +64 -0
  71. data/lib/simplecov_mcp/tools/all_files_coverage_tool.rb +20 -50
  72. data/lib/simplecov_mcp/tools/coverage_detailed_tool.rb +13 -7
  73. data/lib/simplecov_mcp/tools/coverage_raw_tool.rb +12 -7
  74. data/lib/simplecov_mcp/tools/coverage_summary_tool.rb +13 -8
  75. data/lib/simplecov_mcp/tools/coverage_table_tool.rb +20 -60
  76. data/lib/simplecov_mcp/tools/coverage_totals_tool.rb +44 -0
  77. data/lib/simplecov_mcp/tools/help_tool.rb +38 -66
  78. data/lib/simplecov_mcp/tools/uncovered_lines_tool.rb +13 -8
  79. data/lib/simplecov_mcp/tools/validate_tool.rb +72 -0
  80. data/lib/simplecov_mcp/tools/version_tool.rb +7 -14
  81. data/lib/simplecov_mcp/util.rb +18 -12
  82. data/lib/simplecov_mcp/version.rb +1 -1
  83. data/lib/simplecov_mcp.rb +23 -29
  84. data/spec/all_files_coverage_tool_spec.rb +4 -3
  85. data/spec/{cli_config_spec.rb → app_config_spec.rb} +31 -26
  86. data/spec/base_tool_spec.rb +17 -14
  87. data/spec/cli/show_default_report_spec.rb +2 -2
  88. data/spec/cli_enumerated_options_spec.rb +31 -9
  89. data/spec/cli_error_spec.rb +46 -23
  90. data/spec/cli_format_spec.rb +123 -0
  91. data/spec/cli_json_options_spec.rb +50 -0
  92. data/spec/cli_source_spec.rb +11 -63
  93. data/spec/cli_spec.rb +82 -97
  94. data/spec/cli_usage_spec.rb +15 -15
  95. data/spec/commands/base_command_spec.rb +12 -92
  96. data/spec/commands/command_factory_spec.rb +7 -3
  97. data/spec/commands/detailed_command_spec.rb +10 -24
  98. data/spec/commands/list_command_spec.rb +28 -0
  99. data/spec/commands/raw_command_spec.rb +43 -20
  100. data/spec/commands/summary_command_spec.rb +10 -23
  101. data/spec/commands/totals_command_spec.rb +34 -0
  102. data/spec/commands/uncovered_command_spec.rb +29 -23
  103. data/spec/commands/validate_command_spec.rb +213 -0
  104. data/spec/commands/version_command_spec.rb +38 -0
  105. data/spec/constants_spec.rb +3 -3
  106. data/spec/coverage_reporter_spec.rb +102 -0
  107. data/spec/coverage_table_tool_spec.rb +21 -10
  108. data/spec/coverage_totals_tool_spec.rb +37 -0
  109. data/spec/error_handler_spec.rb +120 -4
  110. data/spec/error_mode_spec.rb +18 -22
  111. data/spec/errors_edge_cases_spec.rb +101 -28
  112. data/spec/errors_stale_spec.rb +34 -0
  113. data/spec/file_based_mcp_tools_spec.rb +6 -6
  114. data/spec/fixtures/project1/lib/bar.rb +2 -0
  115. data/spec/fixtures/project1/lib/foo.rb +2 -0
  116. data/spec/help_tool_spec.rb +2 -18
  117. data/spec/integration_spec.rb +103 -161
  118. data/spec/logging_fallback_spec.rb +3 -3
  119. data/spec/mcp_server_integration_spec.rb +1 -1
  120. data/spec/mcp_server_spec.rb +70 -53
  121. data/spec/mode_detector_spec.rb +46 -41
  122. data/spec/model_error_handling_spec.rb +139 -78
  123. data/spec/model_staleness_spec.rb +13 -13
  124. data/spec/option_normalizers_spec.rb +111 -112
  125. data/spec/option_parsers/env_options_parser_spec.rb +25 -37
  126. data/spec/option_parsers/error_helper_spec.rb +56 -56
  127. data/spec/path_relativizer_spec.rb +15 -0
  128. data/spec/presenters/coverage_detailed_presenter_spec.rb +1 -1
  129. data/spec/presenters/coverage_summary_presenter_spec.rb +1 -1
  130. data/spec/presenters/coverage_uncovered_presenter_spec.rb +1 -1
  131. data/spec/presenters/project_coverage_presenter_spec.rb +9 -8
  132. data/spec/presenters/project_totals_presenter_spec.rb +144 -0
  133. data/spec/resolvers/coverage_line_resolver_spec.rb +261 -36
  134. data/spec/resolvers/resultset_path_resolver_spec.rb +13 -8
  135. data/spec/shared_examples/file_based_mcp_tools.rb +23 -18
  136. data/spec/shared_examples/formatted_command_examples.rb +64 -0
  137. data/spec/simple_cov_mcp_module_spec.rb +24 -3
  138. data/spec/simplecov_mcp/formatters/source_formatter_spec.rb +267 -0
  139. data/spec/simplecov_mcp/formatters_spec.rb +76 -0
  140. data/spec/simplecov_mcp/presenters/base_coverage_presenter_spec.rb +79 -0
  141. data/spec/simplecov_mcp_model_spec.rb +97 -47
  142. data/spec/simplecov_mcp_opts_spec.rb +42 -39
  143. data/spec/spec_helper.rb +27 -92
  144. data/spec/staleness_checker_spec.rb +10 -9
  145. data/spec/staleness_more_spec.rb +4 -4
  146. data/spec/support/cli_helpers.rb +22 -0
  147. data/spec/support/control_flow_helpers.rb +20 -0
  148. data/spec/support/fake_mcp.rb +40 -0
  149. data/spec/support/io_helpers.rb +29 -0
  150. data/spec/support/mcp_helpers.rb +35 -0
  151. data/spec/support/mcp_runner.rb +10 -8
  152. data/spec/support/mocking_helpers.rb +30 -0
  153. data/spec/table_format_spec.rb +70 -0
  154. data/spec/tools/validate_tool_spec.rb +132 -0
  155. data/spec/tools_error_handling_spec.rb +34 -48
  156. data/spec/util_spec.rb +5 -4
  157. data/spec/version_spec.rb +7 -7
  158. data/spec/version_tool_spec.rb +20 -22
  159. metadata +90 -23
  160. data/docs/BRANCH_ONLY_COVERAGE.md +0 -81
  161. data/docs/CLI_USAGE.md +0 -637
  162. data/docs/EXAMPLES.md +0 -430
  163. data/docs/INSTALLATION.md +0 -352
  164. data/spec/cli_success_predicate_spec.rb +0 -141
@@ -7,8 +7,8 @@ RSpec.describe 'Error Mode System' do
7
7
  Class.new do
8
8
  attr_reader :messages
9
9
 
10
- def initialize; @messages = []; end
11
- def error(msg); @messages << msg; end
10
+ def initialize = @messages = []
11
+ def error(msg) = @messages << msg
12
12
  end.new
13
13
  end
14
14
 
@@ -27,8 +27,8 @@ RSpec.describe 'Error Mode System' do
27
27
  end
28
28
  end
29
29
 
30
- context 'with error_mode: :on' do
31
- subject(:handler) { SimpleCovMcp::ErrorHandler.new(error_mode: :on, logger: test_logger) }
30
+ context 'with error_mode: :log' do
31
+ subject(:handler) { SimpleCovMcp::ErrorHandler.new(error_mode: :log, logger: test_logger) }
32
32
 
33
33
  it 'logs errors but not stack traces' do
34
34
  expect(handler.log_errors?).to be true
@@ -41,8 +41,8 @@ RSpec.describe 'Error Mode System' do
41
41
  end
42
42
  end
43
43
 
44
- context 'with error_mode: :trace' do
45
- subject(:handler) { SimpleCovMcp::ErrorHandler.new(error_mode: :trace, logger: test_logger) }
44
+ context 'with error_mode: :debug' do
45
+ subject(:handler) { SimpleCovMcp::ErrorHandler.new(error_mode: :debug, logger: test_logger) }
46
46
 
47
47
  it 'logs errors with stack traces' do
48
48
  expect(handler.log_errors?).to be true
@@ -51,7 +51,7 @@ RSpec.describe 'Error Mode System' do
51
51
  # Create an error with a proper backtrace
52
52
  begin
53
53
  raise StandardError, 'Test error message'
54
- rescue StandardError => e
54
+ rescue => e
55
55
  handler.handle_error(e, context: 'test', reraise: false)
56
56
  end
57
57
 
@@ -64,14 +64,14 @@ RSpec.describe 'Error Mode System' do
64
64
 
65
65
  describe 'ErrorHandlerFactory' do
66
66
  it 'creates handlers with correct modes' do
67
- cli_handler = SimpleCovMcp::ErrorHandlerFactory.for_cli(error_mode: :trace)
68
- expect(cli_handler.error_mode).to eq(:trace)
67
+ cli_handler = SimpleCovMcp::ErrorHandlerFactory.for_cli(error_mode: :debug)
68
+ expect(cli_handler.error_mode).to eq(:debug)
69
69
 
70
70
  lib_handler = SimpleCovMcp::ErrorHandlerFactory.for_library(error_mode: :off)
71
71
  expect(lib_handler.error_mode).to eq(:off)
72
72
 
73
- mcp_handler = SimpleCovMcp::ErrorHandlerFactory.for_mcp_server(error_mode: :on)
74
- expect(mcp_handler.error_mode).to eq(:on)
73
+ mcp_handler = SimpleCovMcp::ErrorHandlerFactory.for_mcp_server(error_mode: :log)
74
+ expect(mcp_handler.error_mode).to eq(:log)
75
75
  end
76
76
  end
77
77
 
@@ -82,13 +82,13 @@ RSpec.describe 'Error Mode System' do
82
82
  test_error = StandardError.new('Test MCP error')
83
83
 
84
84
  # Test different error modes
85
- [:off, :on, :trace].each do |mode|
85
+ [:off, :log, :debug].each do |mode|
86
86
  expect(SimpleCovMcp::ErrorHandlerFactory)
87
87
  .to receive(:for_mcp_server).with(error_mode: mode).and_call_original
88
88
 
89
89
  response = SimpleCovMcp::BaseTool.handle_mcp_error(test_error, 'TestTool', error_mode: mode)
90
90
  expect(response).to be_a(MCP::Tool::Response)
91
- expect(response.payload.first[:text]).to include('Error:')
91
+ expect(response.payload.first['text']).to include('Error:')
92
92
  end
93
93
  end
94
94
  end
@@ -101,21 +101,17 @@ RSpec.describe 'Error Mode System' do
101
101
 
102
102
  # Test that the option parser accepts the flag
103
103
  expect do
104
- cli.send(:parse_options!, ['--error-mode', 'trace', 'summary', 'lib/foo.rb'])
104
+ cli.send(:parse_options!, ['--error-mode', 'debug', 'summary', 'lib/foo.rb'])
105
105
  end.not_to raise_error
106
106
 
107
- expect(cli.config.error_mode).to eq(:trace)
107
+ expect(cli.config.error_mode).to eq(:debug)
108
108
  end
109
109
 
110
110
  it 'creates error handler with specified mode' do
111
111
  cli = SimpleCovMcp::CoverageCLI.new
112
112
  cli.send(:parse_options!, ['--error-mode', 'off', 'summary', 'lib/foo.rb'])
113
113
 
114
- # Trigger error handler creation
115
- cli.send(:ensure_error_handler)
116
-
117
- error_handler = cli.instance_variable_get(:@error_handler)
118
- expect(error_handler.error_mode).to eq(:off)
114
+ expect(cli.send(:error_handler).error_mode).to eq(:off)
119
115
  end
120
116
 
121
117
  it 'validates error mode values' do
@@ -136,8 +132,8 @@ RSpec.describe 'Error Mode System' do
136
132
 
137
133
  it 'accepts all valid error modes' do
138
134
  expect { SimpleCovMcp::ErrorHandler.new(error_mode: :off) }.not_to raise_error
139
- expect { SimpleCovMcp::ErrorHandler.new(error_mode: :on) }.not_to raise_error
140
- expect { SimpleCovMcp::ErrorHandler.new(error_mode: :trace) }.not_to raise_error
135
+ expect { SimpleCovMcp::ErrorHandler.new(error_mode: :log) }.not_to raise_error
136
+ expect { SimpleCovMcp::ErrorHandler.new(error_mode: :debug) }.not_to raise_error
141
137
  end
142
138
  end
143
139
  end
@@ -2,7 +2,66 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- RSpec.describe 'SimpleCovMcp error edge cases' do
5
+ RSpec.describe SimpleCovMcp do
6
+ describe SimpleCovMcp::ConfigurationError do
7
+ describe '#user_friendly_message' do
8
+ it 'prefixes message with "Configuration error:"' do
9
+ error = described_class.new('Invalid option value')
10
+
11
+ expect(error.user_friendly_message).to eq('Configuration error: Invalid option value')
12
+ end
13
+
14
+ it 'handles empty message' do
15
+ error = described_class.new('')
16
+
17
+ expect(error.user_friendly_message).to eq('Configuration error: ')
18
+ end
19
+
20
+ it 'handles nil message' do
21
+ # When nil is passed to StandardError, it uses the class name as the message
22
+ error = described_class.new(nil)
23
+
24
+ expect(error.user_friendly_message).to eq('Configuration error: SimpleCovMcp::ConfigurationError')
25
+ end
26
+ end
27
+ end
28
+
29
+
30
+ describe SimpleCovMcp::ResultsetNotFoundError do
31
+ describe '#user_friendly_message' do
32
+ it 'includes helpful tips in CLI mode' do
33
+ # Create a CLI context (not MCP mode)
34
+ error_handler = SimpleCovMcp::ErrorHandlerFactory.for_cli
35
+ context = SimpleCovMcp.create_context(error_handler: error_handler, mode: :cli)
36
+ SimpleCovMcp.with_context(context) do
37
+ error = described_class.new('Coverage data not found')
38
+ message = error.user_friendly_message
39
+
40
+ expect(message).to include(
41
+ 'File error: Coverage data not found',
42
+ 'Try one of the following:',
43
+ 'cd to a directory containing coverage/.resultset.json',
44
+ 'Specify a resultset: simplecov-mcp -r PATH',
45
+ 'Use -h for help: simplecov-mcp -h'
46
+ )
47
+ end
48
+ end
49
+
50
+ it 'does not include helpful tips in MCP mode' do
51
+ # Create an MCP context
52
+ error_handler = SimpleCovMcp::ErrorHandlerFactory.for_mcp_server
53
+ context = SimpleCovMcp.create_context(error_handler: error_handler, mode: :mcp)
54
+ SimpleCovMcp.with_context(context) do
55
+ error = described_class.new('Coverage data not found')
56
+ message = error.user_friendly_message
57
+
58
+ expect(message).to eq('File error: Coverage data not found')
59
+ expect(message).not_to include('Try one of the following:')
60
+ end
61
+ end
62
+ end
63
+ end
64
+
6
65
  describe SimpleCovMcp::CoverageDataStaleError do
7
66
  describe 'time formatting edge cases' do
8
67
  it 'handles invalid epoch seconds gracefully in rescue path' do
@@ -12,7 +71,7 @@ RSpec.describe 'SimpleCovMcp error edge cases' do
12
71
  raise ArgumentError, "Can't convert"
13
72
  end
14
73
 
15
- error = SimpleCovMcp::CoverageDataStaleError.new(
74
+ error = described_class.new(
16
75
  'Test error',
17
76
  nil,
18
77
  file_path: 'test.rb',
@@ -34,7 +93,7 @@ RSpec.describe 'SimpleCovMcp error edge cases' do
34
93
  'unparseable_time_string'
35
94
  end
36
95
 
37
- error = SimpleCovMcp::CoverageDataStaleError.new(
96
+ error = described_class.new(
38
97
  'Test error',
39
98
  nil,
40
99
  file_path: 'test.rb',
@@ -63,7 +122,7 @@ RSpec.describe 'SimpleCovMcp error edge cases' do
63
122
  raise ArgumentError, "Can't convert"
64
123
  end
65
124
 
66
- error = SimpleCovMcp::CoverageDataStaleError.new(
125
+ error = described_class.new(
67
126
  'Test error',
68
127
  nil,
69
128
  file_path: 'test.rb',
@@ -81,9 +140,9 @@ RSpec.describe 'SimpleCovMcp error edge cases' do
81
140
  end
82
141
 
83
142
  describe 'default message generation' do
84
- it 'uses default message when message is nil' do
85
- error = SimpleCovMcp::CoverageDataStaleError.new(
86
- nil, # No message provided
143
+ it 'uses default message when message is nil with file_path' do
144
+ error = described_class.new(
145
+ nil, # No message provided - triggers default_message
87
146
  nil,
88
147
  file_path: 'test.rb',
89
148
  file_mtime: Time.at(2000),
@@ -91,52 +150,66 @@ RSpec.describe 'SimpleCovMcp error edge cases' do
91
150
  )
92
151
 
93
152
  message = error.user_friendly_message
94
- # When message is nil, the error class name is used by StandardError
95
- # which then triggers default_message to be called
96
- expect(message).to include('Coverage data')
97
- expect(message).to include('stale')
153
+ # default_message returns "Coverage data appears stale for test.rb"
154
+ expect(message).to include('Coverage data appears stale for test.rb')
98
155
  # File path should appear in the details section
99
156
  expect(message).to match(/File\s+-/)
100
157
  end
101
158
 
102
159
  it 'uses generic default message when file_path is nil' do
103
- error = SimpleCovMcp::CoverageDataStaleError.new(
104
- nil, # No message
160
+ # This tests the fallback path when file_path is nil: fp = file_path || 'file'
161
+ error = described_class.new(
162
+ nil, # No message - triggers default_message
105
163
  nil,
106
- file_path: nil, # No file path
164
+ file_path: nil, # No file path - triggers 'file' fallback
107
165
  file_mtime: Time.at(2000),
108
166
  cov_timestamp: 1000
109
167
  )
110
168
 
111
169
  message = error.user_friendly_message
112
- # When file_path is nil, should use 'file' as fallback
113
- expect(message).to include('Coverage data')
114
- expect(message).to include('file')
170
+ # When file_path is nil, default_message returns "Coverage data appears stale for file"
171
+ expect(message).to include('Coverage data appears stale for file')
115
172
  end
116
173
  end
117
174
  end
118
175
 
119
176
  describe SimpleCovMcp::CoverageDataProjectStaleError do
120
177
  describe 'default message generation' do
121
- it 'uses default message when message is nil' do
122
- error = SimpleCovMcp::CoverageDataProjectStaleError.new(
123
- nil, # No message provided
178
+ # These tests exercise the private default_message method
179
+ it 'includes project stale info when message is nil' do
180
+ error = described_class.new(
181
+ nil, # StandardError sets message to class name when nil
124
182
  nil,
125
183
  cov_timestamp: 1000,
126
184
  newer_files: ['file1.rb', 'file2.rb']
127
185
  )
128
186
 
129
187
  message = error.user_friendly_message
130
- # When message is nil, StandardError uses class name, then default_message is called
131
- expect(message).to include('Coverage data')
132
- expect(message).to include('project')
188
+ # user_friendly_message prefixes with "Coverage data stale (project):"
189
+ expect(message).to include('Coverage data stale (project)')
190
+ expect(message).to include('Newer files')
191
+ end
192
+
193
+ it 'exercises default_message directly via send' do
194
+ # Directly test the private default_message method for coverage
195
+ # This is necessary because user_friendly_message uses `message || default_message`
196
+ # and StandardError sets message to class name when initialized with nil
197
+ error = described_class.new(
198
+ 'explicit message',
199
+ nil,
200
+ cov_timestamp: 1000
201
+ )
202
+
203
+ # Call the private default_message method directly
204
+ result = error.send(:default_message)
205
+ expect(result).to eq('Coverage data appears stale for project')
133
206
  end
134
207
  end
135
208
 
136
209
  describe 'large file list truncation' do
137
210
  it 'shows all files when there are 10 or fewer deleted files' do
138
211
  deleted_files = (1..10).map { |i| "deleted_file_#{i}.rb" }
139
- error = SimpleCovMcp::CoverageDataProjectStaleError.new(
212
+ error = described_class.new(
140
213
  'Test error',
141
214
  nil,
142
215
  cov_timestamp: 1000,
@@ -153,7 +226,7 @@ RSpec.describe 'SimpleCovMcp error edge cases' do
153
226
 
154
227
  it 'truncates and shows ellipsis when there are more than 10 deleted files' do
155
228
  deleted_files = (1..15).map { |i| "deleted_file_#{i}.rb" }
156
- error = SimpleCovMcp::CoverageDataProjectStaleError.new(
229
+ error = described_class.new(
157
230
  'Test error',
158
231
  nil,
159
232
  cov_timestamp: 1000,
@@ -176,7 +249,7 @@ RSpec.describe 'SimpleCovMcp error edge cases' do
176
249
 
177
250
  it 'shows all files when there are 10 or fewer missing files' do
178
251
  missing_files = (1..10).map { |i| "missing_file_#{i}.rb" }
179
- error = SimpleCovMcp::CoverageDataProjectStaleError.new(
252
+ error = described_class.new(
180
253
  'Test error',
181
254
  nil,
182
255
  cov_timestamp: 1000,
@@ -193,7 +266,7 @@ RSpec.describe 'SimpleCovMcp error edge cases' do
193
266
 
194
267
  it 'truncates and shows ellipsis when there are more than 10 missing files' do
195
268
  missing_files = (1..12).map { |i| "missing_file_#{i}.rb" }
196
- error = SimpleCovMcp::CoverageDataProjectStaleError.new(
269
+ error = described_class.new(
197
270
  'Test error',
198
271
  nil,
199
272
  cov_timestamp: 1000,
@@ -214,7 +287,7 @@ RSpec.describe 'SimpleCovMcp error edge cases' do
214
287
 
215
288
  it 'truncates and shows ellipsis when there are more than 10 newer files' do
216
289
  newer_files = (1..20).map { |i| "newer_file_#{i}.rb" }
217
- error = SimpleCovMcp::CoverageDataProjectStaleError.new(
290
+ error = described_class.new(
218
291
  'Test error',
219
292
  nil,
220
293
  cov_timestamp: 1000,
@@ -46,4 +46,38 @@ RSpec.describe SimpleCovMcp::CoverageDataStaleError do
46
46
  expect(msg).to match(/Coverage\s*-\s*time:\s*not found.*lines: 0/m)
47
47
  expect(msg).not_to include('Delta')
48
48
  end
49
+
50
+ it 'uses default message when message is nil' do
51
+ err = described_class.new(
52
+ nil,
53
+ nil,
54
+ file_path: 'lib/example.rb',
55
+ file_mtime: Time.now,
56
+ cov_timestamp: Time.now.to_i - 1000,
57
+ src_len: 10,
58
+ cov_len: 8,
59
+ resultset_path: '/coverage/.resultset.json'
60
+ )
61
+
62
+ msg = err.user_friendly_message
63
+ expect(msg).to include('Coverage data stale:')
64
+ expect(msg).to include('Coverage data appears stale for lib/example.rb')
65
+ end
66
+
67
+ it 'uses "file" in default message when file_path is also nil' do
68
+ err = described_class.new(
69
+ nil,
70
+ nil,
71
+ file_path: nil,
72
+ file_mtime: nil,
73
+ cov_timestamp: nil,
74
+ src_len: 0,
75
+ cov_len: 0,
76
+ resultset_path: nil
77
+ )
78
+
79
+ msg = err.user_friendly_message
80
+ expect(msg).to include('Coverage data stale:')
81
+ expect(msg).to include('Coverage data appears stale for file')
82
+ end
49
83
  end
@@ -11,7 +11,7 @@ require 'simplecov_mcp/tools/coverage_detailed_tool'
11
11
 
12
12
  RSpec.describe 'File-based MCP Tools' do
13
13
  # Test each file-based tool using the shared example with its specific configuration
14
- FILE_BASED_TOOL_CONFIGS.each do |tool_name, config|
14
+ FILE_BASED_TOOL_CONFIGS.each_value do |config|
15
15
  describe config[:tool_class] do
16
16
  it_behaves_like 'a file-based MCP tool', config
17
17
  end
@@ -27,7 +27,7 @@ RSpec.describe 'File-based MCP Tools' do
27
27
 
28
28
  it 'all file-based tools accept the same basic parameters' do
29
29
  # Test that all tools can be called with the same parameter signature
30
- FILE_BASED_TOOL_CONFIGS.each do |tool_name, config|
30
+ FILE_BASED_TOOL_CONFIGS.each_value do |config|
31
31
  model = instance_double(SimpleCovMcp::CoverageModel)
32
32
  allow(SimpleCovMcp::CoverageModel).to receive(:new).and_return(model)
33
33
  allow(model).to receive(config[:model_method]).and_return(config[:mock_data])
@@ -46,7 +46,7 @@ RSpec.describe 'File-based MCP Tools' do
46
46
  end
47
47
 
48
48
  it 'all file-based tools return JSON resources with consistent structure' do
49
- FILE_BASED_TOOL_CONFIGS.each do |tool_name, config|
49
+ FILE_BASED_TOOL_CONFIGS.each_value do |config|
50
50
  model = instance_double(SimpleCovMcp::CoverageModel)
51
51
  allow(SimpleCovMcp::CoverageModel).to receive(:new).and_return(model)
52
52
  allow(model).to receive(config[:model_method]).and_return(config[:mock_data])
@@ -79,7 +79,7 @@ RSpec.describe 'File-based MCP Tools' do
79
79
  config[:expected_keys].include?('summary')
80
80
  end
81
81
 
82
- summary_tools.each do |tool_name, config|
82
+ summary_tools.each_value do |config|
83
83
  model = instance_double(SimpleCovMcp::CoverageModel)
84
84
  allow(SimpleCovMcp::CoverageModel).to receive(:new).and_return(model)
85
85
  allow(model).to receive(config[:model_method]).and_return(config[:mock_data])
@@ -90,8 +90,8 @@ RSpec.describe 'File-based MCP Tools' do
90
90
  data = JSON.parse(response.payload.first['text'])
91
91
 
92
92
  if data.key?('summary')
93
- expect(data['summary']).to include('covered', 'total', 'pct')
94
- expect(data['summary']['pct']).to be_a(Numeric)
93
+ expect(data['summary']).to include('covered', 'total', 'percentage')
94
+ expect(data['summary']['percentage']).to be_a(Numeric)
95
95
  end
96
96
  end
97
97
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  puts 'bar line 1 (uncovered)'
2
4
  puts 'bar line 2 (uncovered)'
3
5
  puts 'bar line 3'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  puts 'foo line 1'
2
4
  puts 'foo line 2 (uncovered)'
3
5
 
@@ -20,23 +20,7 @@ RSpec.describe SimpleCovMcp::Tools::HelpTool do
20
20
  tool_names = data['tools'].map { |entry| entry['tool'] }
21
21
 
22
22
  expect(tool_names).to include('coverage_summary_tool', 'uncovered_lines_tool',
23
- 'all_files_coverage_tool', 'coverage_table_tool', 'version_tool')
24
- expect(data['tools']).to all(include('use_when', 'avoid_when', 'inputs', 'example'))
25
- end
26
-
27
- it 'filters entries when a query is provided' do
28
- response = described_class.call(query: 'uncovered', server_context: server_context)
29
- payload = response.payload.first
30
- expect(payload['type']).to eq('text')
31
- data = JSON.parse(payload['text'])
32
-
33
- expect(data['tools']).not_to be_empty
34
- expect(data['tools']).to all(satisfy do |entry|
35
- combined =
36
- [entry['tool'], entry['label'], entry['use_when'], entry['avoid_when']]
37
- .compact.join(' ').downcase
38
- combined.include?('uncovered')
39
- end)
40
- expect(data['tools'].map { |entry| entry['tool'] }).to include('uncovered_lines_tool')
23
+ 'all_files_coverage_tool', 'coverage_totals_tool', 'coverage_table_tool', 'version_tool')
24
+ expect(data['tools']).to all(include('use_when', 'avoid_when', 'inputs'))
41
25
  end
42
26
  end