Railstutorial.org Unique Email Verification

In section 6.2.4 of the Ruby on Rails 3 Tutorial, Michael Hartle describes a clause about checking the uniqueness of email addresses: if two identical requests are approaching in time, request A can pass the test, then B pass the test, then A save, then B get saved, and You will get two entries with the same value. Each of them was valid at the time of verification.

My question is not about a solution (put a unique constraint in the database so B save will not work). It's about writing a test to prove that the solution works. I tried to write my own, but everything I came up with turned out to mimic the usual, simple tests of uniqueness.

Being completely new to rspec, my naive approach was to simply write a script:

it 'should reject duplicate email addresses with caveat' do A = User.new( @attr ) A.should be_valid # always valid B = User.new( @attr ) B.should be_valid # always valid, as expected A.save.should == true # save always works fine B.save.should == false # this is the problem case # B.should_not be_valid # ...same results as "save.should" end 

but this test passes / fails exactly in the same cases as the regular uniqueness test; B.save.should == false passes when my code is written so that the regular uniqueness test passes and fails when the regular test fails.

So my question is: "How can I write an rspec test that checks that I will solve this problem?" If the answer turns out to be β€œcomplex”, is there any other Rails validation framework I should look at?

+4
source share
1 answer

It's complicated. Racial conditions are so disgusting precisely because they are so difficult to reproduce. Inside save , something like this happens:

  • Validate
  • Write to the database.

So, to reproduce the synchronization problem, you will need to place two save calls to overlap like this (pseudo-Rails):

 a.validate # first half of a.save b.validate # first half of b.save a.write_to_db # second half of a.save b.write_to_db # second half of b.save 

but you cannot open the save method and it is quite easy to script with your internal elements.

But (and this is big, but), you can completely skip validations :

Please note that save also has the ability to skip checks if passed :validate => false as an argument. This method should be used with caution.

So if you use

 b.save(:validate => false) 

you should only get a "write to the database" at half b save and send your data to the database without checking. This should cause a database constraint violation, and I'm sure it will raise ActiveRecord :: StatementInvalid , so I think you will need to look for an exception, not just a false return from save :

 b.save(:validate => false).should raise_exception(ActiveRecord::StatementInvalid) 

You can tighten this to look for a specific exception message. I have nothing convenient for testing this test, so try it in the Rails console and configure the settings accordingly.

+4
source

Source: https://habr.com/ru/post/1389203/


All Articles