discourse/spec/models/topic_timer_spec.rb
Sam Saffron 4ea21fa2d0 DEV: use #frozen_string_literal: true on all spec
This change both speeds up specs (less strings to allocate) and helps catch
cases where methods in Discourse are mutating inputs.

Overall we will be migrating everything to use #frozen_string_literal: true
it will take a while, but this is the first and safest move in this direction
2019-04-30 10:27:42 +10:00

322 lines
9.2 KiB
Ruby

# frozen_string_literal: true
require 'rails_helper'
RSpec.describe TopicTimer, type: :model do
let(:topic_timer) {
# we should not need to do this but somehow
# fabricator is failing here
TopicTimer.create!(
user_id: -1,
topic: Fabricate(:topic),
execute_at: 1.hour.from_now,
status_type: TopicTimer.types[:close]
)
}
let(:topic) { Fabricate(:topic) }
let(:admin) { Fabricate(:admin) }
before do
freeze_time Time.new(2018)
end
context "validations" do
describe '#status_type' do
it 'should ensure that only one active public topic status update exists' do
topic_timer.update!(topic: topic)
Fabricate(:topic_timer, deleted_at: Time.zone.now, topic: topic)
expect { Fabricate(:topic_timer, topic: topic) }
.to raise_error(ActiveRecord::RecordInvalid)
end
it 'should ensure that only one active private topic timer exists per user' do
Fabricate(:topic_timer, topic: topic, user: admin, status_type: TopicTimer.types[:reminder])
expect { Fabricate(:topic_timer, topic: topic, user: admin, status_type: TopicTimer.types[:reminder]) }
.to raise_error(ActiveRecord::RecordInvalid)
end
it 'should allow users to have their own private topic timer' do
expect do
Fabricate(:topic_timer,
topic: topic,
user: Fabricate(:admin),
status_type: TopicTimer.types[:reminder]
)
end.to_not raise_error
end
end
describe '#execute_at' do
describe 'when #execute_at is greater than #created_at' do
it 'should be valid' do
topic_timer = Fabricate.build(:topic_timer,
execute_at: Time.zone.now + 1.hour,
user: Fabricate(:user),
topic: Fabricate(:topic)
)
expect(topic_timer).to be_valid
end
end
describe 'when #execute_at is smaller than #created_at' do
it 'should not be valid' do
topic_timer = Fabricate.build(:topic_timer,
execute_at: Time.zone.now - 1.hour,
created_at: Time.zone.now,
user: Fabricate(:user),
topic: Fabricate(:topic)
)
expect(topic_timer).to_not be_valid
end
end
end
describe '#category_id' do
describe 'when #status_type is publish_to_category' do
describe 'when #category_id is not present' do
it 'should not be valid' do
topic_timer = Fabricate.build(:topic_timer,
status_type: TopicTimer.types[:publish_to_category]
)
expect(topic_timer).to_not be_valid
expect(topic_timer.errors.keys).to include(:category_id)
end
end
describe 'when #category_id is present' do
it 'should be valid' do
topic_timer = Fabricate.build(:topic_timer,
status_type: TopicTimer.types[:publish_to_category],
category_id: Fabricate(:category).id,
user: Fabricate(:user),
topic: Fabricate(:topic)
)
expect(topic_timer).to be_valid
end
end
end
end
end
context 'callbacks' do
describe 'when #execute_at and #user_id are not changed' do
it 'should not schedule another to update topic' do
Jobs.expects(:enqueue_at).with(
topic_timer.execute_at,
:toggle_topic_closed,
topic_timer_id: topic_timer.id,
state: true
).once
topic_timer
Jobs.expects(:cancel_scheduled_job).never
topic_timer.update!(topic: Fabricate(:topic))
end
end
describe 'when #execute_at value is changed' do
it 'reschedules the job' do
freeze_time
topic_timer
Jobs.expects(:cancel_scheduled_job).with(
:toggle_topic_closed, topic_timer_id: topic_timer.id
)
Jobs.expects(:enqueue_at).with(
3.days.from_now, :toggle_topic_closed,
topic_timer_id: topic_timer.id,
state: true
)
topic_timer.update!(execute_at: 3.days.from_now, created_at: Time.zone.now)
end
describe 'when execute_at is smaller than the current time' do
it 'should enqueue the job immediately' do
freeze_time
topic_timer
Jobs.expects(:enqueue_at).with(
Time.zone.now, :toggle_topic_closed,
topic_timer_id: topic_timer.id,
state: true
)
topic_timer.update!(
execute_at: Time.zone.now - 1.hour,
created_at: Time.zone.now - 2.hour
)
end
end
end
describe 'when user is changed' do
it 'should update the job' do
freeze_time
topic_timer
Jobs.expects(:cancel_scheduled_job).with(
:toggle_topic_closed, topic_timer_id: topic_timer.id
)
admin = Fabricate(:admin)
Jobs.expects(:enqueue_at).with(
topic_timer.execute_at,
:toggle_topic_closed,
topic_timer_id: topic_timer.id,
state: true
)
topic_timer.update!(user: admin)
end
end
describe 'when a open topic status update is created for an open topic' do
let(:topic) { Fabricate(:topic, closed: false) }
let(:topic_timer) do
Fabricate(:topic_timer,
status_type: described_class.types[:open],
topic: topic
)
end
before do
Jobs.run_immediately!
end
it 'should close the topic' do
topic_timer
expect(topic.reload.closed).to eq(true)
end
describe 'when topic has been deleted' do
it 'should not queue the job' do
topic.trash!
topic_timer
expect(Jobs::ToggleTopicClosed.jobs).to eq([])
end
end
end
describe 'when a close topic status update is created for a closed topic' do
let(:topic) { Fabricate(:topic, closed: true) }
let(:topic_timer) do
Fabricate(:topic_timer,
status_type: described_class.types[:close],
topic: topic
)
end
before do
Jobs.run_immediately!
end
it 'should open the topic' do
topic_timer
expect(topic.reload.closed).to eq(false)
end
describe 'when topic has been deleted' do
it 'should not queue the job' do
topic.trash!
topic_timer
expect(Jobs::ToggleTopicClosed.jobs).to eq([])
end
end
end
describe '#public_type' do
[:close, :open, :delete].each do |public_type|
it "is true for #{public_type}" do
timer = Fabricate(:topic_timer, status_type: described_class.types[public_type])
expect(timer.public_type).to eq(true)
end
end
it "is true for publish_to_category" do
timer = Fabricate(:topic_timer, status_type: described_class.types[:publish_to_category], category: Fabricate(:category))
expect(timer.public_type).to eq(true)
end
described_class.private_types.keys.each do |private_type|
it "is false for #{private_type}" do
timer = Fabricate(:topic_timer, status_type: described_class.types[private_type])
expect(timer.public_type).to be_falsey
end
end
end
end
describe '.ensure_consistency!' do
it 'should enqueue jobs that have been missed' do
close_topic_timer = Fabricate(:topic_timer,
execute_at: Time.zone.now - 1.hour,
created_at: Time.zone.now - 2.hour
)
open_topic_timer = Fabricate(:topic_timer,
status_type: described_class.types[:open],
execute_at: Time.zone.now - 1.hour,
created_at: Time.zone.now - 2.hour,
topic: Fabricate(:topic, closed: true)
)
Fabricate(:topic_timer, execute_at: Time.zone.now + 1.hour)
Fabricate(:topic_timer,
execute_at: Time.zone.now - 1.hour,
created_at: Time.zone.now - 2.hour
).topic.trash!
# creating topic timers already enqueues jobs
# let's delete them to test ensure_consistency!
Sidekiq::Worker.clear_all
expect { described_class.ensure_consistency! }
.to change { Jobs::ToggleTopicClosed.jobs.count }.by(2)
job_args = Jobs::ToggleTopicClosed.jobs.first["args"].first
expect(job_args["topic_timer_id"]).to eq(close_topic_timer.id)
expect(job_args["state"]).to eq(true)
job_args = Jobs::ToggleTopicClosed.jobs.last["args"].first
expect(job_args["topic_timer_id"]).to eq(open_topic_timer.id)
expect(job_args["state"]).to eq(false)
end
it "should enqueue remind me jobs that have been missed" do
reminder = Fabricate(:topic_timer,
status_type: described_class.types[:reminder],
execute_at: Time.zone.now - 1.hour,
created_at: Time.zone.now - 2.hour
)
# creating topic timers already enqueues jobs
# let's delete them to test ensure_consistency!
Sidekiq::Worker.clear_all
expect { described_class.ensure_consistency! }
.to change { Jobs::TopicReminder.jobs.count }.by(1)
job_args = Jobs::TopicReminder.jobs.first["args"].first
expect(job_args["topic_timer_id"]).to eq(reminder.id)
end
end
end