Skip to content

Commit 1d16474

Browse files
authored
Fix raw state integers + add sentry logs (#73)
* Replace raw state integers with named enum scopes in jobs Using state: 0 relied on knowing the integer value of the approved enum, which breaks silently if values change. Both DailyPuzzleJob and PuzzleInventoryCheckJob now use Puzzle.approved.where(sent_at: nil). * Small refactor
1 parent 794e150 commit 1d16474

File tree

4 files changed

+162
-3
lines changed

4 files changed

+162
-3
lines changed

app/jobs/daily_puzzle_job.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ class DailyPuzzleJob < ApplicationJob
33
retry_on StandardError, attempts: 3
44

55
def perform
6-
puzzle = Puzzle.where(sent_at: nil, state: 0).order("RANDOM()").first
6+
puzzle = Puzzle.approved.where(sent_at: nil).order("RANDOM()").first
77
return unless puzzle
88

99
Server.where(active: true).each do |server|

app/jobs/puzzle_inventory_check_job.rb

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ class PuzzleInventoryCheckJob < ApplicationJob
33
retry_on StandardError, attempts: 3
44

55
def perform
6-
approved_unsent_puzzle_count = Puzzle.where(state: 0, sent_at: nil).count
6+
approved_unsent_puzzle_count = Puzzle.approved.where(sent_at: nil).count
77

88
if approved_unsent_puzzle_count < 5
99
send_low_inventory_notification(approved_unsent_puzzle_count)
@@ -13,13 +13,17 @@ def perform
1313
private
1414

1515
def send_low_inventory_notification(count)
16+
channel_id = ENV["SHIELD_NOTIFICATIONS_CHANNEL"]
17+
return Rails.logger.warn("SHIELD_NOTIFICATIONS_CHANNEL is not configured, skipping low inventory notification") unless channel_id
18+
1619
notification_message = SlackClient::Messages::LowPuzzleInventoryNotification.new(count).create
17-
send_message(notification_message, channel_id: ENV.fetch("SHIELD_NOTIFICATIONS_CHANNEL", nil))
20+
send_message(notification_message, channel_id: channel_id)
1821
end
1922

2023
def send_message(message, channel_id:)
2124
SlackClient::Client.instance.chat_postMessage(channel: channel_id, blocks: message)
2225
rescue Slack::Web::Api::Errors::SlackError => e
26+
Sentry.capture_exception(e)
2327
Rails.logger.error "Failed to send Slack message: #{e.message} #{e.response_metadata}"
2428
end
2529
end

test/jobs/daily_puzzle_job_test.rb

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
require "test_helper"
2+
3+
class DailyPuzzleJobTest < ActiveJob::TestCase
4+
test "only selects approved puzzles, not pending or rejected" do
5+
pending_puzzle = Puzzle.create!(
6+
question: "Pending puzzle question",
7+
answer: "ruby",
8+
explanation: "Test explanation",
9+
state: :pending,
10+
sent_at: nil,
11+
suggested_by: "test_user"
12+
)
13+
rejected_puzzle = Puzzle.create!(
14+
question: "Rejected puzzle question",
15+
answer: "rails",
16+
explanation: "Test explanation",
17+
state: :rejected,
18+
sent_at: nil,
19+
suggested_by: "test_user"
20+
)
21+
22+
DailyPuzzleJob.perform_now
23+
24+
assert_nil pending_puzzle.reload.sent_at
25+
assert_nil rejected_puzzle.reload.sent_at
26+
end
27+
28+
test "only selects puzzles that have not been sent yet" do
29+
already_sent = Puzzle.create!(
30+
question: "Already sent puzzle question",
31+
answer: "ruby",
32+
explanation: "Test explanation",
33+
state: :approved,
34+
sent_at: 1.day.ago,
35+
suggested_by: "test_user"
36+
)
37+
38+
DailyPuzzleJob.perform_now
39+
40+
assert_equal already_sent.reload.sent_at.to_i, 1.day.ago.to_i
41+
end
42+
43+
test "marks the selected puzzle as archived and sets sent_at" do
44+
puzzle = Puzzle.create!(
45+
question: "Approved unsent puzzle question",
46+
answer: "ruby",
47+
explanation: "Test explanation",
48+
state: :approved,
49+
sent_at: nil,
50+
suggested_by: "test_user"
51+
)
52+
53+
DailyPuzzleJob.perform_now
54+
55+
puzzle.reload
56+
assert_not_nil puzzle.sent_at
57+
assert_equal "archived", puzzle.state
58+
end
59+
60+
test "does nothing when no approved unsent puzzles exist" do
61+
Puzzle.approved.where(sent_at: nil).delete_all
62+
63+
assert_nothing_raised do
64+
DailyPuzzleJob.perform_now
65+
end
66+
end
67+
end
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
require "test_helper"
2+
3+
class PuzzleInventoryCheckJobTest < ActiveJob::TestCase
4+
setup do
5+
Puzzle.approved.where(sent_at: nil).delete_all
6+
ENV["SHIELD_NOTIFICATIONS_CHANNEL"] = "test-channel"
7+
end
8+
9+
test "sends notification when fewer than 5 approved unsent puzzles exist" do
10+
3.times do |i|
11+
Puzzle.create!(
12+
question: "Approved unsent puzzle #{i}",
13+
answer: "ruby",
14+
state: :approved,
15+
sent_at: nil,
16+
explanation: "Test explanation",
17+
suggested_by: "test_user"
18+
)
19+
end
20+
21+
notification_sent = false
22+
job = PuzzleInventoryCheckJob.new
23+
job.define_singleton_method(:send_message) { |*| notification_sent = true }
24+
job.perform
25+
26+
assert notification_sent, "Expected a low inventory notification to be sent"
27+
end
28+
29+
test "does not send notification when 5 or more approved unsent puzzles exist" do
30+
5.times do |i|
31+
Puzzle.create!(
32+
question: "Approved unsent puzzle #{i}",
33+
answer: "ruby",
34+
state: :approved,
35+
sent_at: nil,
36+
explanation: "Test explanation",
37+
suggested_by: "test_user"
38+
)
39+
end
40+
41+
notification_sent = false
42+
job = PuzzleInventoryCheckJob.new
43+
job.define_singleton_method(:send_message) { |*| notification_sent = true }
44+
job.perform
45+
46+
assert_not notification_sent, "Expected no notification to be sent"
47+
end
48+
49+
test "only counts approved puzzles, not pending or rejected" do
50+
5.times do |i|
51+
Puzzle.create!(
52+
question: "Pending puzzle #{i}",
53+
answer: "ruby",
54+
state: :pending,
55+
sent_at: nil,
56+
explanation: "Test explanation",
57+
suggested_by: "test_user"
58+
)
59+
end
60+
61+
notification_sent = false
62+
job = PuzzleInventoryCheckJob.new
63+
job.define_singleton_method(:send_message) { |*| notification_sent = true }
64+
job.perform
65+
66+
assert notification_sent, "Pending puzzles should not count toward approved inventory"
67+
end
68+
69+
test "only counts unsent puzzles" do
70+
5.times do |i|
71+
Puzzle.create!(
72+
question: "Already sent puzzle #{i}",
73+
answer: "ruby",
74+
state: :approved,
75+
sent_at: 1.day.ago,
76+
explanation: "Test explanation",
77+
suggested_by: "test_user"
78+
)
79+
end
80+
81+
notification_sent = false
82+
job = PuzzleInventoryCheckJob.new
83+
job.define_singleton_method(:send_message) { |*| notification_sent = true }
84+
job.perform
85+
86+
assert notification_sent, "Already sent puzzles should not count toward available inventory"
87+
end
88+
end

0 commit comments

Comments
 (0)