diff --git a/lib/rpush/daemon/batch.rb b/lib/rpush/daemon/batch.rb index c7cfa22b..7037b4a6 100644 --- a/lib/rpush/daemon/batch.rb +++ b/lib/rpush/daemon/batch.rb @@ -39,7 +39,15 @@ def unresolved end end + # Most transports bypass Delivery#mark_retryable's own fail_after check by calling + # this directly, so it's enforced here too rather than assuming it's redundant. def mark_retryable(notification, deliver_after) + if notification.fail_after && notification.fail_after < Time.now + mark_failed(notification, nil, + "Notification failed to be delivered before #{notification.fail_after.strftime('%Y-%m-%d %H:%M:%S')}.") + return + end + @mutex.synchronize do @retryable[deliver_after] ||= [] @retryable[deliver_after] << notification diff --git a/spec/unit/daemon/batch_spec.rb b/spec/unit/daemon/batch_spec.rb index 3df19752..4a8ffdc5 100644 --- a/spec/unit/daemon/batch_spec.rb +++ b/spec/unit/daemon/batch_spec.rb @@ -1,8 +1,8 @@ require 'unit_spec_helper' describe Rpush::Daemon::Batch do - let(:notification1) { double(:notification1, id: 1, delivered: false, failed: false) } - let(:notification2) { double(:notification2, id: 2, delivered: false, failed: false) } + let(:notification1) { double(:notification1, id: 1, delivered: false, failed: false, fail_after: nil) } + let(:notification2) { double(:notification2, id: 2, delivered: false, failed: false, fail_after: nil) } let(:batch) { Rpush::Daemon::Batch.new([notification1, notification2]) } let(:store) { double.as_null_object } let(:time) { Time.now } @@ -96,6 +96,22 @@ expect(batch.retryable).to eq(time => [notification1]) end end + + context 'when one of the notifications has an expired fail_after' do + let(:notification2) { + double(:notification2, id: 2, delivered: false, failed: false, fail_after: time - 1) + } + + it 'fails that notification instead of retrying it' do + batch.mark_all_retryable(time, error) + expect(batch.failed.values.flatten).to eq [notification2] + end + + it 'still retries the other notification' do + batch.mark_all_retryable(time, error) + expect(batch.retryable).to eq(time => [notification1]) + end + end end describe 'mark_failed' do @@ -133,6 +149,33 @@ batch.mark_retryable(notification1, time) expect(batch.retryable).to eq(time => [notification1]) end + + context 'when fail_after has already passed' do + let(:notification1) { + double(:notification1, id: 1, delivered: false, failed: false, fail_after: time - 1) + } + + it 'marks it failed instead of retryable' do + batch.mark_retryable(notification1, time) + expect(batch.failed).to eq([nil, "Notification failed to be delivered before #{(time - 1).strftime('%Y-%m-%d %H:%M:%S')}."] => [notification1]) + end + + it 'does not add it to the retryable set' do + batch.mark_retryable(notification1, time) + expect(batch.retryable).to be_empty + end + end + + context 'when fail_after has not yet passed' do + let(:notification1) { + double(:notification1, id: 1, delivered: false, failed: false, fail_after: time + 60) + } + + it 'still marks it retryable' do + batch.mark_retryable(notification1, time) + expect(batch.retryable).to eq(time => [notification1]) + end + end end describe 'unresolved' do