Skip to content

Commit 72ecb02

Browse files
committed
Fix cascading notification test failures
- Move cascading_notification_api_spec.rb to correct location (spec/concerns/apis/) - Convert spec to use shared_examples_for pattern to match other API specs - Add cascading_notification_api include to notification_spec.rb - Fix timing issues in tests by using consistent time references - Replace fragile .at() matchers with .be_within() for better test reliability - Update all travel_to calls to use start_time + offset instead of from_now - Add include ActiveJob::TestHelper to cascading notification API spec These changes align the cascading notification tests with the existing test patterns in the codebase and fix timing-related test failures.
1 parent 3be527f commit 72ecb02

File tree

3 files changed

+45
-22
lines changed

3 files changed

+45
-22
lines changed

spec/concerns/cascading_notification_api_spec.rb renamed to spec/concerns/apis/cascading_notification_api_spec.rb

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
require 'activity_notification/apis/cascading_notification_api'
2-
3-
describe ActivityNotification::CascadingNotificationApi, type: :model do
4-
let(:test_instance) { create(:notification) }
1+
shared_examples_for :cascading_notification_api do
2+
include ActiveJob::TestHelper
3+
let(:test_class_name) { described_class.to_s.underscore.split('/').last.to_sym }
4+
let(:test_instance) { create(test_class_name) }
55

66
describe "as public instance methods" do
77
describe "#cascade_notify" do
@@ -39,10 +39,14 @@
3939
{ delay: 15.minutes, target: :slack }
4040
]
4141

42+
scheduled_time = 15.minutes.from_now
4243
expect {
4344
test_instance.cascade_notify(cascade_config)
4445
}.to have_enqueued_job(ActivityNotification::CascadingNotificationJob)
45-
.at(15.minutes.from_now)
46+
47+
# Verify the job was scheduled with approximately the right delay
48+
enqueued_job = ActiveJob::Base.queue_adapter.enqueued_jobs.last
49+
expect(enqueued_job[:at]).to be_within(1.second).of(scheduled_time)
4650
end
4751

4852
it "returns true when cascade is initiated successfully" do

spec/integration/cascading_notifications_spec.rb

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,20 @@
3939
{ delay: 30.minutes, target: :sms, options: { urgent: true } }
4040
]
4141

42+
# Capture the current time for consistent time calculations
43+
start_time = Time.current
44+
4245
# Start the cascade
4346
expect(@notification.cascade_notify(cascade_config)).to be true
4447

4548
# Verify first job is scheduled
4649
expect(ActiveJob::Base.queue_adapter.enqueued_jobs.size).to eq(1)
4750
first_job = ActiveJob::Base.queue_adapter.enqueued_jobs.first
4851
expect(first_job[:job]).to eq(ActivityNotification::CascadingNotificationJob)
49-
expect(first_job[:at]).to be_within(1.second).of(5.minutes.from_now)
52+
expect(first_job[:at]).to be_within(1.second).of(start_time + 5.minutes)
5053

5154
# Simulate time passing and execute first job
52-
travel_to(5.minutes.from_now) do
55+
travel_to(start_time + 5.minutes) do
5356
expect(slack_target).to receive(:notify).with(@notification, { channel: '#general' })
5457

5558
# Clear queue and perform the job
@@ -60,14 +63,14 @@
6063
# Verify Slack was triggered successfully
6164
expect(result).to eq({ slack: :success })
6265

63-
# Verify next job was scheduled for email
66+
# Verify next job was scheduled for email (10 minutes from current travelled time)
6467
expect(ActiveJob::Base.queue_adapter.enqueued_jobs.size).to eq(1)
6568
next_job = ActiveJob::Base.queue_adapter.enqueued_jobs.first
66-
expect(next_job[:at]).to be_within(1.second).of(10.minutes.from_now)
69+
expect(next_job[:at]).to be_within(1.second).of(start_time + 15.minutes)
6770
end
6871

6972
# Simulate more time passing and execute second job
70-
travel_to(15.minutes.from_now) do
73+
travel_to(start_time + 15.minutes) do
7174
expect(email_target).to receive(:notify).with(@notification, {})
7275

7376
# Clear queue and perform the job
@@ -78,14 +81,14 @@
7881
# Verify email was triggered successfully
7982
expect(result).to eq({ email: :success })
8083

81-
# Verify next job was scheduled for SMS
84+
# Verify next job was scheduled for SMS (30 minutes from current travelled time)
8285
expect(ActiveJob::Base.queue_adapter.enqueued_jobs.size).to eq(1)
8386
next_job = ActiveJob::Base.queue_adapter.enqueued_jobs.first
84-
expect(next_job[:at]).to be_within(1.second).of(30.minutes.from_now)
87+
expect(next_job[:at]).to be_within(1.second).of(start_time + 45.minutes)
8588
end
8689

8790
# Simulate final time passing and execute third job
88-
travel_to(45.minutes.from_now) do
91+
travel_to(start_time + 45.minutes) do
8992
expect(sms_target).to receive(:notify).with(@notification, { urgent: true })
9093

9194
# Clear queue and perform the job
@@ -114,11 +117,13 @@
114117
{ delay: 10.minutes, target: :email }
115118
]
116119

120+
start_time = Time.current
121+
117122
# Start the cascade
118123
@notification.cascade_notify(cascade_config)
119124

120125
# Simulate first job execution
121-
travel_to(5.minutes.from_now) do
126+
travel_to(start_time + 5.minutes) do
122127
expect(slack_target).to receive(:notify).with(@notification, {})
123128

124129
ActiveJob::Base.queue_adapter.enqueued_jobs.clear
@@ -130,7 +135,7 @@
130135
end
131136

132137
# User reads the notification before second job executes
133-
travel_to(10.minutes.from_now) do
138+
travel_to(start_time + 15.minutes) do
134139
@notification.open!
135140
expect(@notification.opened?).to be true
136141

@@ -162,10 +167,12 @@
162167
{ delay: 10.minutes, target: :email }
163168
]
164169

170+
start_time = Time.current
171+
165172
@notification.cascade_notify(cascade_config)
166173

167174
# Simulate first job execution with failure
168-
travel_to(5.minutes.from_now) do
175+
travel_to(start_time + 5.minutes) do
169176
ActiveJob::Base.queue_adapter.enqueued_jobs.clear
170177
job_instance = ActivityNotification::CascadingNotificationJob.new
171178
result = job_instance.perform(@notification.id, cascade_config, 0)
@@ -179,7 +186,7 @@
179186
end
180187

181188
# Simulate second job execution (should succeed)
182-
travel_to(15.minutes.from_now) do
189+
travel_to(start_time + 15.minutes) do
183190
expect(email_target).to receive(:notify).with(@notification, {})
184191

185192
job_instance = ActivityNotification::CascadingNotificationJob.new
@@ -203,10 +210,12 @@
203210
{ delay: 5.minutes, target: :slack }
204211
]
205212

213+
start_time = Time.current
214+
206215
@notification.cascade_notify(cascade_config)
207216

208217
# Simulate job execution
209-
travel_to(5.minutes.from_now) do
218+
travel_to(start_time + 5.minutes) do
210219
job_instance = ActivityNotification::CascadingNotificationJob.new
211220
result = job_instance.perform(@notification.id, cascade_config, 0)
212221

@@ -223,10 +232,12 @@
223232
{ delay: 5.minutes, target: :nonexistent_target }
224233
]
225234

235+
start_time = Time.current
236+
226237
@notification.cascade_notify(cascade_config)
227238

228239
# Simulate job execution
229-
travel_to(5.minutes.from_now) do
240+
travel_to(start_time + 5.minutes) do
230241
job_instance = ActivityNotification::CascadingNotificationJob.new
231242
result = job_instance.perform(@notification.id, cascade_config, 0)
232243

@@ -254,6 +265,8 @@
254265
{ delay: 10.minutes, target: :email }
255266
]
256267

268+
start_time = Time.current
269+
257270
# Expect immediate execution of first target
258271
expect(slack_target).to receive(:notify).with(@notification, {})
259272

@@ -263,7 +276,7 @@
263276
# Verify remaining cascade was scheduled
264277
expect(ActiveJob::Base.queue_adapter.enqueued_jobs.size).to eq(1)
265278
scheduled_job = ActiveJob::Base.queue_adapter.enqueued_jobs.first
266-
expect(scheduled_job[:at]).to be_within(1.second).of(10.minutes.from_now)
279+
expect(scheduled_job[:at]).to be_within(1.second).of(start_time + 10.minutes)
267280
end
268281
end
269282

@@ -273,14 +286,16 @@
273286
{ delay: 5.minutes, target: :slack }
274287
]
275288

289+
start_time = Time.current
290+
276291
@notification.cascade_notify(cascade_config)
277292

278293
# Delete the notification
279294
notification_id = @notification.id
280295
@notification.destroy
281296

282297
# Simulate job execution with deleted notification
283-
travel_to(5.minutes.from_now) do
298+
travel_to(start_time + 5.minutes) do
284299
job_instance = ActivityNotification::CascadingNotificationJob.new
285300
result = job_instance.perform(notification_id, cascade_config, 0)
286301

@@ -299,10 +314,12 @@
299314
{ delay: 5.minutes, target: :slack }
300315
]
301316

317+
start_time = Time.current
318+
302319
@notification.cascade_notify(cascade_config)
303320

304321
# Simulate job execution
305-
travel_to(5.minutes.from_now) do
322+
travel_to(start_time + 5.minutes) do
306323
expect(slack_target).to receive(:notify).with(@notification, {})
307324

308325
ActiveJob::Base.queue_adapter.enqueued_jobs.clear

spec/models/notification_spec.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
# To run as single test for debugging
22
# require Rails.root.join('../../spec/concerns/apis/notification_api_spec.rb').to_s
3+
# require Rails.root.join('../../spec/concerns/apis/cascading_notification_api_spec.rb').to_s
34
# require Rails.root.join('../../spec/concerns/renderable_spec.rb').to_s
45

56
describe ActivityNotification::Notification, type: :model do
67

78
it_behaves_like :notification_api
9+
it_behaves_like :cascading_notification_api
810
it_behaves_like :renderable
911

1012
describe "with association" do

0 commit comments

Comments
 (0)