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
|
@@ -1,25 +1,27 @@
|
|
|
1
1
|
# Error Handling Guide
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[Back to main README](../README.md)
|
|
4
|
+
|
|
5
|
+
Error handling differs by usage mode:
|
|
4
6
|
|
|
5
7
|
## CLI Mode
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
Errors are displayed as user-friendly messages without stack traces:
|
|
8
10
|
|
|
9
11
|
```bash
|
|
10
12
|
$ simplecov-mcp summary nonexistent.rb
|
|
11
13
|
File error: No coverage data found for the specified file
|
|
12
14
|
```
|
|
13
15
|
|
|
14
|
-
For debugging, use the `--error-mode
|
|
16
|
+
For debugging, use the `--error-mode debug` flag to include stack traces in log output and display the first 5 lines of the backtrace in CLI output:
|
|
15
17
|
|
|
16
18
|
```bash
|
|
17
|
-
$ simplecov-mcp --error-mode
|
|
19
|
+
$ simplecov-mcp --error-mode debug summary nonexistent.rb
|
|
18
20
|
```
|
|
19
21
|
|
|
20
22
|
## Library Mode
|
|
21
23
|
|
|
22
|
-
|
|
24
|
+
Calls to `SimpleCovMcp::CoverageModel` raise custom exceptions you can handle programmatically:
|
|
23
25
|
|
|
24
26
|
```ruby
|
|
25
27
|
handler = SimpleCovMcp::ErrorHandlerFactory.for_library # disables CLI-style logging
|
|
@@ -44,13 +46,11 @@ Available exception classes:
|
|
|
44
46
|
|
|
45
47
|
## MCP Server Mode
|
|
46
48
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
- **Logging enabled** - Errors are logged to `simplecov_mcp.log` in the current directory for server debugging
|
|
50
|
-
- **Clean error messages** - User-friendly messages are returned to the client without stack traces by default
|
|
51
|
-
- **Structured responses** - Errors are returned as proper MCP tool responses, not exceptions
|
|
49
|
+
Errors are returned as structured responses to the MCP client:
|
|
52
50
|
|
|
53
|
-
|
|
51
|
+
- **Logging enabled** - Errors go to `simplecov_mcp.log` in the current directory by default
|
|
52
|
+
- **Clean error messages** - User-friendly messages, no stack traces by default
|
|
53
|
+
- **Structured responses** - Tool responses instead of exceptions
|
|
54
54
|
|
|
55
55
|
## Custom Error Handlers
|
|
56
56
|
|
|
@@ -72,7 +72,7 @@ end
|
|
|
72
72
|
|
|
73
73
|
## Stale Coverage Errors
|
|
74
74
|
|
|
75
|
-
When strict staleness checking is enabled (`--
|
|
75
|
+
When strict staleness checking is enabled (`--staleness error`), the model (and CLI) raise a `CoverageDataStaleError` if a source file appears newer than the coverage data or the line counts differ.
|
|
76
76
|
|
|
77
77
|
- Enable per instance: `SimpleCovMcp::CoverageModel.new(staleness: 'error')`
|
|
78
78
|
|
|
@@ -0,0 +1,588 @@
|
|
|
1
|
+
# Examples and Recipes
|
|
2
|
+
|
|
3
|
+
[Back to main README](../README.md)
|
|
4
|
+
|
|
5
|
+
Practical examples for common tasks with simplecov-mcp. Examples are organized by skill level and use case.
|
|
6
|
+
|
|
7
|
+
> For brevity, these examples use `smcp`, an alias to the demo fixture with partial coverage:
|
|
8
|
+
> `alias smcp='simplecov-mcp --root docs/fixtures/demo_project'`
|
|
9
|
+
> Swap `smcp` for `simplecov-mcp` to run against your own project and resultset.
|
|
10
|
+
|
|
11
|
+
## Table of Contents
|
|
12
|
+
|
|
13
|
+
- [Quick Start Examples](#quick-start-examples)
|
|
14
|
+
- [CLI Examples](#cli-examples)
|
|
15
|
+
- [Ruby Library Examples](#ruby-library-examples)
|
|
16
|
+
- [AI Assistant Prompts](#ai-assistant-prompts)
|
|
17
|
+
- [CI/CD Integration](#cicd-integration)
|
|
18
|
+
- [Advanced Usage](#advanced-usage)
|
|
19
|
+
|
|
20
|
+
## Quick Start Examples
|
|
21
|
+
|
|
22
|
+
### View All Coverage
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# Default: show all files, worst coverage first
|
|
26
|
+
smcp
|
|
27
|
+
|
|
28
|
+
# Show files with best coverage first
|
|
29
|
+
smcp -o d list # -o = --sort-order, d = descending
|
|
30
|
+
|
|
31
|
+
# Export to JSON for processing
|
|
32
|
+
smcp -fJ list > coverage-report.json
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Check Specific File
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# Quick summary
|
|
39
|
+
smcp summary app/models/order.rb
|
|
40
|
+
|
|
41
|
+
# See which lines aren't covered
|
|
42
|
+
smcp uncovered app/controllers/orders_controller.rb
|
|
43
|
+
|
|
44
|
+
# View uncovered code with context
|
|
45
|
+
smcp -s u -c 3 uncovered app/controllers/orders_controller.rb # -s = --source (u = uncovered), -c = --context-lines
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Find Coverage Gaps
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# Files with worst coverage
|
|
52
|
+
smcp list | head -10
|
|
53
|
+
|
|
54
|
+
# Only show files below 80%
|
|
55
|
+
smcp -fJ list | jq '.files[] | select(.percentage < 95)'
|
|
56
|
+
|
|
57
|
+
# Ruby alternative:
|
|
58
|
+
smcp -fJ list | ruby -r json -e '
|
|
59
|
+
JSON.parse($stdin.read)["files"].select { |f| f["percentage"] < 95 }.each do |f|
|
|
60
|
+
puts JSON.pretty_generate(f)
|
|
61
|
+
end
|
|
62
|
+
'
|
|
63
|
+
|
|
64
|
+
# Rexe alternative:
|
|
65
|
+
smcp -fJ list | rexe -ij -mb -oJ 'self["files"].select { |f| f["percentage"] < 95 }'
|
|
66
|
+
|
|
67
|
+
# Check specific directory
|
|
68
|
+
smcp -g "lib/payments/**/*.rb" list # -g = --tracked-globs
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## CLI Examples
|
|
72
|
+
|
|
73
|
+
### Coverage Analysis
|
|
74
|
+
|
|
75
|
+
**Detailed investigation:**
|
|
76
|
+
```bash
|
|
77
|
+
# See detailed hit counts
|
|
78
|
+
smcp detailed lib/api/client.rb
|
|
79
|
+
|
|
80
|
+
# Show full source with coverage markers
|
|
81
|
+
smcp -s f summary lib/api/client.rb # f = full
|
|
82
|
+
|
|
83
|
+
# Focus on uncovered areas only
|
|
84
|
+
smcp -s u -c 5 uncovered lib/payments/refund_service.rb # u = uncovered
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Working with JSON Output
|
|
88
|
+
|
|
89
|
+
In addition to the benefit of JSON encoding being human readable, it can be used in single line commands to fetch and compute values using `jq`, Ruby's JSON library, or `rexe`.
|
|
90
|
+
Here are some examples:
|
|
91
|
+
|
|
92
|
+
**Parse and filter:**
|
|
93
|
+
```bash
|
|
94
|
+
# Files below threshold
|
|
95
|
+
smcp -fJ list | jq '.files[] | select(.percentage < 95) | {file, coverage: .percentage}'
|
|
96
|
+
|
|
97
|
+
# Ruby alternative:
|
|
98
|
+
smcp -fJ list | ruby -r json -e '
|
|
99
|
+
JSON.parse($stdin.read)["files"].select { |f| f["percentage"] < 95 }.each do |f|
|
|
100
|
+
puts JSON.pretty_generate({file: f["file"], coverage: f["percentage"]})
|
|
101
|
+
end
|
|
102
|
+
'
|
|
103
|
+
|
|
104
|
+
# Rexe alternative:
|
|
105
|
+
smcp -fJ list | rexe -ij -mb -oJ '
|
|
106
|
+
self["files"].select { |f| f["percentage"] < 95 }.map do |f|
|
|
107
|
+
{file: f["file"], coverage: f["percentage"]}
|
|
108
|
+
end
|
|
109
|
+
'
|
|
110
|
+
|
|
111
|
+
# Count total uncovered lines
|
|
112
|
+
smcp -fJ totals | jq '.lines.uncovered'
|
|
113
|
+
|
|
114
|
+
# Ruby alternative:
|
|
115
|
+
smcp -fJ totals | ruby -r json -e '
|
|
116
|
+
puts JSON.parse($stdin.read)["lines"]["uncovered"]
|
|
117
|
+
'
|
|
118
|
+
|
|
119
|
+
# Rexe alternative:
|
|
120
|
+
smcp -fJ totals | rexe -ij -mb -op 'self["lines"]["uncovered"]'
|
|
121
|
+
|
|
122
|
+
# Group by directory (full path)
|
|
123
|
+
smcp -fJ list |
|
|
124
|
+
jq '.files
|
|
125
|
+
| map(. + {dir: (.file | split("/") | .[0:-1] | join("/"))})
|
|
126
|
+
| sort_by(.dir)
|
|
127
|
+
| group_by(.dir)
|
|
128
|
+
| map({dir: .[0].dir, avg: (map(.percentage) | add / length)})'
|
|
129
|
+
|
|
130
|
+
# Ruby alternative:
|
|
131
|
+
smcp -fJ list | ruby -r json -e '
|
|
132
|
+
grouped = JSON.parse($stdin.read)["files"]
|
|
133
|
+
.map { |f| f.merge("dir" => File.dirname(f["file"])) }
|
|
134
|
+
.group_by { |f| f["dir"] }
|
|
135
|
+
.map { |dir, files|
|
|
136
|
+
avg = files.sum { |f| f["percentage"] } / files.size
|
|
137
|
+
{dir: dir, avg: avg}
|
|
138
|
+
}
|
|
139
|
+
puts JSON.pretty_generate(grouped)
|
|
140
|
+
'
|
|
141
|
+
|
|
142
|
+
# Rexe alternative:
|
|
143
|
+
smcp -fJ list | rexe -ij -mb -oJ '
|
|
144
|
+
self["files"]
|
|
145
|
+
.map { |f| f.merge("dir" => File.dirname(f["file"])) }
|
|
146
|
+
.group_by { |f| f["dir"] }
|
|
147
|
+
.map { |dir, files|
|
|
148
|
+
avg = files.sum { |f| f["percentage"] } / files.size
|
|
149
|
+
{dir: dir, avg: avg}
|
|
150
|
+
}
|
|
151
|
+
'
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
**Generate reports:**
|
|
155
|
+
```bash
|
|
156
|
+
# Create markdown table
|
|
157
|
+
echo "| Coverage | File |" > report.md
|
|
158
|
+
echo "|----------|------|" >> report.md
|
|
159
|
+
smcp -fJ list | jq -r '.files[] | "| \(.percentage)% | \(.file) |"' >> report.md
|
|
160
|
+
|
|
161
|
+
# Ruby alternative:
|
|
162
|
+
smcp -fJ list | ruby -r json -e '
|
|
163
|
+
JSON.parse($stdin.read)["files"].each do |f|
|
|
164
|
+
puts "| #{f["percentage"]}% | #{f["file"]} |"
|
|
165
|
+
end
|
|
166
|
+
' >> report.md
|
|
167
|
+
|
|
168
|
+
# Rexe alternative:
|
|
169
|
+
smcp -fJ list | rexe -ij -mb '
|
|
170
|
+
self["files"].each { |f| puts "| #{f["percentage"]}% | #{f["file"]} |" }
|
|
171
|
+
' >> report.md
|
|
172
|
+
|
|
173
|
+
# Export for spreadsheet
|
|
174
|
+
smcp -fJ list | jq -r '.files[] | [.file, .percentage] | @csv' > coverage.csv
|
|
175
|
+
|
|
176
|
+
# Ruby alternative:
|
|
177
|
+
smcp -fJ list | ruby -r json -r csv -e '
|
|
178
|
+
JSON.parse($stdin.read)["files"].each do |f|
|
|
179
|
+
puts CSV.generate_line([f["file"], f["percentage"]]).chomp
|
|
180
|
+
end
|
|
181
|
+
' > coverage.csv
|
|
182
|
+
|
|
183
|
+
# Rexe alternative:
|
|
184
|
+
smcp -fJ list | rexe -r csv -ij -mb '
|
|
185
|
+
self["files"].each { |f| puts CSV.generate_line([f["file"], f["percentage"]]).chomp }
|
|
186
|
+
' > coverage.csv
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Ruby Library Examples
|
|
190
|
+
|
|
191
|
+
### Basic Usage
|
|
192
|
+
|
|
193
|
+
```ruby
|
|
194
|
+
require "simplecov_mcp"
|
|
195
|
+
|
|
196
|
+
root = "docs/fixtures/demo_project"
|
|
197
|
+
model = SimpleCovMcp::CoverageModel.new(root: root)
|
|
198
|
+
|
|
199
|
+
# Project totals
|
|
200
|
+
totals = model.project_totals
|
|
201
|
+
puts "Total files: #{totals['files']['total']}"
|
|
202
|
+
puts "Average coverage: #{totals['percentage']}%"
|
|
203
|
+
|
|
204
|
+
# Check specific file
|
|
205
|
+
summary = model.summary_for("app/models/order.rb")
|
|
206
|
+
puts "Coverage: #{summary['summary']['percentage']}%"
|
|
207
|
+
|
|
208
|
+
# Find uncovered lines
|
|
209
|
+
uncovered = model.uncovered_for("lib/payments/refund_service.rb")
|
|
210
|
+
puts "Uncovered lines: #{uncovered['uncovered'].join(', ')}"
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Filtering and Analysis
|
|
214
|
+
|
|
215
|
+
```ruby
|
|
216
|
+
require "simplecov_mcp"
|
|
217
|
+
|
|
218
|
+
root = "docs/fixtures/demo_project"
|
|
219
|
+
model = SimpleCovMcp::CoverageModel.new(root: root)
|
|
220
|
+
all_files = model.all_files
|
|
221
|
+
|
|
222
|
+
# Find files below threshold
|
|
223
|
+
THRESHOLD = 80.0
|
|
224
|
+
low_coverage = all_files.select { |f| f['percentage'] < THRESHOLD }
|
|
225
|
+
|
|
226
|
+
if low_coverage.any?
|
|
227
|
+
puts "Files below #{THRESHOLD}%:"
|
|
228
|
+
low_coverage.each do |file|
|
|
229
|
+
puts " #{file['file']}: #{file['percentage']}%"
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
# Group by directory using totals command logic
|
|
234
|
+
dirs = %w[app lib lib/payments lib/ops/jobs].uniq
|
|
235
|
+
dirs.each do |dir|
|
|
236
|
+
pattern = File.join(dir, '**/*.rb')
|
|
237
|
+
totals = model.project_totals(tracked_globs: pattern)
|
|
238
|
+
puts "#{dir}: #{totals['percentage'].round(2)}% (#{totals['files']['total']} files)"
|
|
239
|
+
end
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### Custom Formatting
|
|
243
|
+
|
|
244
|
+
```ruby
|
|
245
|
+
require "simplecov_mcp"
|
|
246
|
+
require "pathname"
|
|
247
|
+
|
|
248
|
+
root = "docs/fixtures/demo_project"
|
|
249
|
+
model = SimpleCovMcp::CoverageModel.new(root: root)
|
|
250
|
+
all_files = model.all_files
|
|
251
|
+
|
|
252
|
+
# Filter to lib/payments (coverage data stores absolute paths)
|
|
253
|
+
lib_root = File.expand_path("lib/payments", File.expand_path(root, Dir.pwd))
|
|
254
|
+
lib_files = all_files.select { |f| f['file'].start_with?(lib_root) }
|
|
255
|
+
|
|
256
|
+
# Generate custom table
|
|
257
|
+
table = model.format_table(lib_files, sort_order: :ascending)
|
|
258
|
+
puts table
|
|
259
|
+
|
|
260
|
+
# Or create your own format
|
|
261
|
+
lib_files.each do |file|
|
|
262
|
+
status = file['percentage'] >= 90 ? '✓' : '⚠'
|
|
263
|
+
relative_path = Pathname.new(file['file']).relative_path_from(Pathname.pwd)
|
|
264
|
+
puts "#{status} #{relative_path}: #{file['percentage']}%"
|
|
265
|
+
end
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## AI Assistant Prompts
|
|
269
|
+
|
|
270
|
+
### Coverage Analysis
|
|
271
|
+
|
|
272
|
+
```
|
|
273
|
+
Using simplecov-mcp, show me a table of all files sorted by coverage percentage.
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
```
|
|
277
|
+
Using simplecov-mcp, find the 10 files with the lowest coverage and create a markdown report with:
|
|
278
|
+
1. File path
|
|
279
|
+
2. Current coverage %
|
|
280
|
+
3. Number of uncovered lines
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
```
|
|
284
|
+
Using simplecov-mcp, analyze the lib/payments/ directory and identify which files should be prioritized for additional testing based on coverage gaps and file complexity.
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### Finding Specific Issues
|
|
288
|
+
|
|
289
|
+
```
|
|
290
|
+
Using simplecov-mcp, show me the uncovered lines in app/controllers/orders_controller.rb with 5 lines of context around each uncovered block.
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
```
|
|
294
|
+
Using simplecov-mcp, find all files in lib/payments/ with less than 80% coverage and list the specific uncovered line numbers for each.
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### Test Generation
|
|
298
|
+
|
|
299
|
+
```
|
|
300
|
+
Using simplecov-mcp, identify the uncovered lines in lib/ops/jobs/report_job.rb and write RSpec tests to cover them.
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
```
|
|
304
|
+
Using simplecov-mcp, analyze coverage gaps in the app/controllers/ directory and generate a test plan prioritizing:
|
|
305
|
+
1. Public API methods
|
|
306
|
+
2. Error handling paths
|
|
307
|
+
3. Edge cases
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Reporting
|
|
311
|
+
|
|
312
|
+
```
|
|
313
|
+
Using simplecov-mcp, create a coverage report showing:
|
|
314
|
+
- Overall project coverage percentage
|
|
315
|
+
- Top 5 files with worst coverage
|
|
316
|
+
- Recommended next steps to improve coverage
|
|
317
|
+
|
|
318
|
+
Format as markdown.
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
## Test Run Integration
|
|
322
|
+
|
|
323
|
+
### Display Low Coverage Files After RSpec
|
|
324
|
+
|
|
325
|
+
Add this to your `spec/spec_helper.rb` to automatically report files below a coverage threshold after each test run:
|
|
326
|
+
|
|
327
|
+
```ruby
|
|
328
|
+
require 'simplecov'
|
|
329
|
+
SimpleCov.start do
|
|
330
|
+
enable_coverage :branch
|
|
331
|
+
add_filter %r{^/spec/}
|
|
332
|
+
track_files 'lib/**/*.rb' # Ensures new/untested files show up with 0%
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
# Report lowest coverage files at the end of the test run
|
|
336
|
+
SimpleCov.at_exit do
|
|
337
|
+
SimpleCov.result.format!
|
|
338
|
+
require 'simplecov_mcp'
|
|
339
|
+
report = SimpleCovMcp::CoverageReporter.report(threshold: 80, count: 5)
|
|
340
|
+
puts report if report
|
|
341
|
+
end
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
This produces output like:
|
|
345
|
+
|
|
346
|
+
```
|
|
347
|
+
Lowest coverage files (< 80%):
|
|
348
|
+
0.0% lib/myapp/config_parser.rb
|
|
349
|
+
19.3% lib/myapp/formatters/source_formatter.rb
|
|
350
|
+
24.0% lib/myapp/model.rb
|
|
351
|
+
26.0% lib/myapp/cli.rb
|
|
352
|
+
45.2% lib/myapp/commands/base.rb
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
**Parameters:**
|
|
356
|
+
- `threshold:` - Coverage percentage below which files are included (default: 80)
|
|
357
|
+
- `count:` - Maximum number of files to show (default: 5)
|
|
358
|
+
|
|
359
|
+
**Returns:** Formatted string, or `nil` if no files are below the threshold.
|
|
360
|
+
|
|
361
|
+
## CI/CD Integration
|
|
362
|
+
|
|
363
|
+
### GitHub Actions
|
|
364
|
+
|
|
365
|
+
**Fail on low coverage:**
|
|
366
|
+
```yaml
|
|
367
|
+
name: Coverage Check
|
|
368
|
+
|
|
369
|
+
on: [push, pull_request]
|
|
370
|
+
|
|
371
|
+
jobs:
|
|
372
|
+
coverage:
|
|
373
|
+
runs-on: ubuntu-latest
|
|
374
|
+
steps:
|
|
375
|
+
- uses: actions/checkout@v3
|
|
376
|
+
|
|
377
|
+
- name: Setup Ruby
|
|
378
|
+
uses: ruby/setup-ruby@v1
|
|
379
|
+
with:
|
|
380
|
+
ruby-version: 3.3
|
|
381
|
+
bundler-cache: true
|
|
382
|
+
|
|
383
|
+
- name: Run tests
|
|
384
|
+
run: bundle exec rspec
|
|
385
|
+
|
|
386
|
+
- name: Install simplecov-mcp
|
|
387
|
+
run: gem install simplecov-mcp
|
|
388
|
+
|
|
389
|
+
- name: Check coverage threshold
|
|
390
|
+
run: |
|
|
391
|
+
smcp -fJ list > coverage.json
|
|
392
|
+
BELOW_THRESHOLD=$(jq '[.files[] | select(.percentage < 80)] | length' coverage.json)
|
|
393
|
+
|
|
394
|
+
# Ruby alternative:
|
|
395
|
+
# BELOW_THRESHOLD=$(ruby -r json -e '
|
|
396
|
+
# puts JSON.parse(File.read("coverage.json"))["files"].count { |f| f["percentage"] < 80 }
|
|
397
|
+
# ')
|
|
398
|
+
|
|
399
|
+
# Rexe alternative:
|
|
400
|
+
# BELOW_THRESHOLD=$(rexe -f coverage.json -op 'self["files"].count { |f| f["percentage"] < 80 }')
|
|
401
|
+
|
|
402
|
+
if [ "$BELOW_THRESHOLD" -gt 0 ]; then
|
|
403
|
+
echo "❌ $BELOW_THRESHOLD files below 80% coverage"
|
|
404
|
+
jq -r '.files[] | select(.percentage < 80) | "\(.file): \(.percentage)%"' coverage.json
|
|
405
|
+
|
|
406
|
+
# Ruby alternative:
|
|
407
|
+
# ruby -r json -e '
|
|
408
|
+
# JSON.parse(File.read("coverage.json"))["files"].select { |f| f["percentage"] < 80 }.each do |f|
|
|
409
|
+
# puts "#{f["file"]}: #{f["percentage"]}%"
|
|
410
|
+
# end
|
|
411
|
+
# '
|
|
412
|
+
|
|
413
|
+
# Rexe alternative:
|
|
414
|
+
# rexe -f coverage.json '
|
|
415
|
+
# self["files"].select { |f| f["percentage"] < 80 }.each do |f|
|
|
416
|
+
# puts "#{f["file"]}: #{f["percentage"]}%"
|
|
417
|
+
# end
|
|
418
|
+
# '
|
|
419
|
+
|
|
420
|
+
exit 1
|
|
421
|
+
fi
|
|
422
|
+
echo "✓ All files meet coverage threshold"
|
|
423
|
+
|
|
424
|
+
- name: Upload coverage report
|
|
425
|
+
uses: actions/upload-artifact@v3
|
|
426
|
+
with:
|
|
427
|
+
name: coverage-report
|
|
428
|
+
path: coverage.json
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
**Check for stale coverage:**
|
|
432
|
+
```yaml
|
|
433
|
+
- name: Verify coverage is fresh
|
|
434
|
+
run: simplecov-mcp --staleness error || exit 1
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
### GitLab CI
|
|
438
|
+
|
|
439
|
+
```yaml
|
|
440
|
+
test:
|
|
441
|
+
image: ruby:3.3
|
|
442
|
+
before_script:
|
|
443
|
+
- gem install simplecov-mcp
|
|
444
|
+
script:
|
|
445
|
+
- bundle exec rspec
|
|
446
|
+
- simplecov-mcp --staleness error
|
|
447
|
+
artifacts:
|
|
448
|
+
paths:
|
|
449
|
+
- coverage/
|
|
450
|
+
reports:
|
|
451
|
+
coverage_report:
|
|
452
|
+
coverage_format: simplecov
|
|
453
|
+
path: coverage/.resultset.json
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
### Custom Success Predicate
|
|
457
|
+
|
|
458
|
+
```ruby
|
|
459
|
+
# coverage_policy.rb
|
|
460
|
+
->(model) do
|
|
461
|
+
all_files = model.all_files
|
|
462
|
+
|
|
463
|
+
# Must have at least 80% average coverage
|
|
464
|
+
totals = model.project_totals
|
|
465
|
+
return false if totals['percentage'] < 80.0
|
|
466
|
+
|
|
467
|
+
# No files below 60%
|
|
468
|
+
return false if all_files.any? { |f| f['percentage'] < 60.0 }
|
|
469
|
+
|
|
470
|
+
# lib/ files must average 90%
|
|
471
|
+
lib_totals = model.project_totals(tracked_globs: ['lib/**/*.rb'])
|
|
472
|
+
return false if lib_totals['percentage'] < 90.0
|
|
473
|
+
|
|
474
|
+
true
|
|
475
|
+
end
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
```bash
|
|
479
|
+
# Use in CI
|
|
480
|
+
smcp validate coverage_policy.rb
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
## Advanced Usage
|
|
484
|
+
|
|
485
|
+
### Directory-Level Analysis
|
|
486
|
+
|
|
487
|
+
```ruby
|
|
488
|
+
require "simplecov_mcp"
|
|
489
|
+
|
|
490
|
+
root = "docs/fixtures/demo_project"
|
|
491
|
+
model = SimpleCovMcp::CoverageModel.new(root: root)
|
|
492
|
+
all_files = model.all_files
|
|
493
|
+
|
|
494
|
+
# Calculate coverage by directory (uses the same data as `simplecov-mcp totals`)
|
|
495
|
+
patterns = %w[
|
|
496
|
+
app/**/*.rb
|
|
497
|
+
lib/payments/**/*.rb
|
|
498
|
+
lib/ops/jobs/**/*.rb
|
|
499
|
+
]
|
|
500
|
+
|
|
501
|
+
results = patterns.map do |pattern|
|
|
502
|
+
totals = model.project_totals(tracked_globs: pattern)
|
|
503
|
+
|
|
504
|
+
{
|
|
505
|
+
directory: pattern,
|
|
506
|
+
files: totals['files']['total'],
|
|
507
|
+
coverage: totals['percentage'].round(2),
|
|
508
|
+
covered: totals['lines']['covered'],
|
|
509
|
+
total: totals['lines']['total']
|
|
510
|
+
}
|
|
511
|
+
end
|
|
512
|
+
|
|
513
|
+
# Sort by coverage ascending
|
|
514
|
+
results.sort_by { |r| r[:coverage] }.each do |r|
|
|
515
|
+
puts "#{r[:directory]}: #{r[:coverage]}% (#{r[:files]} files)"
|
|
516
|
+
end
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
### Coverage Delta Tracking
|
|
520
|
+
|
|
521
|
+
```ruby
|
|
522
|
+
require "simplecov_mcp"
|
|
523
|
+
require "json"
|
|
524
|
+
|
|
525
|
+
# Save current coverage
|
|
526
|
+
root = "docs/fixtures/demo_project"
|
|
527
|
+
model = SimpleCovMcp::CoverageModel.new(root: root)
|
|
528
|
+
current = model.all_files
|
|
529
|
+
|
|
530
|
+
File.write("coverage_baseline.json", JSON.pretty_generate(current))
|
|
531
|
+
|
|
532
|
+
# Later, compare with baseline
|
|
533
|
+
baseline = JSON.parse(File.read("coverage_baseline.json"))
|
|
534
|
+
|
|
535
|
+
current.each do |file|
|
|
536
|
+
baseline_file = baseline.find { |f| f['file'] == file['file'] }
|
|
537
|
+
next unless baseline_file
|
|
538
|
+
|
|
539
|
+
delta = file['percentage'] - baseline_file['percentage']
|
|
540
|
+
if delta.abs > 0.1 # Changed by more than 0.1%
|
|
541
|
+
symbol = delta > 0 ? '↑' : '↓'
|
|
542
|
+
puts "#{symbol} #{file['file']}: #{baseline_file['percentage']}% → #{file['percentage']}%"
|
|
543
|
+
end
|
|
544
|
+
end
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
### Integration with Code Review
|
|
548
|
+
|
|
549
|
+
```ruby
|
|
550
|
+
# pr_coverage_check.rb
|
|
551
|
+
require "simplecov_mcp"
|
|
552
|
+
require "json"
|
|
553
|
+
|
|
554
|
+
model = SimpleCovMcp::CoverageModel.new
|
|
555
|
+
|
|
556
|
+
# Get changed files from PR (example using git)
|
|
557
|
+
changed_files = `git diff --name-only origin/main`.split("\n")
|
|
558
|
+
changed_files.select! { |f| f.end_with?('.rb') }
|
|
559
|
+
|
|
560
|
+
puts "## Coverage Report for Changed Files\n\n"
|
|
561
|
+
puts "| File | Coverage | Status |"
|
|
562
|
+
puts "|------|----------|--------|"
|
|
563
|
+
|
|
564
|
+
changed_files.each do |file|
|
|
565
|
+
begin
|
|
566
|
+
summary = model.summary_for(file)
|
|
567
|
+
percentage = summary['summary']['percentage']
|
|
568
|
+
status = percentage >= 80 ? '✅' : '⚠️'
|
|
569
|
+
puts "| #{file} | #{percentage}% | #{status} |"
|
|
570
|
+
rescue
|
|
571
|
+
puts "| #{file} | N/A | ❌ No coverage |"
|
|
572
|
+
end
|
|
573
|
+
end
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
## Example Scripts
|
|
577
|
+
|
|
578
|
+
The [`/examples`](/examples) directory contains runnable scripts:
|
|
579
|
+
|
|
580
|
+
- **[filter_and_table_demo.rb](../examples/filter_and_table_demo.rb)** - Filter and format coverage data
|
|
581
|
+
- **[success_predicates/](/examples/success_predicates/)** - Custom coverage policy examples
|
|
582
|
+
|
|
583
|
+
## Related Documentation
|
|
584
|
+
|
|
585
|
+
- [CLI Usage Guide](CLI_USAGE.md) - Complete command reference
|
|
586
|
+
- [Library API Guide](LIBRARY_API.md) - Ruby API documentation
|
|
587
|
+
- [MCP Integration](MCP_INTEGRATION.md) - AI assistant setup
|
|
588
|
+
- [Troubleshooting](TROUBLESHOOTING.md) - Common issues
|