coverband 6.1.5 → 6.1.7
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/.github/workflows/main.yml +4 -4
- data/README.md +123 -0
- data/agents.md +217 -0
- data/bin/coverband-mcp +42 -0
- data/changes.md +23 -0
- data/coverband/log.272267 +1 -0
- data/coverband.gemspec +3 -1
- data/lib/coverband/adapters/hash_redis_store.rb +1 -3
- data/lib/coverband/collectors/route_tracker.rb +1 -1
- data/lib/coverband/collectors/view_tracker.rb +21 -13
- data/lib/coverband/configuration.rb +57 -18
- data/lib/coverband/integrations/resque.rb +2 -2
- data/lib/coverband/mcp/http_handler.rb +118 -0
- data/lib/coverband/mcp/server.rb +116 -0
- data/lib/coverband/mcp/tools/get_coverage_summary.rb +41 -0
- data/lib/coverband/mcp/tools/get_dead_methods.rb +69 -0
- data/lib/coverband/mcp/tools/get_file_coverage.rb +72 -0
- data/lib/coverband/mcp/tools/get_route_tracker_data.rb +60 -0
- data/lib/coverband/mcp/tools/get_translation_tracker_data.rb +60 -0
- data/lib/coverband/mcp/tools/get_uncovered_files.rb +73 -0
- data/lib/coverband/mcp/tools/get_view_tracker_data.rb +60 -0
- data/lib/coverband/mcp.rb +27 -0
- data/lib/coverband/reporters/base.rb +2 -4
- data/lib/coverband/reporters/web.rb +17 -14
- data/lib/coverband/utils/lines_classifier.rb +1 -1
- data/lib/coverband/utils/railtie.rb +1 -1
- data/lib/coverband/utils/result.rb +2 -1
- data/lib/coverband/utils/source_file.rb +5 -5
- data/lib/coverband/utils/tasks.rb +31 -0
- data/lib/coverband/version.rb +1 -1
- data/lib/coverband.rb +7 -7
- data/test/benchmarks/benchmark.rake +7 -15
- data/test/coverband/file_store_integration_test.rb +72 -0
- data/test/coverband/file_store_redis_error_test.rb +56 -0
- data/test/coverband/github_issue_586_test.rb +46 -0
- data/test/coverband/initialization_timing_test.rb +71 -0
- data/test/coverband/mcp/http_handler_test.rb +159 -0
- data/test/coverband/mcp/security_test.rb +145 -0
- data/test/coverband/mcp/server_test.rb +125 -0
- data/test/coverband/mcp/tools/get_coverage_summary_test.rb +75 -0
- data/test/coverband/mcp/tools/get_dead_methods_test.rb +162 -0
- data/test/coverband/mcp/tools/get_file_coverage_test.rb +159 -0
- data/test/coverband/mcp/tools/get_route_tracker_data_test.rb +122 -0
- data/test/coverband/mcp/tools/get_translation_tracker_data_test.rb +122 -0
- data/test/coverband/mcp/tools/get_uncovered_files_test.rb +177 -0
- data/test/coverband/mcp/tools/get_view_tracker_data_test.rb +122 -0
- data/test/coverband/reporters/web_test.rb +5 -0
- data/test/coverband/track_key_test.rb +9 -9
- data/test/coverband/tracker_initialization_test.rb +75 -0
- data/test/coverband/user_environment_simulation_test.rb +75 -0
- data/test/coverband/utils/lines_classifier_test.rb +1 -1
- data/test/integration/mcp_integration_test.rb +175 -0
- data/test/test_helper.rb +4 -5
- metadata +67 -11
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require File.expand_path("../../../test_helper", File.dirname(__FILE__))
|
|
4
|
+
|
|
5
|
+
begin
|
|
6
|
+
require "coverband/mcp"
|
|
7
|
+
rescue LoadError
|
|
8
|
+
puts "MCP gem not available, skipping MCP tools tests"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
if defined?(Coverband::MCP)
|
|
12
|
+
class GetUncoveredFilesTest < Minitest::Test
|
|
13
|
+
def setup
|
|
14
|
+
super
|
|
15
|
+
Coverband.configure do |config|
|
|
16
|
+
config.store = Coverband::Adapters::RedisStore.new(Redis.new(db: 2))
|
|
17
|
+
config.mcp_enabled = true # Enable MCP for testing
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def teardown
|
|
22
|
+
super
|
|
23
|
+
Coverband.configuration.store&.clear!
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
test "tool has correct metadata" do
|
|
27
|
+
assert_includes Coverband::MCP::Tools::GetUncoveredFiles.description, "coverage below a specified threshold"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
test "input schema has optional parameters" do
|
|
31
|
+
schema = Coverband::MCP::Tools::GetUncoveredFiles.input_schema
|
|
32
|
+
assert_instance_of ::MCP::Tool::InputSchema, schema
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
test "call returns uncovered files below threshold" do
|
|
36
|
+
mock_files = {
|
|
37
|
+
"/app/models/user.rb" => {"covered_percent" => 30.0, "never_loaded" => false},
|
|
38
|
+
"/app/models/order.rb" => {"covered_percent" => 80.0, "never_loaded" => false},
|
|
39
|
+
"/app/helpers/helper.rb" => {"covered_percent" => 20.0, "never_loaded" => false},
|
|
40
|
+
"/app/unused.rb" => {"covered_percent" => 0, "never_loaded" => true}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
mock_data = {"files" => mock_files}
|
|
44
|
+
|
|
45
|
+
report_mock = mock("json_report")
|
|
46
|
+
report_mock.expects(:report).returns(mock_data.to_json)
|
|
47
|
+
Coverband::Reporters::JSONReport.expects(:new).with(
|
|
48
|
+
Coverband.configuration.store,
|
|
49
|
+
line_coverage: false
|
|
50
|
+
).returns(report_mock)
|
|
51
|
+
|
|
52
|
+
response = Coverband::MCP::Tools::GetUncoveredFiles.call(
|
|
53
|
+
threshold: 50,
|
|
54
|
+
server_context: {}
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
assert_instance_of ::MCP::Tool::Response, response
|
|
58
|
+
|
|
59
|
+
result = JSON.parse(response.content.first[:text])
|
|
60
|
+
|
|
61
|
+
# Should include files below 50% and never loaded files
|
|
62
|
+
expected_files = ["/app/helpers/helper.rb", "/app/models/user.rb", "/app/unused.rb"]
|
|
63
|
+
actual_files = result["files"].map { |file| file["file"] }
|
|
64
|
+
|
|
65
|
+
assert_equal 3, result["files"].length
|
|
66
|
+
expected_files.each do |file|
|
|
67
|
+
assert_includes actual_files, file
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Should be sorted by coverage percentage (ascending)
|
|
71
|
+
coverages = result["files"].map { |file| file["covered_percent"] || 0 }
|
|
72
|
+
assert_equal coverages.sort, coverages
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
test "call excludes never loaded files when include_never_loaded is false" do
|
|
76
|
+
mock_files = {
|
|
77
|
+
"/app/models/user.rb" => {"covered_percent" => 30.0, "never_loaded" => false},
|
|
78
|
+
"/app/unused.rb" => {"covered_percent" => 0, "never_loaded" => true}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
mock_data = {"files" => mock_files}
|
|
82
|
+
|
|
83
|
+
report_mock = mock("json_report")
|
|
84
|
+
report_mock.expects(:report).returns(mock_data.to_json)
|
|
85
|
+
Coverband::Reporters::JSONReport.expects(:new).returns(report_mock)
|
|
86
|
+
|
|
87
|
+
response = Coverband::MCP::Tools::GetUncoveredFiles.call(
|
|
88
|
+
threshold: 50,
|
|
89
|
+
include_never_loaded: false,
|
|
90
|
+
server_context: {}
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
result = JSON.parse(response.content.first[:text])
|
|
94
|
+
|
|
95
|
+
# Should only include user.rb (below threshold but not never_loaded)
|
|
96
|
+
assert_equal 1, result["files"].length
|
|
97
|
+
assert_equal "/app/models/user.rb", result["files"].first["file"]
|
|
98
|
+
assert_equal 30.0, result["files"].first["covered_percent"]
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
test "call uses default values when parameters not provided" do
|
|
102
|
+
mock_files = {
|
|
103
|
+
"/app/models/user.rb" => {"covered_percent" => 40.0, "never_loaded" => false},
|
|
104
|
+
"/app/models/order.rb" => {"covered_percent" => 60.0, "never_loaded" => false}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
mock_data = {"files" => mock_files}
|
|
108
|
+
|
|
109
|
+
report_mock = mock("json_report")
|
|
110
|
+
report_mock.expects(:report).returns(mock_data.to_json)
|
|
111
|
+
Coverband::Reporters::JSONReport.expects(:new).returns(report_mock)
|
|
112
|
+
|
|
113
|
+
response = Coverband::MCP::Tools::GetUncoveredFiles.call(server_context: {})
|
|
114
|
+
|
|
115
|
+
result = JSON.parse(response.content.first[:text])
|
|
116
|
+
|
|
117
|
+
# Default threshold is 50, so should only include user.rb (40%)
|
|
118
|
+
assert_equal 1, result["files"].length
|
|
119
|
+
assert_equal "/app/models/user.rb", result["files"].first["file"]
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
test "call handles files with nil covered_percent" do
|
|
123
|
+
mock_files = {
|
|
124
|
+
"/app/models/user.rb" => {"covered_percent" => nil, "never_loaded" => false},
|
|
125
|
+
"/app/models/order.rb" => {"covered_percent" => 60.0, "never_loaded" => false}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
mock_data = {"files" => mock_files}
|
|
129
|
+
|
|
130
|
+
report_mock = mock("json_report")
|
|
131
|
+
report_mock.expects(:report).returns(mock_data.to_json)
|
|
132
|
+
Coverband::Reporters::JSONReport.expects(:new).returns(report_mock)
|
|
133
|
+
|
|
134
|
+
response = Coverband::MCP::Tools::GetUncoveredFiles.call(
|
|
135
|
+
threshold: 50,
|
|
136
|
+
server_context: {}
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
result = JSON.parse(response.content.first[:text])
|
|
140
|
+
|
|
141
|
+
# File with nil coverage should be included (treated as 0)
|
|
142
|
+
assert_equal 1, result["files"].length
|
|
143
|
+
assert_equal "/app/models/user.rb", result["files"].first["file"]
|
|
144
|
+
assert_nil result["files"].first["covered_percent"]
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
test "call returns empty array when no files below threshold" do
|
|
148
|
+
mock_files = {
|
|
149
|
+
"/app/models/user.rb" => {"covered_percent" => 80.0, "never_loaded" => false},
|
|
150
|
+
"/app/models/order.rb" => {"covered_percent" => 90.0, "never_loaded" => false}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
mock_data = {"files" => mock_files}
|
|
154
|
+
|
|
155
|
+
report_mock = mock("json_report")
|
|
156
|
+
report_mock.expects(:report).returns(mock_data.to_json)
|
|
157
|
+
Coverband::Reporters::JSONReport.expects(:new).returns(report_mock)
|
|
158
|
+
|
|
159
|
+
response = Coverband::MCP::Tools::GetUncoveredFiles.call(
|
|
160
|
+
threshold: 50,
|
|
161
|
+
server_context: {}
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
result = JSON.parse(response.content.first[:text])
|
|
165
|
+
assert_equal 0, result["files"].length
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
test "call handles errors gracefully" do
|
|
169
|
+
Coverband::Reporters::JSONReport.expects(:new).raises(StandardError.new("Test error"))
|
|
170
|
+
|
|
171
|
+
response = Coverband::MCP::Tools::GetUncoveredFiles.call(server_context: {})
|
|
172
|
+
|
|
173
|
+
assert_instance_of ::MCP::Tool::Response, response
|
|
174
|
+
assert_includes response.content.first[:text], "Error getting uncovered files: Test error"
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require File.expand_path("../../../test_helper", File.dirname(__FILE__))
|
|
4
|
+
|
|
5
|
+
begin
|
|
6
|
+
require "coverband/mcp"
|
|
7
|
+
rescue LoadError
|
|
8
|
+
puts "MCP gem not available, skipping MCP tools tests"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
if defined?(Coverband::MCP)
|
|
12
|
+
class GetViewTrackerDataTest < Minitest::Test
|
|
13
|
+
def setup
|
|
14
|
+
super
|
|
15
|
+
Coverband.configure do |config|
|
|
16
|
+
config.store = Coverband::Adapters::RedisStore.new(Redis.new(db: 2))
|
|
17
|
+
config.track_views = true
|
|
18
|
+
config.mcp_enabled = true # Enable MCP for testing
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def teardown
|
|
23
|
+
super
|
|
24
|
+
Coverband.configuration.store&.clear!
|
|
25
|
+
Coverband.configuration.track_views = false
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
test "tool has correct metadata" do
|
|
29
|
+
assert_includes Coverband::MCP::Tools::GetViewTrackerData.description, "Rails view template usage"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
test "input schema has optional show_unused_only parameter" do
|
|
33
|
+
schema = Coverband::MCP::Tools::GetViewTrackerData.input_schema
|
|
34
|
+
assert_instance_of ::MCP::Tool::InputSchema, schema
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
test "call returns view tracking data when tracker is enabled" do
|
|
38
|
+
tracker_mock = mock("view_tracker")
|
|
39
|
+
tracker_mock.expects(:tracking_since).returns("2024-01-01")
|
|
40
|
+
tracker_mock.expects(:as_json).returns({
|
|
41
|
+
"used_keys" => ["app/views/users/index.html.erb", "app/views/users/show.html.erb"],
|
|
42
|
+
"unused_keys" => ["app/views/users/new.html.erb", "app/views/users/edit.html.erb"]
|
|
43
|
+
}.to_json)
|
|
44
|
+
|
|
45
|
+
Coverband.configuration.expects(:view_tracker).returns(tracker_mock)
|
|
46
|
+
|
|
47
|
+
response = Coverband::MCP::Tools::GetViewTrackerData.call(server_context: {})
|
|
48
|
+
|
|
49
|
+
assert_instance_of ::MCP::Tool::Response, response
|
|
50
|
+
|
|
51
|
+
result = JSON.parse(response.content.first[:text])
|
|
52
|
+
|
|
53
|
+
assert_equal "2024-01-01", result["tracking_since"]
|
|
54
|
+
assert_equal 2, result["total_used"]
|
|
55
|
+
assert_equal 2, result["total_unused"]
|
|
56
|
+
assert_includes result["used_views"], "app/views/users/index.html.erb"
|
|
57
|
+
assert_includes result["unused_views"], "app/views/users/edit.html.erb"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
test "call returns only unused views when show_unused_only is true" do
|
|
61
|
+
tracker_mock = mock("view_tracker")
|
|
62
|
+
tracker_mock.expects(:tracking_since).returns("2024-01-01")
|
|
63
|
+
tracker_mock.expects(:as_json).returns({
|
|
64
|
+
"unused_keys" => ["app/views/users/edit.html.erb", "app/views/orders/index.html.erb"]
|
|
65
|
+
}.to_json)
|
|
66
|
+
|
|
67
|
+
Coverband.configuration.expects(:view_tracker).returns(tracker_mock)
|
|
68
|
+
|
|
69
|
+
response = Coverband::MCP::Tools::GetViewTrackerData.call(
|
|
70
|
+
show_unused_only: true,
|
|
71
|
+
server_context: {}
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
result = JSON.parse(response.content.first[:text])
|
|
75
|
+
|
|
76
|
+
assert_equal "2024-01-01", result["tracking_since"]
|
|
77
|
+
assert_equal 2, result["total_unused"]
|
|
78
|
+
assert_includes result["unused_views"], "app/views/users/edit.html.erb"
|
|
79
|
+
refute_includes result, "used_views"
|
|
80
|
+
refute_includes result, "total_used"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
test "call returns message when view tracking is not enabled" do
|
|
84
|
+
Coverband.configuration.expects(:view_tracker).returns(nil)
|
|
85
|
+
|
|
86
|
+
response = Coverband::MCP::Tools::GetViewTrackerData.call(server_context: {})
|
|
87
|
+
|
|
88
|
+
assert_instance_of ::MCP::Tool::Response, response
|
|
89
|
+
assert_includes response.content.first[:text], "View tracking is not enabled"
|
|
90
|
+
assert_includes response.content.first[:text], "config.track_views = true"
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
test "call handles empty tracking data gracefully" do
|
|
94
|
+
tracker_mock = mock("view_tracker")
|
|
95
|
+
tracker_mock.expects(:tracking_since).returns("2024-01-01")
|
|
96
|
+
tracker_mock.expects(:as_json).returns({
|
|
97
|
+
"used_keys" => nil,
|
|
98
|
+
"unused_keys" => nil
|
|
99
|
+
}.to_json)
|
|
100
|
+
|
|
101
|
+
Coverband.configuration.expects(:view_tracker).returns(tracker_mock)
|
|
102
|
+
|
|
103
|
+
response = Coverband::MCP::Tools::GetViewTrackerData.call(server_context: {})
|
|
104
|
+
|
|
105
|
+
result = JSON.parse(response.content.first[:text])
|
|
106
|
+
|
|
107
|
+
assert_equal 0, result["total_used"]
|
|
108
|
+
assert_equal 0, result["total_unused"]
|
|
109
|
+
assert_equal [], result["used_views"]
|
|
110
|
+
assert_equal [], result["unused_views"]
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
test "call handles errors gracefully" do
|
|
114
|
+
Coverband.configuration.expects(:view_tracker).raises(StandardError.new("Test error"))
|
|
115
|
+
|
|
116
|
+
response = Coverband::MCP::Tools::GetViewTrackerData.call(server_context: {})
|
|
117
|
+
|
|
118
|
+
assert_instance_of ::MCP::Tool::Response, response
|
|
119
|
+
assert_includes response.content.first[:text], "Error getting view tracker data: Test error"
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
@@ -7,7 +7,7 @@ class TrackKeyTest < Minitest::Test
|
|
|
7
7
|
mock_view_tracker = mock
|
|
8
8
|
mock_view_tracker.expects(:track_key).with("view/path/index.html.erb").returns(true)
|
|
9
9
|
Coverband.configuration.expects(:view_tracker).returns(mock_view_tracker)
|
|
10
|
-
|
|
10
|
+
|
|
11
11
|
assert Coverband.track_key(:view_tracker, "view/path/index.html.erb")
|
|
12
12
|
end
|
|
13
13
|
|
|
@@ -23,7 +23,7 @@ class TrackKeyTest < Minitest::Test
|
|
|
23
23
|
|
|
24
24
|
test "track_key handles missing trackers" do
|
|
25
25
|
Coverband.configuration.expects(:view_tracker).returns(nil)
|
|
26
|
-
|
|
26
|
+
|
|
27
27
|
assert_equal false, Coverband.track_key(:view_tracker, "some_view")
|
|
28
28
|
end
|
|
29
29
|
|
|
@@ -31,7 +31,7 @@ class TrackKeyTest < Minitest::Test
|
|
|
31
31
|
mock_invalid_tracker = mock
|
|
32
32
|
mock_invalid_tracker.expects(:respond_to?).with(:track_key).returns(false)
|
|
33
33
|
Coverband.configuration.expects(:view_tracker).returns(mock_invalid_tracker)
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
assert_equal false, Coverband.track_key(:view_tracker, "some_view")
|
|
36
36
|
end
|
|
37
37
|
|
|
@@ -39,7 +39,7 @@ class TrackKeyTest < Minitest::Test
|
|
|
39
39
|
mock_translations_tracker = mock
|
|
40
40
|
mock_translations_tracker.expects(:track_key).with("translation.key").returns(true)
|
|
41
41
|
Coverband.configuration.expects(:translations_tracker).returns(mock_translations_tracker)
|
|
42
|
-
|
|
42
|
+
|
|
43
43
|
assert Coverband.track_key(:translations_tracker, "translation.key")
|
|
44
44
|
end
|
|
45
45
|
|
|
@@ -47,20 +47,20 @@ class TrackKeyTest < Minitest::Test
|
|
|
47
47
|
mock_routes_tracker = mock
|
|
48
48
|
mock_routes_tracker.expects(:track_key).with("index#show").returns(true)
|
|
49
49
|
Coverband.configuration.expects(:routes_tracker).returns(mock_routes_tracker)
|
|
50
|
-
|
|
50
|
+
|
|
51
51
|
assert Coverband.track_key(:routes_tracker, "index#show")
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
test "track_key logs error when tracking fails" do
|
|
55
55
|
mock_logger = mock
|
|
56
56
|
mock_logger.expects(:error).with(regexp_matches(/Failed to track key/))
|
|
57
|
-
|
|
57
|
+
|
|
58
58
|
mock_tracker = mock
|
|
59
59
|
mock_tracker.expects(:track_key).with("test_key").raises(StandardError.new("Test error"))
|
|
60
|
-
|
|
60
|
+
|
|
61
61
|
Coverband.configuration.expects(:translations_tracker).returns(mock_tracker)
|
|
62
62
|
Coverband.configuration.expects(:logger).returns(mock_logger)
|
|
63
|
-
|
|
63
|
+
|
|
64
64
|
assert_equal false, Coverband.track_key(:translations_tracker, "test_key")
|
|
65
65
|
end
|
|
66
|
-
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require File.expand_path("../test_helper", File.dirname(__FILE__))
|
|
4
|
+
|
|
5
|
+
class TrackerInitializationTest < Minitest::Test
|
|
6
|
+
def setup
|
|
7
|
+
super
|
|
8
|
+
Thread.current[:coverband_instance] = nil
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def teardown
|
|
12
|
+
super
|
|
13
|
+
Thread.current[:coverband_instance] = nil
|
|
14
|
+
Coverband.configure do |config|
|
|
15
|
+
# Reset
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
test "tracker initialization can trigger Redis connection" do
|
|
20
|
+
skip "ViewTracker requires Rails 7+" unless defined?(Rails::VERSION) && Rails::VERSION::STRING.split(".").first.to_i >= 7
|
|
21
|
+
|
|
22
|
+
# Reset everything
|
|
23
|
+
Coverband.configuration.instance_variable_set(:@store, nil)
|
|
24
|
+
Coverband.configuration.instance_variable_set(:@view_tracker, nil)
|
|
25
|
+
|
|
26
|
+
# Mock Redis to see if it gets called when trackers are created
|
|
27
|
+
Redis.expects(:new).raises(Redis::CannotConnectError.new("Connection refused")).once
|
|
28
|
+
|
|
29
|
+
# This could happen during railtie initialization
|
|
30
|
+
# Creating a view tracker when no store is configured yet
|
|
31
|
+
Coverband.configuration.railtie!
|
|
32
|
+
|
|
33
|
+
# Should have fallen back to NullStore due to Redis error
|
|
34
|
+
assert_instance_of Coverband::Adapters::NullStore, Coverband.configuration.store
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
test "tracker initialization after FileStore config works fine" do
|
|
38
|
+
skip "ViewTracker requires Rails 7+" unless defined?(Rails::VERSION) && Rails::VERSION::STRING.split(".").first.to_i >= 7
|
|
39
|
+
|
|
40
|
+
# Configure FileStore first
|
|
41
|
+
Coverband.configure do |config|
|
|
42
|
+
config.store = Coverband::Adapters::FileStore.new("tmp/tracker_test")
|
|
43
|
+
config.track_views = true
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Redis should never be attempted
|
|
47
|
+
Redis.expects(:new).never
|
|
48
|
+
|
|
49
|
+
# Creating trackers should use the configured FileStore
|
|
50
|
+
Coverband.configuration.railtie!
|
|
51
|
+
|
|
52
|
+
assert_instance_of Coverband::Adapters::FileStore, Coverband.configuration.store
|
|
53
|
+
assert_instance_of Coverband::Collectors::ViewTracker, Coverband.configuration.view_tracker
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
test "store configuration prevents Redis connection in tracker contexts" do
|
|
57
|
+
# Test the core issue without Rails dependency
|
|
58
|
+
# Configure FileStore first
|
|
59
|
+
Coverband.configure do |config|
|
|
60
|
+
config.store = Coverband::Adapters::FileStore.new("tmp/abstract_tracker_test")
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Redis should never be attempted
|
|
64
|
+
Redis.expects(:new).never
|
|
65
|
+
|
|
66
|
+
# This tests that the store getter works correctly
|
|
67
|
+
# when called from tracker initialization contexts
|
|
68
|
+
store = Coverband.configuration.store
|
|
69
|
+
assert_instance_of Coverband::Adapters::FileStore, store
|
|
70
|
+
|
|
71
|
+
# Multiple accesses should return the same cached instance
|
|
72
|
+
store2 = Coverband.configuration.store
|
|
73
|
+
assert_same store, store2
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# This test simulates using Coverband without COVERBAND_DISABLE_AUTO_START
|
|
4
|
+
# to see if the issue still occurs
|
|
5
|
+
|
|
6
|
+
require File.expand_path("../test_helper", File.dirname(__FILE__))
|
|
7
|
+
|
|
8
|
+
class UserEnvironmentSimulationTest < Minitest::Test
|
|
9
|
+
def setup
|
|
10
|
+
super
|
|
11
|
+
# Clean slate
|
|
12
|
+
Thread.current[:coverband_instance] = nil
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def teardown
|
|
16
|
+
super
|
|
17
|
+
Thread.current[:coverband_instance] = nil
|
|
18
|
+
# Restore the configured state
|
|
19
|
+
Coverband.configure do |config|
|
|
20
|
+
# Use default config
|
|
21
|
+
end
|
|
22
|
+
ENV.delete("COVERBAND_DISABLE_AUTO_START")
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
test "user workflow with auto-start disabled" do
|
|
26
|
+
# User disables auto-start and configures manually (recommended approach)
|
|
27
|
+
ENV["COVERBAND_DISABLE_AUTO_START"] = "true"
|
|
28
|
+
|
|
29
|
+
begin
|
|
30
|
+
# Reset state
|
|
31
|
+
Coverband.class_variable_set(:@@configured, false)
|
|
32
|
+
Coverband.configuration.instance_variable_set(:@store, nil)
|
|
33
|
+
|
|
34
|
+
# User configuration exactly like the GitHub issue
|
|
35
|
+
Coverband.configure do |config|
|
|
36
|
+
config.root = Dir.pwd
|
|
37
|
+
config.background_reporting_enabled = false
|
|
38
|
+
config.store = Coverband::Adapters::FileStore.new("tmp/coverband_log")
|
|
39
|
+
config.logger = Logger.new($stdout)
|
|
40
|
+
config.verbose = false
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Start manually
|
|
44
|
+
Coverband.start
|
|
45
|
+
|
|
46
|
+
# Should use FileStore without any Redis errors
|
|
47
|
+
assert_instance_of Coverband::Adapters::FileStore, Coverband.configuration.store
|
|
48
|
+
|
|
49
|
+
# These operations should work fine
|
|
50
|
+
Coverband.report_coverage
|
|
51
|
+
ensure
|
|
52
|
+
ENV.delete("COVERBAND_DISABLE_AUTO_START")
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
test "multiple reports with FileStore should never try Redis" do
|
|
57
|
+
Coverband.configure do |config|
|
|
58
|
+
config.root = Dir.pwd
|
|
59
|
+
config.background_reporting_enabled = false
|
|
60
|
+
config.store = Coverband::Adapters::FileStore.new("tmp/test_multiple_reports")
|
|
61
|
+
config.logger = Logger.new($stdout)
|
|
62
|
+
config.verbose = false
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Mock Redis to ensure it's never called after proper configuration
|
|
66
|
+
Redis.expects(:new).never
|
|
67
|
+
|
|
68
|
+
# Multiple reports should work fine
|
|
69
|
+
5.times do
|
|
70
|
+
Coverband.report_coverage
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
assert_instance_of Coverband::Adapters::FileStore, Coverband.configuration.store
|
|
74
|
+
end
|
|
75
|
+
end
|