2024-01-24 10:04:31 +01:00
|
|
|
describe LockableConcern, type: :controller do
|
|
|
|
controller(ApplicationController) do
|
|
|
|
include LockableConcern
|
|
|
|
|
|
|
|
def test_action
|
|
|
|
lock_action(params.fetch(:lock_key)) do
|
|
|
|
render plain: 'Action completed'
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
before do
|
|
|
|
routes.draw { get 'test_action/:lock_key' => 'anonymous#test_action' }
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '#lock_action' do
|
|
|
|
# randomize key to avoid collision on concurrent tests
|
|
|
|
let(:lock_key) { "test_lock_#{SecureRandom.uuid}" }
|
|
|
|
subject { get :test_action, params: { lock_key: } }
|
|
|
|
|
|
|
|
context 'when there is no concurrent request' do
|
|
|
|
it 'completes the action' do
|
|
|
|
expect(subject).to have_http_status(:ok)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when there are concurrent requests' do
|
|
|
|
it 'aborts the second request' do
|
|
|
|
# Simulating the first request acquiring the lock
|
2024-06-14 11:34:41 +02:00
|
|
|
Kredis.flag(lock_key).mark(expires_in: 3.seconds)
|
2024-01-24 10:04:31 +01:00
|
|
|
|
|
|
|
# Making the second request
|
|
|
|
expect(subject).to have_http_status(:locked)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when the lock expires' do
|
|
|
|
it 'allows another request after expiration' do
|
2024-06-14 11:34:41 +02:00
|
|
|
Kredis.flag(lock_key).mark(expires_in: 0.001.seconds)
|
2024-01-24 10:04:31 +01:00
|
|
|
sleep 0.002
|
|
|
|
|
|
|
|
expect(subject).to have_http_status(:ok)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|