simplecov-mcp 0.2.0 → 0.3.0
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 +12 -1
- data/lib/simple_cov_mcp/base_tool.rb +16 -0
- data/lib/simple_cov_mcp/tools/all_files_coverage_tool.rb +1 -1
- data/lib/simple_cov_mcp/tools/coverage_detailed_tool.rb +1 -1
- data/lib/simple_cov_mcp/tools/coverage_raw_tool.rb +1 -1
- data/lib/simple_cov_mcp/tools/coverage_summary_tool.rb +1 -1
- data/lib/simple_cov_mcp/tools/help_tool.rb +2 -1
- data/lib/simple_cov_mcp/tools/uncovered_lines_tool.rb +1 -1
- data/lib/simple_cov_mcp/version.rb +1 -1
- data/spec/all_files_coverage_tool_spec.rb +6 -4
- data/spec/coverage_detailed_tool_spec.rb +36 -0
- data/spec/coverage_raw_tool_spec.rb +32 -0
- data/spec/coverage_summary_tool_spec.rb +39 -0
- data/spec/help_tool_spec.rb +13 -12
- data/spec/uncovered_lines_tool_spec.rb +33 -0
- data/spec/version_spec.rb +6 -1
- data/spec/version_tool_spec.rb +22 -0
- metadata +10 -6
- data/spec/cli_json_source_spec.rb +0 -92
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '08e9a29ca4d2e2daafd4620690021de136eaa2cfc2ba652340f73d8b69547d8d'
|
4
|
+
data.tar.gz: 784e8ef1f21e6ed4e37783a840935e909d8a6d1f6ae4c941da34d0e6635f9f95
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 52e2a95fda335f611f38cd0b97164a62e381532ceb71b330c32023be10eeb8d97e999bdc32f0dafac168f0470cac481151e78f223623e90a377fe79ef807705b
|
7
|
+
data.tar.gz: 42ce8d9f01b069f2e3dd0d0e817cd135298fa14d06553ef5b0771d37baf9f5bd4c194d9a9296a823966da21ec505e69b41fd489dfcfa1b0e0b74bb63949b7a21
|
data/README.md
CHANGED
@@ -92,6 +92,12 @@ echo '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"all_files_
|
|
92
92
|
|
93
93
|
Tip: In an MCP-capable editor/agent, configure `simplecov-mcp` as a stdio server and call the same tool names with the `arguments` shown above.
|
94
94
|
|
95
|
+
Response content types (MCP):
|
96
|
+
|
97
|
+
- JSON data returns as a single `type: "resource"` item with `resource.mimeType: "application/json"` and the JSON string in `resource.text` (e.g., `name: "coverage_summary.json"`).
|
98
|
+
- Human-readable strings (e.g., the table and version) return as `type: "text"`.
|
99
|
+
- Errors return as `type: "text"` with a friendly message.
|
100
|
+
|
95
101
|
Resultset resolution:
|
96
102
|
|
97
103
|
- If `resultset:` is provided, it may be:
|
@@ -120,7 +126,7 @@ Using simplecov-mcp, show me a table of all files and their coverages.
|
|
120
126
|
|
121
127
|
----
|
122
128
|
|
123
|
-
Using simplecov-mcp, find the uncovered code lines and report to me:
|
129
|
+
Using simplecov-mcp, find the uncovered code lines and report to me, in a markdown file:
|
124
130
|
|
125
131
|
* the most important coverage omissions to address
|
126
132
|
* the simplest coverage omissions to address
|
@@ -348,6 +354,11 @@ Available tools:
|
|
348
354
|
- Returns `{ files: [{"file","covered","total","percentage","stale"}, ...] }` where `stale` is a boolean.
|
349
355
|
- `version_tool()` — returns version information
|
350
356
|
|
357
|
+
Response shape and content types:
|
358
|
+
|
359
|
+
- JSON tools above return a single content item `{ "type": "resource", "resource": { "mimeType": "application/json", "text": "{...}", "name": "<tool>.json" } }`.
|
360
|
+
- `coverage_table_tool` and `version_tool` return `{ "type": "text", "text": "..." }`.
|
361
|
+
|
351
362
|
Notes:
|
352
363
|
|
353
364
|
- `resultset` lets clients pass a nonstandard path to `.resultset.json` directly (absolute or relative to `root`). It may be:
|
@@ -44,6 +44,22 @@ module SimpleCovMcp
|
|
44
44
|
::MCP::Tool::Response.new([{ type: 'text', text: "Error: #{normalized.user_friendly_message}" }])
|
45
45
|
end
|
46
46
|
|
47
|
+
# Respond with JSON as a resource to avoid clients mutating content types.
|
48
|
+
# The resource embeds the JSON string with a clear MIME type.
|
49
|
+
def self.respond_json(payload, name: 'data.json', pretty: false)
|
50
|
+
json = pretty ? JSON.pretty_generate(payload) : JSON.generate(payload)
|
51
|
+
::MCP::Tool::Response.new([
|
52
|
+
{
|
53
|
+
'type' => 'resource',
|
54
|
+
'resource' => {
|
55
|
+
'mimeType' => 'application/json',
|
56
|
+
'text' => json,
|
57
|
+
'name' => name
|
58
|
+
}
|
59
|
+
}
|
60
|
+
])
|
61
|
+
end
|
62
|
+
|
47
63
|
private
|
48
64
|
|
49
65
|
def self.log_mcp_error(error, tool_name)
|
@@ -53,7 +53,7 @@ module SimpleCovMcp
|
|
53
53
|
stale_count = files.count { |f| f['stale'] }
|
54
54
|
ok_count = total - stale_count
|
55
55
|
payload = { files: files, counts: { total: total, ok: ok_count, stale: stale_count } }
|
56
|
-
|
56
|
+
respond_json(payload, name: 'all_files_coverage.json')
|
57
57
|
rescue => e
|
58
58
|
handle_mcp_error(e, 'AllFilesCoverageTool')
|
59
59
|
end
|
@@ -19,7 +19,7 @@ module SimpleCovMcp
|
|
19
19
|
mode = stale
|
20
20
|
model = CoverageModel.new(root: root, resultset: resultset, staleness: mode)
|
21
21
|
data = model.detailed_for(path)
|
22
|
-
|
22
|
+
respond_json(data, name: 'coverage_detailed.json', pretty: true)
|
23
23
|
rescue => e
|
24
24
|
handle_mcp_error(e, 'CoverageDetailedTool')
|
25
25
|
end
|
@@ -19,7 +19,7 @@ module SimpleCovMcp
|
|
19
19
|
mode = stale
|
20
20
|
model = CoverageModel.new(root: root, resultset: resultset, staleness: mode)
|
21
21
|
data = model.raw_for(path)
|
22
|
-
|
22
|
+
respond_json(data, name: 'coverage_raw.json', pretty: true)
|
23
23
|
rescue => e
|
24
24
|
handle_mcp_error(e, 'CoverageRawTool')
|
25
25
|
end
|
@@ -19,7 +19,7 @@ module SimpleCovMcp
|
|
19
19
|
mode = stale
|
20
20
|
model = CoverageModel.new(root: root, resultset: resultset, staleness: mode)
|
21
21
|
data = model.summary_for(path)
|
22
|
-
|
22
|
+
respond_json(data, name: 'coverage_summary.json', pretty: true)
|
23
23
|
rescue => e
|
24
24
|
handle_mcp_error(e, 'CoverageSummaryTool')
|
25
25
|
end
|
@@ -88,7 +88,8 @@ module SimpleCovMcp
|
|
88
88
|
entries = TOOL_GUIDE.map { |guide| format_entry(guide) }
|
89
89
|
entries = filter_entries(entries, query) if query && !query.strip.empty?
|
90
90
|
|
91
|
-
|
91
|
+
data = { query: query, tools: entries }
|
92
|
+
respond_json(data, name: 'tools_help.json')
|
92
93
|
rescue => e
|
93
94
|
handle_mcp_error(e, 'HelpTool')
|
94
95
|
end
|
@@ -19,7 +19,7 @@ module SimpleCovMcp
|
|
19
19
|
mode = stale
|
20
20
|
model = CoverageModel.new(root: root, resultset: resultset, staleness: mode)
|
21
21
|
data = model.uncovered_for(path)
|
22
|
-
|
22
|
+
respond_json(data, name: 'uncovered_lines.json', pretty: true)
|
23
23
|
rescue => e
|
24
24
|
handle_mcp_error(e, 'UncoveredLinesTool')
|
25
25
|
end
|
@@ -28,11 +28,13 @@ RSpec.describe SimpleCovMcp::Tools::AllFilesCoverageTool do
|
|
28
28
|
|
29
29
|
response = described_class.call(root: root, server_context: server_context)
|
30
30
|
entry = response.payload.first
|
31
|
-
|
31
|
+
expect(entry['type']).to eq('resource')
|
32
|
+
expect(entry['resource']).to include('mimeType' => 'application/json')
|
33
|
+
json = JSON.parse(entry['resource']['text'])
|
32
34
|
|
33
|
-
expect(json).to have_key(
|
34
|
-
files = json[
|
35
|
-
counts = json[
|
35
|
+
expect(json).to have_key('files')
|
36
|
+
files = json['files']
|
37
|
+
counts = json['counts']
|
36
38
|
|
37
39
|
expect(files.length).to eq(2)
|
38
40
|
expect(counts).to include('total' => 2).or include(total: 2)
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'simple_cov_mcp/tools/coverage_detailed_tool'
|
5
|
+
|
6
|
+
RSpec.describe SimpleCovMcp::Tools::CoverageDetailedTool do
|
7
|
+
let(:server_context) { instance_double('ServerContext').as_null_object }
|
8
|
+
|
9
|
+
before do
|
10
|
+
stub_const('MCP::Tool::Response', Struct.new(:payload))
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'returns JSON as an application/json resource' do
|
14
|
+
model = instance_double(SimpleCovMcp::CoverageModel)
|
15
|
+
allow(SimpleCovMcp::CoverageModel).to receive(:new).and_return(model)
|
16
|
+
allow(model).to receive(:detailed_for).with('lib/foo.rb').and_return(
|
17
|
+
{
|
18
|
+
'file' => '/abs/path/lib/foo.rb',
|
19
|
+
'lines' => [
|
20
|
+
{ 'line' => 1, 'hits' => 1, 'covered' => true },
|
21
|
+
{ 'line' => 2, 'hits' => 0, 'covered' => false }
|
22
|
+
],
|
23
|
+
'summary' => { 'covered' => 1, 'total' => 2, 'pct' => 50.0 }
|
24
|
+
}
|
25
|
+
)
|
26
|
+
|
27
|
+
response = described_class.call(path: 'lib/foo.rb', server_context: server_context)
|
28
|
+
item = response.payload.first
|
29
|
+
expect(item['type']).to eq('resource')
|
30
|
+
expect(item['resource']).to include('mimeType' => 'application/json', 'name' => 'coverage_detailed.json')
|
31
|
+
data = JSON.parse(item['resource']['text'])
|
32
|
+
expect(data).to include('file', 'lines', 'summary')
|
33
|
+
expect(data['lines']).to be_an(Array)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'simple_cov_mcp/tools/coverage_raw_tool'
|
5
|
+
|
6
|
+
RSpec.describe SimpleCovMcp::Tools::CoverageRawTool do
|
7
|
+
let(:server_context) { instance_double('ServerContext').as_null_object }
|
8
|
+
|
9
|
+
before do
|
10
|
+
stub_const('MCP::Tool::Response', Struct.new(:payload))
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'returns JSON as an application/json resource' do
|
14
|
+
model = instance_double(SimpleCovMcp::CoverageModel)
|
15
|
+
allow(SimpleCovMcp::CoverageModel).to receive(:new).and_return(model)
|
16
|
+
allow(model).to receive(:raw_for).with('lib/foo.rb').and_return(
|
17
|
+
{
|
18
|
+
'file' => '/abs/path/lib/foo.rb',
|
19
|
+
'lines' => [nil, 1, 0]
|
20
|
+
}
|
21
|
+
)
|
22
|
+
|
23
|
+
response = described_class.call(path: 'lib/foo.rb', server_context: server_context)
|
24
|
+
item = response.payload.first
|
25
|
+
expect(item['type']).to eq('resource')
|
26
|
+
expect(item['resource']).to include('mimeType' => 'application/json', 'name' => 'coverage_raw.json')
|
27
|
+
data = JSON.parse(item['resource']['text'])
|
28
|
+
expect(data).to include('file', 'lines')
|
29
|
+
expect(data['lines']).to be_an(Array)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'simple_cov_mcp/tools/coverage_summary_tool'
|
5
|
+
|
6
|
+
RSpec.describe SimpleCovMcp::Tools::CoverageSummaryTool do
|
7
|
+
let(:server_context) { instance_double('ServerContext').as_null_object }
|
8
|
+
|
9
|
+
before do
|
10
|
+
response_class = Class.new do
|
11
|
+
attr_reader :payload, :meta
|
12
|
+
def initialize(payload, meta: nil)
|
13
|
+
@payload = payload
|
14
|
+
@meta = meta
|
15
|
+
end
|
16
|
+
end
|
17
|
+
stub_const('MCP::Tool::Response', response_class)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'returns JSON as an application/json resource' do
|
21
|
+
model = instance_double(SimpleCovMcp::CoverageModel)
|
22
|
+
allow(SimpleCovMcp::CoverageModel).to receive(:new).and_return(model)
|
23
|
+
allow(model).to receive(:summary_for).with('lib/foo.rb').and_return(
|
24
|
+
{
|
25
|
+
'file' => '/abs/path/lib/foo.rb',
|
26
|
+
'summary' => { 'covered' => 10, 'total' => 12, 'pct' => 83.33 }
|
27
|
+
}
|
28
|
+
)
|
29
|
+
|
30
|
+
response = described_class.call(path: 'lib/foo.rb', server_context: server_context)
|
31
|
+
item = response.payload.first
|
32
|
+
expect(item['type']).to eq('resource')
|
33
|
+
expect(item['resource']).to include('mimeType' => 'application/json', 'name' => 'coverage_summary.json')
|
34
|
+
data = JSON.parse(item['resource']['text'])
|
35
|
+
expect(data).to include('file', 'summary')
|
36
|
+
expect(data['summary']).to include('covered', 'total', 'pct')
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
data/spec/help_tool_spec.rb
CHANGED
@@ -10,9 +10,9 @@ RSpec.describe SimpleCovMcp::Tools::HelpTool do
|
|
10
10
|
response_class = Class.new do
|
11
11
|
attr_reader :payload, :meta
|
12
12
|
|
13
|
-
def initialize(payload
|
13
|
+
def initialize(payload)
|
14
14
|
@payload = payload
|
15
|
-
@meta =
|
15
|
+
@meta = nil
|
16
16
|
end
|
17
17
|
end
|
18
18
|
stub_const('MCP::Tool::Response', response_class)
|
@@ -20,28 +20,29 @@ RSpec.describe SimpleCovMcp::Tools::HelpTool do
|
|
20
20
|
|
21
21
|
it 'returns guidance for each registered tool' do
|
22
22
|
response = described_class.call(server_context: server_context)
|
23
|
-
expect(response.meta
|
23
|
+
expect(response.meta).to be_nil
|
24
24
|
|
25
25
|
payload = response.payload.first
|
26
|
-
expect(payload[
|
27
|
-
|
28
|
-
data = payload[
|
29
|
-
tool_names = data[
|
26
|
+
expect(payload['type']).to eq('resource')
|
27
|
+
expect(payload['resource']).to include('mimeType' => 'application/json')
|
28
|
+
data = JSON.parse(payload['resource']['text'])
|
29
|
+
tool_names = data['tools'].map { |entry| entry['tool'] }
|
30
30
|
|
31
31
|
expect(tool_names).to include('coverage_summary_tool', 'uncovered_lines_tool', 'all_files_coverage_tool', 'coverage_table_tool', 'version_tool')
|
32
|
-
expect(data[
|
32
|
+
expect(data['tools']).to all(include('use_when', 'avoid_when', 'inputs', 'example'))
|
33
33
|
end
|
34
34
|
|
35
35
|
it 'filters entries when a query is provided' do
|
36
36
|
response = described_class.call(query: 'uncovered', server_context: server_context)
|
37
37
|
payload = response.payload.first
|
38
|
-
|
38
|
+
expect(payload['type']).to eq('resource')
|
39
|
+
data = JSON.parse(payload['resource']['text'])
|
39
40
|
|
40
|
-
expect(data[
|
41
|
-
expect(data[
|
41
|
+
expect(data['tools']).not_to be_empty
|
42
|
+
expect(data['tools']).to all(satisfy do |entry|
|
42
43
|
combined = [entry['tool'], entry['label'], entry['use_when'], entry['avoid_when']].compact.join(' ').downcase
|
43
44
|
combined.include?('uncovered')
|
44
45
|
end)
|
45
|
-
expect(data[
|
46
|
+
expect(data['tools'].map { |entry| entry['tool'] }).to include('uncovered_lines_tool')
|
46
47
|
end
|
47
48
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'simple_cov_mcp/tools/uncovered_lines_tool'
|
5
|
+
|
6
|
+
RSpec.describe SimpleCovMcp::Tools::UncoveredLinesTool do
|
7
|
+
let(:server_context) { instance_double('ServerContext').as_null_object }
|
8
|
+
|
9
|
+
before do
|
10
|
+
stub_const('MCP::Tool::Response', Struct.new(:payload))
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'returns JSON as an application/json resource' do
|
14
|
+
model = instance_double(SimpleCovMcp::CoverageModel)
|
15
|
+
allow(SimpleCovMcp::CoverageModel).to receive(:new).and_return(model)
|
16
|
+
allow(model).to receive(:uncovered_for).with('lib/foo.rb').and_return(
|
17
|
+
{
|
18
|
+
'file' => '/abs/path/lib/foo.rb',
|
19
|
+
'uncovered' => [5, 9, 12],
|
20
|
+
'summary' => { 'covered' => 10, 'total' => 12, 'pct' => 83.33 }
|
21
|
+
}
|
22
|
+
)
|
23
|
+
|
24
|
+
response = described_class.call(path: 'lib/foo.rb', server_context: server_context)
|
25
|
+
item = response.payload.first
|
26
|
+
expect(item['type']).to eq('resource')
|
27
|
+
expect(item['resource']).to include('mimeType' => 'application/json', 'name' => 'uncovered_lines.json')
|
28
|
+
data = JSON.parse(item['resource']['text'])
|
29
|
+
expect(data).to include('file', 'uncovered', 'summary')
|
30
|
+
expect(data['uncovered']).to eq([5, 9, 12])
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
data/spec/version_spec.rb
CHANGED
@@ -5,6 +5,11 @@ require 'spec_helper'
|
|
5
5
|
RSpec.describe 'Version constant' do
|
6
6
|
it 'exposes a semver-like version string' do
|
7
7
|
expect(SimpleCovMcp::VERSION).to be_a(String)
|
8
|
-
|
8
|
+
# Named fragments for readability (simplified SemVer)
|
9
|
+
CORE = /\d+\.\d+\.\d+/
|
10
|
+
ID = /[[:alnum:].-]+/ # ASCII alnum plus dot/hyphen
|
11
|
+
SEMVER = /\A#{CORE.source}(?:-#{ID.source})?(?:\+#{ID.source})?\z/
|
12
|
+
|
13
|
+
expect(SimpleCovMcp::VERSION).to match(SEMVER)
|
9
14
|
end
|
10
15
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'simple_cov_mcp/tools/version_tool'
|
5
|
+
|
6
|
+
RSpec.describe SimpleCovMcp::Tools::VersionTool do
|
7
|
+
let(:server_context) { instance_double('ServerContext').as_null_object }
|
8
|
+
|
9
|
+
before do
|
10
|
+
stub_const('MCP::Tool::Response', Struct.new(:payload))
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'returns a text payload with the version string' do
|
14
|
+
response = described_class.call(server_context: server_context)
|
15
|
+
item = response.payload.first
|
16
|
+
expect(item[:type] || item['type']).to eq('text')
|
17
|
+
text = item[:text] || item['text']
|
18
|
+
expect(text).to include('SimpleCovMcp version:')
|
19
|
+
expect(text).to include(SimpleCovMcp::VERSION)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: simplecov-mcp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Keith R. Bennett
|
@@ -13,16 +13,16 @@ dependencies:
|
|
13
13
|
name: mcp
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
15
15
|
requirements:
|
16
|
-
- - "
|
16
|
+
- - ">="
|
17
17
|
- !ruby/object:Gem::Version
|
18
|
-
version: '0.
|
18
|
+
version: '0.3'
|
19
19
|
type: :runtime
|
20
20
|
prerelease: false
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
22
22
|
requirements:
|
23
|
-
- - "
|
23
|
+
- - ">="
|
24
24
|
- !ruby/object:Gem::Version
|
25
|
-
version: '0.
|
25
|
+
version: '0.3'
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
27
|
name: awesome_print
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
@@ -106,11 +106,13 @@ files:
|
|
106
106
|
- spec/all_files_coverage_tool_spec.rb
|
107
107
|
- spec/base_tool_spec.rb
|
108
108
|
- spec/cli_error_spec.rb
|
109
|
-
- spec/cli_json_source_spec.rb
|
110
109
|
- spec/cli_source_spec.rb
|
111
110
|
- spec/cli_spec.rb
|
112
111
|
- spec/cli_table_spec.rb
|
113
112
|
- spec/cli_usage_spec.rb
|
113
|
+
- spec/coverage_detailed_tool_spec.rb
|
114
|
+
- spec/coverage_raw_tool_spec.rb
|
115
|
+
- spec/coverage_summary_tool_spec.rb
|
114
116
|
- spec/coverage_table_tool_spec.rb
|
115
117
|
- spec/error_handler_spec.rb
|
116
118
|
- spec/errors_stale_spec.rb
|
@@ -123,8 +125,10 @@ files:
|
|
123
125
|
- spec/simplecov_mcp_model_spec.rb
|
124
126
|
- spec/spec_helper.rb
|
125
127
|
- spec/staleness_more_spec.rb
|
128
|
+
- spec/uncovered_lines_tool_spec.rb
|
126
129
|
- spec/util_spec.rb
|
127
130
|
- spec/version_spec.rb
|
131
|
+
- spec/version_tool_spec.rb
|
128
132
|
homepage: https://github.com/keithrbennett/simplecov-mcp
|
129
133
|
licenses:
|
130
134
|
- MIT
|
@@ -1,92 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
RSpec.describe SimpleCovMcp::CoverageCLI do
|
6
|
-
let(:root) { (FIXTURES / 'project1').to_s }
|
7
|
-
|
8
|
-
def run_cli_json(*argv)
|
9
|
-
cli = described_class.new
|
10
|
-
out = nil
|
11
|
-
silence_output do |stdout, _stderr|
|
12
|
-
cli.run(argv.flatten)
|
13
|
-
out = stdout.string
|
14
|
-
end
|
15
|
-
JSON.parse(out)
|
16
|
-
end
|
17
|
-
|
18
|
-
it 'includes source rows in JSON for summary --source=full' do
|
19
|
-
data = run_cli_json('summary', 'lib/foo.rb', '--root', root, '--resultset', 'coverage', '--json', '--source=full')
|
20
|
-
expect(data['file']).to eq('lib/foo.rb')
|
21
|
-
expect(data['source']).to be_an(Array)
|
22
|
-
expect(data['source'].first).to include('line', 'code', 'hits', 'covered')
|
23
|
-
end
|
24
|
-
|
25
|
-
it 'includes source rows in JSON for uncovered --source=uncovered' do
|
26
|
-
data = run_cli_json('uncovered', 'lib/foo.rb', '--root', root, '--resultset', 'coverage', '--json', '--source=uncovered', '--source-context', '1')
|
27
|
-
expect(data['file']).to eq('lib/foo.rb')
|
28
|
-
expect(data['source']).to be_an(Array)
|
29
|
-
# Only a subset of lines around uncovered should appear
|
30
|
-
lines = data['source'].map { |h| h['line'] }
|
31
|
-
expect(lines).to include(2) # the uncovered line
|
32
|
-
end
|
33
|
-
|
34
|
-
it 'includes source rows in JSON for detailed --source=full' do
|
35
|
-
data = run_cli_json('detailed', 'lib/foo.rb', '--root', root, '--resultset', 'coverage', '--json', '--source=full')
|
36
|
-
expect(data['file']).to eq('lib/foo.rb')
|
37
|
-
expect(data['lines']).to be_an(Array)
|
38
|
-
expect(data['source']).to be_an(Array)
|
39
|
-
end
|
40
|
-
|
41
|
-
it 'renders uncovered source with various context sizes without error' do
|
42
|
-
[0, -5, 50].each do |ctx|
|
43
|
-
out, err, status = begin
|
44
|
-
cli = described_class.new
|
45
|
-
s = nil
|
46
|
-
o = e = nil
|
47
|
-
silence_output do |stdout, stderr|
|
48
|
-
begin
|
49
|
-
cli.run(['uncovered', 'lib/foo.rb', '--root', root, '--resultset', 'coverage', '--source=uncovered', '--source-context', ctx.to_s, '--no-color'])
|
50
|
-
s = 0
|
51
|
-
rescue SystemExit => ex
|
52
|
-
s = ex.status
|
53
|
-
end
|
54
|
-
o = stdout.string
|
55
|
-
e = stderr.string
|
56
|
-
end
|
57
|
-
[o, e, s]
|
58
|
-
end
|
59
|
-
expect(status).to eq(0)
|
60
|
-
expect(err).to eq("")
|
61
|
-
expect(out).to include('File: lib/foo.rb')
|
62
|
-
expect(out).to include('Uncovered lines: 2')
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
it 'respects --color and --no-color for source rendering' do
|
67
|
-
# Force color on
|
68
|
-
out_color = begin
|
69
|
-
cli = described_class.new
|
70
|
-
silence_output do |stdout, _stderr|
|
71
|
-
cli.run(['summary', 'lib/foo.rb', '--root', root, '--resultset', 'coverage', '--source', '--color'])
|
72
|
-
stdout.string
|
73
|
-
end
|
74
|
-
end
|
75
|
-
# If source table is rendered, it should contain ANSI escapes when color is on
|
76
|
-
if out_color.include?('Line') && out_color.include?('|')
|
77
|
-
expect(out_color).to match(/\e\[\d+m/)
|
78
|
-
else
|
79
|
-
expect(out_color).to include('[source not available]')
|
80
|
-
end
|
81
|
-
|
82
|
-
# Force color off: expect no ANSI sequences
|
83
|
-
out_nocolor = begin
|
84
|
-
cli = described_class.new
|
85
|
-
silence_output do |stdout, _stderr|
|
86
|
-
cli.run(['summary', 'lib/foo.rb', '--root', root, '--resultset', 'coverage', '--source', '--no-color'])
|
87
|
-
stdout.string
|
88
|
-
end
|
89
|
-
end
|
90
|
-
expect(out_nocolor).not_to match(/\e\[\d+m/)
|
91
|
-
end
|
92
|
-
end
|