Skip to content

Commit 9c05564

Browse files
committed
test: expand rspec coverage across all modules
1 parent 47a3b03 commit 9c05564

File tree

121 files changed

+5095
-611
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

121 files changed

+5095
-611
lines changed

app/controllers/api/v1/monitoring_controller.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ class MonitoringController < BaseController
2828
# Names must match self.class.name inside each job (after Zeitwerk namespace
2929
# resolution) because record_job_heartbeat uses "prostaff:job_heartbeat:#{name}".
3030
SCHEDULED_JOBS = [
31-
{ name: 'Analytics::RefreshMetadataViewsJob', interval_hours: 2, alert_after_hours: 3 },
32-
{ name: 'Authentication::CleanupExpiredTokensJob', interval_hours: 24, alert_after_hours: 25 }
31+
{ name: 'Analytics::RefreshMetadataViewsJob', interval_hours: 2, alert_after_hours: 3 },
32+
{ name: 'Authentication::CleanupExpiredTokensJob', interval_hours: 24, alert_after_hours: 25 }
3333
].freeze
3434

3535
# GET /api/v1/monitoring/sidekiq

app/modules/analytics/controllers/competitive_player_controller.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ module Controllers
2323
class CompetitivePlayerController < Api::V1::BaseController
2424
def player_stats
2525
summoner_name = params[:summoner_name].to_s.strip
26-
return render_error('summoner_name is required', :bad_request) if summoner_name.blank?
26+
return render_error(message: 'summoner_name is required', status: :bad_request) if summoner_name.blank?
2727

2828
matches = scoped_matches(summoner_name)
2929
return render_success(empty_response(summoner_name)) if matches.empty?

app/modules/competitive/jobs/historical_backfill_job.rb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def perform
4949

5050
# Step 1: Trigger the backfill on the scraper (returns immediately).
5151
Rails.logger.info(
52-
"[HistoricalBackfillJob] Triggering backfill on scraper: " \
52+
'[HistoricalBackfillJob] Triggering backfill on scraper: ' \
5353
"league=#{league} min_year=#{min_year}"
5454
)
5555

@@ -64,7 +64,7 @@ def perform
6464
rescue ProStaffScraperService::ScraperError => e
6565
Rails.logger.warn(
6666
"[HistoricalBackfillJob] Scraper trigger failed: #{e.message}. " \
67-
"Proceeding to sync step (scraper may already be running)."
67+
'Proceeding to sync step (scraper may already be running).'
6868
)
6969
end
7070

@@ -98,7 +98,7 @@ def perform
9898
)
9999

100100
# If nothing is pending/in-progress, the backfill is done.
101-
break if remaining == 0
101+
break if remaining.zero?
102102
rescue ProStaffScraperService::ScraperError => e
103103
Rails.logger.warn(
104104
"[HistoricalBackfillJob] Status poll failed: #{e.message}"
@@ -110,7 +110,7 @@ def perform
110110

111111
# Step 3: Sync matches from ES into Rails DB for all organizations.
112112
Rails.logger.info(
113-
"[HistoricalBackfillJob] Starting sync step: " \
113+
'[HistoricalBackfillJob] Starting sync step: ' \
114114
"league=#{league} our_team=#{our_team} limit=#{sync_limit}"
115115
)
116116

@@ -128,7 +128,7 @@ def perform
128128

129129
record_job_heartbeat
130130

131-
Rails.logger.info("[HistoricalBackfillJob] Done.")
131+
Rails.logger.info('[HistoricalBackfillJob] Done.')
132132
end
133133
end
134134
end

app/modules/competitive/jobs/sync_scraper_matches_job.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ def perform(organization_id, league:, our_team: nil, limit: 100)
6868
"totals=#{totals.inspect}"
6969
)
7070

71+
AiIntelligence::RebuildChampionMatrixJob.perform_later if totals[:imported].positive?
72+
7173
totals
7274
rescue ActiveRecord::RecordNotFound => e
7375
Rails.logger.error "[SyncScraperMatchesJob] Organization #{organization_id} not found: #{e.message}"

app/modules/competitive/services/draft_comparator_service.rb

Lines changed: 42 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -145,37 +145,9 @@ def meta_analysis(role:, patch:)
145145
# @param patch [String] Patch version
146146
# @return [Array<Hash>] Suggested counters with winrate
147147
def suggest_counters(opponent_pick:, role:, patch:)
148-
# Find matches where opponent_pick was played
149-
matches = CompetitiveMatch.recent(30)
150-
matches = matches.by_patch(patch) if patch.present?
151-
152-
counters = Hash.new { |h, k| h[k] = { wins: 0, total: 0 } }
153-
154-
matches.each do |match|
155-
# Check if opponent picked this champion in this role
156-
opponent_champion = match.opponent_picks.find do |p|
157-
p['champion'] == opponent_pick && p['role']&.downcase == role.downcase
158-
end
159-
160-
next unless opponent_champion
161-
162-
# Find what was picked against it in same role
163-
our_champion = match.our_picks.find { |p| p['role']&.downcase == role.downcase }
164-
next unless our_champion && our_champion['champion']
165-
166-
counter_name = our_champion['champion']
167-
counters[counter_name][:total] += 1
168-
counters[counter_name][:wins] += 1 if match.victory?
169-
end
170-
171-
# Calculate winrates and sort
172-
counters.map do |champion, stats|
173-
{
174-
champion: champion,
175-
games: stats[:total],
176-
winrate: ((stats[:wins].to_f / stats[:total]) * 100).round(2)
177-
}
178-
end.sort_by { |c| -c[:winrate] }.first(5)
148+
matches = fetch_matches_for_patch(patch)
149+
counters = build_counters_hash(matches, opponent_pick: opponent_pick, role: role)
150+
format_counters(counters)
179151
end
180152

181153
private
@@ -203,4 +175,43 @@ def extract_picks_and_bans(matches, role)
203175

204176
[picks, bans]
205177
end
178+
179+
def fetch_matches_for_patch(patch)
180+
matches = CompetitiveMatch.recent(30)
181+
patch.present? ? matches.by_patch(patch) : matches
182+
end
183+
184+
def build_counters_hash(matches, opponent_pick:, role:)
185+
counters = Hash.new { |h, k| h[k] = { wins: 0, total: 0 } }
186+
matches.each { |match| accumulate_counter(counters, match, opponent_pick: opponent_pick, role: role) }
187+
counters
188+
end
189+
190+
def accumulate_counter(counters, match, opponent_pick:, role:)
191+
opponent_champion = find_opponent_pick(match, opponent_pick: opponent_pick, role: role)
192+
return unless opponent_champion
193+
194+
our_champion = match.our_picks.find { |p| p['role']&.downcase == role.downcase }
195+
return unless our_champion && our_champion['champion']
196+
197+
counter_name = our_champion['champion']
198+
counters[counter_name][:total] += 1
199+
counters[counter_name][:wins] += 1 if match.victory?
200+
end
201+
202+
def find_opponent_pick(match, opponent_pick:, role:)
203+
match.opponent_picks.find do |p|
204+
p['champion'] == opponent_pick && p['role']&.downcase == role.downcase
205+
end
206+
end
207+
208+
def format_counters(counters)
209+
counters.map do |champion, stats|
210+
{
211+
champion: champion,
212+
games: stats[:total],
213+
winrate: ((stats[:wins].to_f / stats[:total]) * 100).round(2)
214+
}
215+
end.sort_by { |c| -c[:winrate] }.first(5)
216+
end
206217
end

app/modules/matches/controllers/export_controller.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ module Controllers
1313
# GET /api/v1/matches/:id/export -> JSON
1414
# GET /api/v1/matches/:id/export?format=csv -> CSV
1515
class ExportController < Api::V1::BaseController
16+
skip_before_action :set_default_response_format
17+
1618
EXPORT_FIELDS = %w[
1719
player_name champion role kills deaths assists
1820
cs neutral_minions_killed cs_at_10
@@ -44,7 +46,7 @@ def render_json_export(match, stats)
4446
match_id: match.id,
4547
riot_match_id: match.riot_match_id,
4648
game_start: match.game_start,
47-
patch_version: match.patch_version,
49+
patch_version: match.game_version,
4850
players: stats.map { |s| build_row_hash(s) }
4951
})
5052
end

app/modules/messaging/controllers/messages_controller.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ def index
3131

3232
result = paginate(messages, per_page: 50)
3333

34-
render_success(
35-
messages: serialize_messages(result[:data].reverse),
36-
pagination: result[:pagination]
37-
)
34+
render_success({
35+
messages: serialize_messages(result[:data].reverse),
36+
pagination: result[:pagination]
37+
})
3838
rescue ActionController::ParameterMissing
3939
render_error(
4040
message: 'recipient_id is required',

app/modules/meta_intelligence/controllers/builds_controller.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ def destroy
9999
# @param [String] patch specific patch to aggregate (optional)
100100
# @return [JSON] { message: 'Aggregation enqueued' }
101101
def aggregate
102-
Jobs::UpdateMetaStatsJob.perform_later(
102+
MetaIntelligence::UpdateMetaStatsJob.perform_later(
103103
current_organization.id,
104104
scope: params[:scope] || 'org',
105105
patch: params[:patch]

app/modules/players/controllers/rosters_controller.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ def set_player
155155
end
156156

157157
def set_scouting_target
158-
@scouting_target = organization_scoped(ScoutingTarget).find(params[:scouting_target_id])
158+
@scouting_target = ScoutingTarget.find(params[:scouting_target_id])
159159
rescue ActiveRecord::RecordNotFound
160160
render_error(
161161
message: 'Scouting target not found',

app/modules/players/controllers/stats_export_controller.rb

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ module Controllers
1313
# GET /api/v1/players/:id/stats/export
1414
# GET /api/v1/players/:id/stats/export?format=csv&from=2026-01-01&to=2026-03-06
1515
class StatsExportController < Api::V1::BaseController
16+
skip_before_action :set_default_response_format
17+
1618
EXPORT_FIELDS = %w[
1719
match_date patch_version opponent champion role
1820
kills deaths assists kda_display cs cs_at_10 cs_per_min
@@ -27,7 +29,7 @@ class StatsExportController < Api::V1::BaseController
2729
].freeze
2830

2931
def show
30-
player = organization_scoped(Player).find(params[:player_id])
32+
player = organization_scoped(Player).find(params[:id])
3133
stats = filtered_stats(player)
3234

3335
respond_to do |format|
@@ -85,8 +87,8 @@ def build_row_array(stat)
8587

8688
def export_field_value(stat, field)
8789
case field
88-
when 'match_date' then stat.match&.game_start&.strftime('%Y-%m-%d')
89-
when 'patch_version' then stat.match&.patch_version
90+
when 'match_date' then stat.match&.game_start&.strftime('%Y-%m-%d')
91+
when 'patch_version' then stat.match&.game_version
9092
when 'opponent' then stat.match&.opponent_name
9193
when 'result' then stat.match&.victory? ? 'W' : 'L'
9294
when 'kda_display' then stat.kda_display

0 commit comments

Comments
 (0)