I have implemented a solution similar to this to prune my database.
# model.rb
after_create do
self.class.prune(ENV['VARIABLE_NAME'])
end
def self.prune(max)
order('created_at DESC').last.destroy! until count <= max
end
This works well in manual testing.
In RSpec, the test looks like this:
# spec/models/model_spec.rb
before(:each) do
@model = Model.new
end
describe "prune" do
it "should prune the database when it becomes larger than the allowed size" do
25.times { create(:model) }
first_model = model.first
expect{create(:model)}.to change{Model.count}.by(0)
expect{Model.find(first_model.id)}.to raise_error(ActiveRecord::RecordNotFound)
end
end
end
The result is
1) Model prune should prune the database when it becomes larger than the allowed size
Failure/Error: expect{Model.find(first_model.id)}.to raise_error(ActiveRecord::RecordNotFound)
expected ActiveRecord::RecordNotFound but nothing was raised
Inspecting the database during the test execution reveals that the call to order('created_at DESC').last
is yielding the first instance of the model created in the 25.times
block (Model#2) and not the model created in the before(:each)
block (Model#1).
If I change the line
25.times { create(:model) }
to
25.times { sleep(1); create(:model) }
the test passes. If I instead sleep(0.1)
, the test still fails.
Does this mean that if my app creates two or more Model instances within 1 second of each other that it will choose the newest among them when choosing which to destroy (as opposed to the oldest, which is the intended behavior)? Could this be an ActiveRecord or MySQL bug?
Or if not, is there something about the way FactoryGirl or RSpec create records that isn't representative of production? How can I be sure my test represents realistic scenarios?