coverband 5.2.5.rc.1 → 5.2.5.rc.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/main.yml +6 -1
- data/Gemfile +2 -10
- data/{Gemfile.rails4 → Gemfile.rails7} +2 -1
- data/changes.md +8 -0
- data/coverband.gemspec +3 -3
- data/diagram.svg +1 -1
- data/lib/coverband/adapters/hash_redis_store.rb +1 -1
- data/lib/coverband/adapters/redis_store.rb +0 -2
- data/lib/coverband/collectors/route_tracker.rb +175 -0
- data/lib/coverband/configuration.rb +4 -1
- data/lib/coverband/integrations/background.rb +1 -0
- data/lib/coverband/reporters/web.rb +41 -4
- data/lib/coverband/utils/html_formatter.rb +8 -0
- data/lib/coverband/utils/rails6_ext.rb +58 -0
- data/lib/coverband/utils/railtie.rb +20 -0
- data/lib/coverband/version.rb +1 -1
- data/lib/coverband.rb +1 -0
- data/test/benchmarks/benchmark.rake +0 -1
- data/test/coverband/collectors/route_tracker_test.rb +125 -0
- data/test/coverband/collectors/view_tracker_test.rb +1 -1
- data/test/coverband/reporters/web_test.rb +1 -1
- data/test/forked/rails_full_stack_views_test.rb +5 -0
- data/test/forked/rails_route_tracker_stack_test.rb +45 -0
- data/test/forked/rails_view_tracker_stack_test.rb +1 -1
- data/test/rails5_dummy/config/coverband.rb +1 -2
- data/test/rails7_dummy/Rakefile +6 -0
- data/test/rails7_dummy/app/controllers/dummy_controller.rb +5 -0
- data/test/rails7_dummy/app/controllers/dummy_view_controller.rb +16 -0
- data/test/rails7_dummy/app/views/dummy_view/show.html.erb +5 -0
- data/test/rails7_dummy/app/views/dummy_view/show_haml.html.haml +4 -0
- data/test/rails7_dummy/app/views/dummy_view/show_slim.html.slim +4 -0
- data/test/rails7_dummy/config/application.rb +15 -0
- data/test/rails7_dummy/config/boot.rb +3 -0
- data/test/rails7_dummy/config/coverband.rb +3 -0
- data/test/rails7_dummy/config/coverband_missing_redis.rb +3 -0
- data/test/rails7_dummy/config/environment.rb +5 -0
- data/test/rails7_dummy/config/routes.rb +7 -0
- data/test/rails7_dummy/config/secrets.yml +3 -0
- data/test/rails7_dummy/config.ru +4 -0
- data/test/rails7_dummy/tmp/.keep +0 -0
- data/test/test_helper.rb +2 -4
- data/views/nav.erb +7 -2
- data/views/route_tracker.erb +49 -0
- data/views/view_tracker.erb +1 -1
- metadata +41 -5
- data/Gemfile.rails6 +0 -9
@@ -119,7 +119,7 @@ module Coverband
|
|
119
119
|
|
120
120
|
data = coverage_data_from_redis(data_from_redis)
|
121
121
|
hash[file] = data_from_redis.select { |meta_data_key, _value| META_DATA_KEYS.include?(meta_data_key) }.merge!("data" => data)
|
122
|
-
hash[file][LAST_UPDATED_KEY] =
|
122
|
+
hash[file][LAST_UPDATED_KEY] = hash[file][LAST_UPDATED_KEY].nil? || hash[file][LAST_UPDATED_KEY] == "" ? nil : hash[file][LAST_UPDATED_KEY].to_i
|
123
123
|
hash[file].merge!(LAST_UPDATED_KEY => hash[file][LAST_UPDATED_KEY], FIRST_UPDATED_KEY => hash[file][FIRST_UPDATED_KEY].to_i)
|
124
124
|
end
|
125
125
|
|
@@ -0,0 +1,175 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "set"
|
4
|
+
require "singleton"
|
5
|
+
|
6
|
+
module Coverband
|
7
|
+
module Collectors
|
8
|
+
###
|
9
|
+
# This class tracks route usage via ActiveSupport::Notifications
|
10
|
+
###
|
11
|
+
class RouteTracker
|
12
|
+
attr_accessor :target
|
13
|
+
attr_reader :logger, :store, :ignore_patterns
|
14
|
+
|
15
|
+
def initialize(options = {})
|
16
|
+
raise NotImplementedError, "Route Tracker requires Rails 4 or greater" unless self.class.supported_version?
|
17
|
+
raise "Coverband: route tracker initialized before configuration!" if !Coverband.configured? && ENV["COVERBAND_TEST"] == "test"
|
18
|
+
|
19
|
+
@ignore_patterns = Coverband.configuration.ignore
|
20
|
+
@store = options.fetch(:store) { Coverband.configuration.store }
|
21
|
+
@logger = options.fetch(:logger) { Coverband.configuration.logger }
|
22
|
+
@target = options.fetch(:target) do
|
23
|
+
if defined?(Rails.application)
|
24
|
+
Rails.application.routes.routes.map do |route|
|
25
|
+
{
|
26
|
+
controller: route.defaults[:controller],
|
27
|
+
action: route.defaults[:action],
|
28
|
+
url_path: route.path.spec.to_s.gsub("(.:format)", ""),
|
29
|
+
verb: route.verb
|
30
|
+
}.to_s
|
31
|
+
end
|
32
|
+
else
|
33
|
+
[]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
@one_time_timestamp = false
|
38
|
+
|
39
|
+
@logged_routes = Set.new
|
40
|
+
@routes_to_record = Set.new
|
41
|
+
end
|
42
|
+
|
43
|
+
def logged_routes
|
44
|
+
@logged_routes.to_a
|
45
|
+
end
|
46
|
+
|
47
|
+
def routes_to_record
|
48
|
+
@routes_to_record.to_a
|
49
|
+
end
|
50
|
+
|
51
|
+
###
|
52
|
+
# This method is called on every routing call, so we try to reduce method calls
|
53
|
+
# and ensure high performance
|
54
|
+
###
|
55
|
+
def track_routes(_name, _start, _finish, _id, payload)
|
56
|
+
route = if payload[:request]
|
57
|
+
{
|
58
|
+
controller: nil,
|
59
|
+
action: nil,
|
60
|
+
url_path: payload[:request].path,
|
61
|
+
verb: payload[:request].method
|
62
|
+
}
|
63
|
+
else
|
64
|
+
{
|
65
|
+
controller: payload[:controller],
|
66
|
+
action: payload[:action],
|
67
|
+
url_path: payload[:path],
|
68
|
+
verb: payload[:method]
|
69
|
+
}
|
70
|
+
end
|
71
|
+
if route
|
72
|
+
if newly_seen_route?(route)
|
73
|
+
@logged_routes << route
|
74
|
+
@routes_to_record << route if track_route?(route)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def used_routes
|
80
|
+
redis_store.hgetall(tracker_key)
|
81
|
+
end
|
82
|
+
|
83
|
+
def all_routes
|
84
|
+
all_routes = []
|
85
|
+
target.each do |route|
|
86
|
+
all_routes << route
|
87
|
+
end
|
88
|
+
all_routes.uniq
|
89
|
+
end
|
90
|
+
|
91
|
+
def unused_routes(used_routes = nil)
|
92
|
+
recently_used_routes = (used_routes || self.used_routes).keys
|
93
|
+
all_routes - recently_used_routes
|
94
|
+
end
|
95
|
+
|
96
|
+
def as_json
|
97
|
+
used_routes = self.used_routes
|
98
|
+
{
|
99
|
+
unused_routes: unused_routes(used_routes),
|
100
|
+
used_routes: used_routes
|
101
|
+
}.to_json
|
102
|
+
end
|
103
|
+
|
104
|
+
def tracking_since
|
105
|
+
if (tracking_time = redis_store.get(tracker_time_key))
|
106
|
+
Time.at(tracking_time.to_i).iso8601
|
107
|
+
else
|
108
|
+
"N/A"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def reset_recordings
|
113
|
+
redis_store.del(tracker_key)
|
114
|
+
redis_store.del(tracker_time_key)
|
115
|
+
end
|
116
|
+
|
117
|
+
def clear_route!(route)
|
118
|
+
return unless route
|
119
|
+
|
120
|
+
redis_store.hdel(tracker_key, route)
|
121
|
+
@logged_routes.delete(route)
|
122
|
+
end
|
123
|
+
|
124
|
+
def report_routes_tracked
|
125
|
+
redis_store.set(tracker_time_key, Time.now.to_i) unless @one_time_timestamp || tracker_time_key_exists?
|
126
|
+
@one_time_timestamp = true
|
127
|
+
reported_time = Time.now.to_i
|
128
|
+
@routes_to_record.to_a.each do |route|
|
129
|
+
redis_store.hset(tracker_key, route.to_s, reported_time)
|
130
|
+
end
|
131
|
+
@routes_to_record.clear
|
132
|
+
rescue => e
|
133
|
+
# we don't want to raise errors if Coverband can't reach redis.
|
134
|
+
# This is a nice to have not a bring the system down
|
135
|
+
logger&.error "Coverband: route_tracker failed to store, error #{e.class.name} info #{e.message}"
|
136
|
+
end
|
137
|
+
|
138
|
+
def self.supported_version?
|
139
|
+
defined?(Rails) && defined?(Rails::VERSION) && Rails::VERSION::STRING.split(".").first.to_i >= 4
|
140
|
+
end
|
141
|
+
|
142
|
+
protected
|
143
|
+
|
144
|
+
def newly_seen_route?(route)
|
145
|
+
!@logged_routes.include?(route)
|
146
|
+
end
|
147
|
+
|
148
|
+
def track_route?(route, options = {})
|
149
|
+
@ignore_patterns.none? { |pattern| route.to_s.include?(pattern) }
|
150
|
+
end
|
151
|
+
|
152
|
+
private
|
153
|
+
|
154
|
+
def redis_store
|
155
|
+
store.raw_store
|
156
|
+
end
|
157
|
+
|
158
|
+
def tracker_time_key_exists?
|
159
|
+
if defined?(redis_store.exists?)
|
160
|
+
redis_store.exists?(tracker_time_key)
|
161
|
+
else
|
162
|
+
redis_store.exists(tracker_time_key)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def tracker_key
|
167
|
+
"route_tracker_2"
|
168
|
+
end
|
169
|
+
|
170
|
+
def tracker_time_key
|
171
|
+
"route_tracker_time"
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
@@ -8,7 +8,8 @@ module Coverband
|
|
8
8
|
:background_reporting_enabled,
|
9
9
|
:test_env, :web_enable_clear, :gem_details, :web_debug, :report_on_exit,
|
10
10
|
:simulate_oneshot_lines_coverage,
|
11
|
-
:view_tracker, :defer_eager_loading_data
|
11
|
+
:view_tracker, :defer_eager_loading_data,
|
12
|
+
:track_routes, :route_tracker
|
12
13
|
attr_writer :logger, :s3_region, :s3_bucket, :s3_access_key_id,
|
13
14
|
:s3_secret_access_key, :password, :api_key, :service_url, :coverband_timeout, :service_dev_mode,
|
14
15
|
:service_test_mode, :process_type, :track_views, :redis_url,
|
@@ -67,6 +68,8 @@ module Coverband
|
|
67
68
|
@web_enable_clear = false
|
68
69
|
@track_views = true
|
69
70
|
@view_tracker = nil
|
71
|
+
@track_routes = false
|
72
|
+
@route_tracker = nil
|
70
73
|
@web_debug = false
|
71
74
|
@report_on_exit = true
|
72
75
|
@use_oneshot_lines_coverage = ENV["ONESHOT"] || false
|
@@ -39,6 +39,7 @@ module Coverband
|
|
39
39
|
sleep(sleep_seconds.to_i) if Coverband.configuration.defer_eager_loading_data?
|
40
40
|
Coverband.report_coverage
|
41
41
|
Coverband.configuration.view_tracker&.report_views_tracked
|
42
|
+
Coverband.configuration.route_tracker&.report_routes_tracked
|
42
43
|
if Coverband.configuration.verbose
|
43
44
|
logger.debug("Coverband: background reporting coverage (#{Coverband.configuration.store.type}). Sleeping #{sleep_seconds}s")
|
44
45
|
end
|
@@ -38,6 +38,10 @@ module Coverband
|
|
38
38
|
request_path_info = request.path_info == "" ? "/" : request.path_info
|
39
39
|
if request.post?
|
40
40
|
case request_path_info
|
41
|
+
when %r{\/clear_route_tracking_route}
|
42
|
+
clear_route_tracking_route
|
43
|
+
when %r{\/clear_route_tracking}
|
44
|
+
clear_route_tracking
|
41
45
|
when %r{\/clear_view_tracking_file}
|
42
46
|
clear_view_tracking_file
|
43
47
|
when %r{\/clear_view_tracking}
|
@@ -59,6 +63,8 @@ module Coverband
|
|
59
63
|
[200, {"Content-Type" => "text/json"}, [view_tracker_data]]
|
60
64
|
when %r{\/view_tracker}
|
61
65
|
[200, {"Content-Type" => "text/html"}, [view_tracker]]
|
66
|
+
when %r{\/route_tracker}
|
67
|
+
[200, {"Content-Type" => "text/html"}, [route_tracker]]
|
62
68
|
when %r{\/enriched_debug_data}
|
63
69
|
[200, {"Content-Type" => "text/json"}, [enriched_debug_data]]
|
64
70
|
when %r{\/debug_data}
|
@@ -95,6 +101,14 @@ module Coverband
|
|
95
101
|
base_path: base_path).format_view_tracker!
|
96
102
|
end
|
97
103
|
|
104
|
+
def route_tracker
|
105
|
+
notice = "<strong>Notice:</strong> #{Rack::Utils.escape_html(request.params["notice"])}<br/>"
|
106
|
+
notice = request.params["notice"] ? notice : ""
|
107
|
+
Coverband::Utils::HTMLFormatter.new(nil,
|
108
|
+
notice: notice,
|
109
|
+
base_path: base_path).format_route_tracker!
|
110
|
+
end
|
111
|
+
|
98
112
|
def view_tracker_data
|
99
113
|
Coverband::Collectors::ViewTracker.new(store: Coverband.configuration.store).as_json
|
100
114
|
end
|
@@ -126,7 +140,7 @@ module Coverband
|
|
126
140
|
else
|
127
141
|
notice = "web_enable_clear isn't enabled in your configuration"
|
128
142
|
end
|
129
|
-
[
|
143
|
+
[302, {"Location" => "#{base_path}?notice=#{notice}"}, []]
|
130
144
|
end
|
131
145
|
|
132
146
|
def clear_file
|
@@ -137,7 +151,7 @@ module Coverband
|
|
137
151
|
else
|
138
152
|
notice = "web_enable_clear isn't enabled in your configuration"
|
139
153
|
end
|
140
|
-
[
|
154
|
+
[302, {"Location" => "#{base_path}?notice=#{notice}"}, []]
|
141
155
|
end
|
142
156
|
|
143
157
|
def clear_view_tracking
|
@@ -148,7 +162,7 @@ module Coverband
|
|
148
162
|
else
|
149
163
|
notice = "web_enable_clear isn't enabled in your configuration"
|
150
164
|
end
|
151
|
-
[
|
165
|
+
[302, {"Location" => "#{base_path}/view_tracker?notice=#{notice}"}, []]
|
152
166
|
end
|
153
167
|
|
154
168
|
def clear_view_tracking_file
|
@@ -160,7 +174,30 @@ module Coverband
|
|
160
174
|
else
|
161
175
|
notice = "web_enable_clear isn't enabled in your configuration"
|
162
176
|
end
|
163
|
-
[
|
177
|
+
[302, {"Location" => "#{base_path}/view_tracker?notice=#{notice}"}, []]
|
178
|
+
end
|
179
|
+
|
180
|
+
def clear_route_tracking
|
181
|
+
if Coverband.configuration.web_enable_clear
|
182
|
+
tracker = Coverband::Collectors::RouteTracker.new(store: Coverband.configuration.store)
|
183
|
+
tracker.reset_recordings
|
184
|
+
notice = "route tracking reset"
|
185
|
+
else
|
186
|
+
notice = "web_enable_clear isn't enabled in your configuration"
|
187
|
+
end
|
188
|
+
[302, {"Location" => "#{base_path}/route_tracker?notice=#{notice}"}, []]
|
189
|
+
end
|
190
|
+
|
191
|
+
def clear_route_tracking_route
|
192
|
+
if Coverband.configuration.web_enable_clear
|
193
|
+
tracker = Coverband::Collectors::RouteTracker.new(store: Coverband.configuration.store)
|
194
|
+
route = request.params["route"]
|
195
|
+
tracker.clear_route!(route)
|
196
|
+
notice = "coverage for route #{route} cleared"
|
197
|
+
else
|
198
|
+
notice = "web_enable_clear isn't enabled in your configuration"
|
199
|
+
end
|
200
|
+
[302, {"Location" => "#{base_path}/route_tracker?notice=#{notice}"}, []]
|
164
201
|
end
|
165
202
|
|
166
203
|
private
|
@@ -37,6 +37,10 @@ module Coverband
|
|
37
37
|
format_view_tracker
|
38
38
|
end
|
39
39
|
|
40
|
+
def format_route_tracker!
|
41
|
+
format_route_tracker
|
42
|
+
end
|
43
|
+
|
40
44
|
def format_source_file!(filename)
|
41
45
|
source_file = @coverage_result.file_from_path_with_type(filename)
|
42
46
|
|
@@ -57,6 +61,10 @@ module Coverband
|
|
57
61
|
template("view_tracker").result(binding)
|
58
62
|
end
|
59
63
|
|
64
|
+
def format_route_tracker
|
65
|
+
template("route_tracker").result(binding)
|
66
|
+
end
|
67
|
+
|
60
68
|
def format(result)
|
61
69
|
Dir[File.join(File.dirname(__FILE__), "../../../public/*")].each do |path|
|
62
70
|
FileUtils.cp_r(path, asset_output_path)
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
###
|
4
|
+
# This backports routing redirect active notification events to Rails 6
|
5
|
+
#
|
6
|
+
# * reproducing this event: https://github.com/rails/rails/pull/43755/files
|
7
|
+
# * and pulls in the later: https://github.com/rails/rails/commit/40dc22f715ede12ab9b7e06d59fae185da2c38c7
|
8
|
+
# * using alias method although prepend might be an interesting alternative
|
9
|
+
# * this doesn't backport the built in listener for the event (ActionDispatch::LogSubscriber) as logging isn't needed
|
10
|
+
###
|
11
|
+
require "action_dispatch/routing/redirection"
|
12
|
+
|
13
|
+
module ActionDispatch
|
14
|
+
module Routing
|
15
|
+
class Redirect < Endpoint
|
16
|
+
def call(env)
|
17
|
+
ActiveSupport::Notifications.instrument("redirect.action_dispatch") do |payload|
|
18
|
+
request = Request.new(env)
|
19
|
+
response = build_response(request)
|
20
|
+
|
21
|
+
payload[:status] = @status
|
22
|
+
payload[:location] = response.headers["Location"]
|
23
|
+
payload[:request] = request
|
24
|
+
|
25
|
+
response.to_a
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def build_response(req)
|
30
|
+
uri = URI.parse(path(req.path_parameters, req))
|
31
|
+
|
32
|
+
unless uri.host
|
33
|
+
if relative_path?(uri.path)
|
34
|
+
uri.path = "#{req.script_name}/#{uri.path}"
|
35
|
+
elsif uri.path.empty?
|
36
|
+
uri.path = req.script_name.empty? ? "/" : req.script_name
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
uri.scheme ||= req.scheme
|
41
|
+
uri.host ||= req.host
|
42
|
+
uri.port ||= req.port unless req.standard_port?
|
43
|
+
|
44
|
+
req.commit_flash
|
45
|
+
|
46
|
+
body = ""
|
47
|
+
|
48
|
+
headers = {
|
49
|
+
"Location" => uri.to_s,
|
50
|
+
"Content-Type" => "text/html",
|
51
|
+
"Content-Length" => body.length.to_s
|
52
|
+
}
|
53
|
+
|
54
|
+
ActionDispatch::Response.new(status, headers, body)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -28,6 +28,26 @@ module Coverband
|
|
28
28
|
end
|
29
29
|
|
30
30
|
begin
|
31
|
+
if Coverband.configuration.track_routes
|
32
|
+
if Gem::Version.new(Rails.version) >= Gem::Version.new("6.0.0") && Gem::Version.new(Rails.version) < Gem::Version.new("7.1.0")
|
33
|
+
require_relative "rails6_ext"
|
34
|
+
end
|
35
|
+
|
36
|
+
Coverband.configuration.route_tracker = Coverband::Collectors::RouteTracker.new
|
37
|
+
|
38
|
+
ActiveSupport::Notifications.subscribe("start_processing.action_controller") do |name, start, finish, id, payload|
|
39
|
+
Coverband.configuration.route_tracker.track_routes(name, start, finish, id, payload)
|
40
|
+
end
|
41
|
+
|
42
|
+
# NOTE: This event was instrumented in Aug 10th 2022, but didn't make the 7.0.4 release and should be in the next release
|
43
|
+
# https://github.com/rails/rails/pull/43755
|
44
|
+
# Automatic tracking of redirects isn't avaible before Rails 7.1.0 (currently tested against the 7.1.0.alpha)
|
45
|
+
# We could consider back porting or patching a solution that works on previous Rails versions
|
46
|
+
ActiveSupport::Notifications.subscribe("redirect.action_dispatch") do |name, start, finish, id, payload|
|
47
|
+
Coverband.configuration.route_tracker.track_routes(name, start, finish, id, payload)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
31
51
|
if Coverband.configuration.track_views
|
32
52
|
COVERBAND_VIEW_TRACKER = if Coverband.coverband_service?
|
33
53
|
Coverband::Collectors::ViewTrackerService.new
|
data/lib/coverband/version.rb
CHANGED
data/lib/coverband.rb
CHANGED
@@ -18,6 +18,7 @@ require "coverband/utils/file_hasher"
|
|
18
18
|
require "coverband/collectors/coverage"
|
19
19
|
require "coverband/collectors/view_tracker"
|
20
20
|
require "coverband/collectors/view_tracker_service"
|
21
|
+
require "coverband/collectors/route_tracker"
|
21
22
|
require "coverband/reporters/base"
|
22
23
|
require "coverband/reporters/console_report"
|
23
24
|
require "coverband/integrations/background"
|
@@ -0,0 +1,125 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require File.expand_path("../../test_helper", File.dirname(__FILE__))
|
4
|
+
require "ostruct"
|
5
|
+
|
6
|
+
class RouterTrackerTest < Minitest::Test
|
7
|
+
# NOTE: using struct vs open struct as open struct has a special keyword method that overshadows the method value on Ruby 2.x
|
8
|
+
Payload = Struct.new(:path, :method)
|
9
|
+
|
10
|
+
def tracker_key
|
11
|
+
Coverband::Collectors::RouteTracker.expects(:supported_version?).at_least_once.returns(true)
|
12
|
+
Coverband::Collectors::RouteTracker.new.send(:tracker_key)
|
13
|
+
end
|
14
|
+
|
15
|
+
def setup
|
16
|
+
super
|
17
|
+
fake_store.raw_store.del(tracker_key)
|
18
|
+
end
|
19
|
+
|
20
|
+
test "init correctly" do
|
21
|
+
Coverband::Collectors::RouteTracker.expects(:supported_version?).returns(true)
|
22
|
+
tracker = Coverband::Collectors::RouteTracker.new(store: fake_store, roots: "dir")
|
23
|
+
assert_equal nil, tracker.target.first
|
24
|
+
assert !tracker.store.nil?
|
25
|
+
assert_equal [], tracker.target
|
26
|
+
assert_equal [], tracker.logged_routes
|
27
|
+
end
|
28
|
+
|
29
|
+
test "track redirect routes" do
|
30
|
+
store = fake_store
|
31
|
+
route_hash = {controller: nil, action: nil, url_path: "path", verb: "GET"}
|
32
|
+
store.raw_store.expects(:hset).with(tracker_key, route_hash.to_s, anything)
|
33
|
+
tracker = Coverband::Collectors::RouteTracker.new(store: store, roots: "dir")
|
34
|
+
|
35
|
+
payload = {
|
36
|
+
request: Payload.new("path", "GET")
|
37
|
+
}
|
38
|
+
tracker.track_routes("name", "start", "finish", "id", payload)
|
39
|
+
tracker.report_routes_tracked
|
40
|
+
assert_equal [route_hash], tracker.logged_routes
|
41
|
+
end
|
42
|
+
|
43
|
+
test "track controller routes" do
|
44
|
+
store = fake_store
|
45
|
+
route_hash = {controller: "SomeController", action: "index", url_path: "path", verb: "GET"}
|
46
|
+
store.raw_store.expects(:hset).with(tracker_key, route_hash.to_s, anything)
|
47
|
+
tracker = Coverband::Collectors::RouteTracker.new(store: store, roots: "dir")
|
48
|
+
payload = {
|
49
|
+
controller: "SomeController",
|
50
|
+
action: "index",
|
51
|
+
path: "path",
|
52
|
+
method: "GET"
|
53
|
+
}
|
54
|
+
tracker.track_routes("name", "start", "finish", "id", payload)
|
55
|
+
tracker.report_routes_tracked
|
56
|
+
assert_equal [route_hash], tracker.logged_routes
|
57
|
+
end
|
58
|
+
|
59
|
+
test "report used routes" do
|
60
|
+
store = fake_store
|
61
|
+
route_hash = {controller: "SomeController", action: "index", url_path: "path", verb: "GET"}
|
62
|
+
tracker = Coverband::Collectors::RouteTracker.new(store: store, roots: "dir")
|
63
|
+
payload = {
|
64
|
+
controller: "SomeController",
|
65
|
+
action: "index",
|
66
|
+
path: "path",
|
67
|
+
method: "GET"
|
68
|
+
}
|
69
|
+
tracker.track_routes("name", "start", "finish", "id", payload)
|
70
|
+
tracker.report_routes_tracked
|
71
|
+
assert_equal [route_hash.to_s], tracker.used_routes.keys
|
72
|
+
end
|
73
|
+
|
74
|
+
test "report unused routes" do
|
75
|
+
store = fake_store
|
76
|
+
tracker = Coverband::Collectors::RouteTracker.new(store: store, roots: "dir")
|
77
|
+
payload = {
|
78
|
+
controller: "SomeController",
|
79
|
+
action: "index",
|
80
|
+
path: "path",
|
81
|
+
method: "GET"
|
82
|
+
}
|
83
|
+
tracker.track_routes("name", "start", "finish", "id", payload)
|
84
|
+
tracker.report_routes_tracked
|
85
|
+
assert_equal [], tracker.unused_routes
|
86
|
+
end
|
87
|
+
|
88
|
+
test "reset store" do
|
89
|
+
store = fake_store
|
90
|
+
payload = {
|
91
|
+
controller: "SomeController",
|
92
|
+
action: "index",
|
93
|
+
path: "path",
|
94
|
+
method: "GET"
|
95
|
+
}
|
96
|
+
store.raw_store.expects(:del).with(tracker_key)
|
97
|
+
store.raw_store.expects(:del).with("route_tracker_time")
|
98
|
+
tracker = Coverband::Collectors::RouteTracker.new(store: store, roots: "dir")
|
99
|
+
tracker.track_routes("name", "start", "finish", "id", payload)
|
100
|
+
tracker.reset_recordings
|
101
|
+
end
|
102
|
+
|
103
|
+
test "clear_file" do
|
104
|
+
store = fake_store
|
105
|
+
route_hash = {controller: "SomeController", action: "index", url_path: "path", verb: "GET"}
|
106
|
+
tracker = Coverband::Collectors::RouteTracker.new(store: store, roots: "dir")
|
107
|
+
payload = {
|
108
|
+
controller: "SomeController",
|
109
|
+
action: "index",
|
110
|
+
path: "path",
|
111
|
+
method: "GET"
|
112
|
+
}
|
113
|
+
tracker.track_routes("name", "start", "finish", "id", payload)
|
114
|
+
tracker.report_routes_tracked
|
115
|
+
assert_equal [route_hash.to_s], tracker.used_routes.keys
|
116
|
+
tracker.clear_route!(route_hash.to_s)
|
117
|
+
assert_equal [], tracker.store.raw_store.hgetall(tracker_key).keys
|
118
|
+
end
|
119
|
+
|
120
|
+
protected
|
121
|
+
|
122
|
+
def fake_store
|
123
|
+
@fake_store ||= Coverband::Adapters::RedisStore.new(Coverband::Test.redis, redis_namespace: "coverband_test")
|
124
|
+
end
|
125
|
+
end
|
@@ -23,6 +23,7 @@ class RailsFullStackTest < Minitest::Test
|
|
23
23
|
assert_content("I am no dummy view tracker text")
|
24
24
|
Coverband.report_coverage
|
25
25
|
Coverband.configuration.view_tracker&.report_views_tracked
|
26
|
+
Coverband.configuration.route_tracker&.report_routes_tracked
|
26
27
|
visit "/coverage/view_tracker"
|
27
28
|
assert_content("Used Views: (1)")
|
28
29
|
assert_content("Unused Views: (2)")
|
@@ -30,6 +31,10 @@ class RailsFullStackTest < Minitest::Test
|
|
30
31
|
assert_selector("li.unused-views", text: "dummy_view/show_haml.html.haml")
|
31
32
|
assert_selector("li.unused-views", text: "dummy_view/show_slim.html.slim")
|
32
33
|
|
34
|
+
visit "/coverage/route_tracker"
|
35
|
+
assert_content("Used Routes: (1)")
|
36
|
+
assert_content("Unused Routes: (5)")
|
37
|
+
|
33
38
|
visit "/dummy_view/show_haml"
|
34
39
|
assert_content("I am haml text")
|
35
40
|
Coverband.report_coverage
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require File.expand_path("../rails_test_helper", File.dirname(__FILE__))
|
4
|
+
|
5
|
+
class RailsRoutesWithoutConfigStackTest < Minitest::Test
|
6
|
+
def setup
|
7
|
+
super
|
8
|
+
setup_server
|
9
|
+
end
|
10
|
+
|
11
|
+
def teardown
|
12
|
+
super
|
13
|
+
shutdown_server
|
14
|
+
end
|
15
|
+
|
16
|
+
test "check route tracker" do
|
17
|
+
output = `sleep 7 && curl http://localhost:9999/dummy_view/show`
|
18
|
+
assert output.match(/rendered view/)
|
19
|
+
assert output.match(/I am no dummy view tracker text/)
|
20
|
+
output = `sleep 2 && curl http://localhost:9999/coverage/route_tracker`
|
21
|
+
assert output.match(/Used Routes: \(1\)/)
|
22
|
+
assert output.match(/dummy_view\/show/)
|
23
|
+
assert output.match(/GET/)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
# NOTE: We aren't leveraging Capybara because it loads all of our other test helpers and such,
|
29
|
+
# which in turn Configures coverband making it impossible to test the configuration error
|
30
|
+
def setup_server
|
31
|
+
ENV["RAILS_ENV"] = "test"
|
32
|
+
require "rails"
|
33
|
+
fork do
|
34
|
+
exec "cd test/rails#{Rails::VERSION::MAJOR}_dummy && COVERBAND_TEST=test bundle exec rackup config.ru -p 9999 --pid /tmp/testrack.pid"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def shutdown_server
|
39
|
+
if File.exist?("/tmp/testrack.pid")
|
40
|
+
pid = `cat /tmp/testrack.pid`&.strip&.to_i
|
41
|
+
Process.kill("HUP", pid)
|
42
|
+
sleep 1
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -17,7 +17,7 @@ class RailsWithoutConfigStackTest < Minitest::Test
|
|
17
17
|
output = `sleep 7 && curl http://localhost:9999/dummy_view/show`
|
18
18
|
assert output.match(/rendered view/)
|
19
19
|
assert output.match(/I am no dummy view tracker text/)
|
20
|
-
output = `sleep
|
20
|
+
output = `sleep 2 && curl http://localhost:9999/coverage/view_tracker`
|
21
21
|
assert output.match(/Used Views: \(1\)/)
|
22
22
|
assert output.match(/dummy_view\/show/)
|
23
23
|
end
|