spec/mongoid/token_spec.rb in mongoid_token-1.1.1 vs spec/mongoid/token_spec.rb in mongoid_token-2.0.0

- old
+ new

@@ -1,235 +1,218 @@ require File.join(File.dirname(__FILE__), %w[.. spec_helper]) -class Account - include Mongoid::Document - include Mongoid::Token - field :name - token :length => 16, :contains => :fixed_numeric -end - -class Person - include Mongoid::Document - include Mongoid::Token - field :email - token :length => 6, :contains => :numeric -end - -class Link - include Mongoid::Document - include Mongoid::Token - - field :url - token :length => 3, :contains => :alphanumeric - - validates :url, :presence => true -end - -class FailLink - include Mongoid::Document - include Mongoid::Token - field :url - token :length => 3, :contains => :alphanumeric, :retry => 0 -end - -class Video - include Mongoid::Document - include Mongoid::Token - - field :name - token :length => 8, :contains => :alpha, :field_name => :vid -end - -class Image - include Mongoid::Document - include Mongoid::Token - - field :url - token :length => 8, :contains => :fixed_numeric_no_leading_zeros -end - -class Event - include Mongoid::Document - include Mongoid::Token - - field :name - token :length => 8, :contains => :alpha_lower -end - -class Node - include Mongoid::Document - include Mongoid::Token - - field :name - token :length => 8, :contains => :fixed_numeric - - embedded_in :cluster -end - -class Cluster - include Mongoid::Document - - field :name - - embeds_many :nodes -end - describe Mongoid::Token do - before :each do - @account = Account.create(:name => "Involved Pty. Ltd.") - @link = Link.create(:url => "http://involved.com.au") - @video = Video.create(:name => "Nyan nyan") - @image = Image.create(:url => "http://involved.com.au/image.png") - @event = Event.create(:name => "Super cool party!") - - Account.create_indexes - Link.create_indexes - FailLink.create_indexes - Video.create_indexes - Image.create_indexes - Event.create_indexes - Node.create_indexes + let(:document_class) do + Class.new(Document) end - it "should have a token field" do - @account.attributes.include?('token').should == true - @link.attributes.include?('token').should == true - @video.attributes.include?('vid').should == true + let(:document) do + document_class.create end - it "should have a token of correct length" do - @account.token.length.should == 16 - @link.token.length.should == 3 - @video.vid.length.should == 8 - @image.token.length.should == 8 - end + describe "#token" do + describe "field" do + before(:each) { document_class.send(:token) } + it "should be created" do + expect(document).to have_field(:token) + end - it "should only generate unique tokens" do - Link.create_indexes - 1000.times do - @link = Link.create(:url => "http://involved.com.au") - Link.where(:token => @link.token).count.should == 1 + it "should be indexed" do + expect(document).to have_index_for(:token => 1).with_options(:unique => true) + end end - end - it "should have a token containing only the specified characters" do - 50.times do - @account = Account.create(:name => "Smith & Co. LLC") - @person = Person.create(:email => "some_random_235@gmail.com") + describe "options" do + it "should accept custom field names" do + document_class.send(:token, :field_name => :smells_as_sweet) + expect(document).to have_field(:smells_as_sweet) + end - @account.token.gsub(/[0-9]/, "").length.should == 0 - @person.token.gsub(/[0-9]/, "").length.should == 0 - end + it "should accept custom lengths" do + document_class.send(:token, :length => 13) + expect(document.token.length).to eq 13 + end - 50.times do - @link = Link.create(:url => "http://involved.com.au") - @link.token.gsub(/[A-Za-z0-9]/, "").length.should == 0 - end + it "should disable custom finders" do + class UntaintedDocument + include Mongoid::Document + include Mongoid::Token + end + dc = Class.new(UntaintedDocument) - 50.times do |index| - @video = Video.create(:name => "A test video") - @video.vid.gsub(/[A-Za-z]/, "").length.should == 0 - end - - 50.times do |index| - @event = Event.create(:name => "Super cool party!2") - @event.token.gsub(/[a-z]/, "").length.should == 0 - end - end + dc.send(:token, :skip_finders => true) + expect(dc.public_methods).to_not include(:find_with_token) + end - it "should create the only after the first save" do - @account = Account.new(:name => "Smith & Co. LLC") - @account.token.should be_nil - @account.save! - @account.token.should_not be_nil - initial_token = @account.token - @account.save! - initial_token.should == @account.token - end + it "should disable `to_param` overrides" do + document_class.send(:token, :override_to_param => false) + expect(document.to_param).to_not eq document.token + end - it "should return the token as its parameter" do - @account.to_param.should == @account.token - @link.to_param.should == @link.token - @video.to_param.should == @video.vid - end + describe "contains" do + context "with :alphanumeric" do + it "should contain only letters and numbers" do + document_class.send(:token, :contains => :alphanumeric, :length => 64) + expect(document.token).to match(/[A-Za-z0-9]{64}/) + end + end + context "with :alpha" do + it "should contain only letters" do + document_class.send(:token, :contains => :alpha, :length => 64) + expect(document.token).to match(/[A-Za-z]{64}/) + end + end + context "with :alpha_upper" do + it "should contain only uppercase letters" do + document_class.send(:token, :contains => :alpha_upper, :length => 64) + expect(document.token).to match(/[A-Z]{64}/) + end + end + context "with :alpha_lower" do + it "should contain only lowercase letters" do + document_class.send(:token, :contains => :alpha_lower, :length => 64) + expect(document.token).to match(/[a-z]{64}/) + end + end + context "with :numeric" do + it "should only contain numbers" do + document_class.send(:token, :contains => :numeric, :length => 64) + expect(document.token).to match(/[0-9]{1,64}/) + end + end + context "with :fixed_numeric" do + it "should contain only numbers and be a fixed-length" do + document_class.send(:token, :contains => :fixed_numeric, :length => 64) + expect(document.token).to match(/[0-9]{64}/) + end + end + context "with :fixed_numeric_no_leading_zeros" do + it "should contain only numbers, be a fixed length, and have no leading zeros" do + document_class.send(:token, :contains => :fixed_numeric_no_leading_zeros, :length => 64) + expect(document.token).to match(/[1-9]{1}[0-9]{63}/) + end + end + end - - it "should be findable by token" do - 50.times do |index| - Account.create(:name => "A random company #{index}") + describe "pattern" do + it "should conform" do + document_class.send(:token, :pattern => "%d%d%d%d%C%C%C%C") + expect(document.token).to match(/[0-9]{4}[A-Z]{4}/) + end + context "when there's a static prefix" do + it "should start with the prefix" do + document_class.send(:token, :pattern => "PREFIX-%d%d%d%d") + expect(document.token).to match(/PREFIX\-[0-9]{4}/) + end + end + context "when there's an infix" do + it "should contain the infix" do + document_class.send(:token, :pattern => "%d%d%d%d-INFIX-%d%d%d%d") + expect(document.token).to match(/[0-9]{4}\-INFIX\-[0-9]{4}/) + end + end + context "when there's a suffix" do + it "should end with the suffix" do + document_class.send(:token, :pattern => "%d%d%d%d-SUFFIX") + expect(document.token).to match(/[0-9]{4}\-SUFFIX/) + end + end + end end - Account.find_by_token(@account.token).id.should == @account.id - Account.find_by_token(Account.last.token).id.should == Account.last.id - 10.times do |index| - Video.create(:name => "Lord of the Rings, Super Special Edition part #{index}") + it "should allow for multiple tokens of different names" do + document_class.send(:token, :contains => :alpha_upper) + document_class.send(:token, :field_name => :sharing_id, :contains => :alpha_lower) + expect(document.token).to match(/[A-Z]{4}/) + expect(document.sharing_id).to match(/[a-z]{4}/) end - Video.find_by_token(@video.vid).id.should == @video.id - Video.find_by_token(Video.last.vid).id.should == Video.last.id end - it "should create a token, if the token is missing" do - @account.token = nil - @account.save! - @account.token.should_not be_nil - end + describe "callbacks" do + context "when the document is a new record" do + let(:document){ document_class.new } + it "should create the token after being saved" do + document_class.send(:token) + expect(document.token).to be_nil + document.save + expect(document.token).to_not be_nil + end + end + context "when the document is not a new record" do + it "should not change the token after being saved" do + document_class.send(:token) + token_before = document.token + document.save + expect(document.token).to eq token_before + end + context "and the token is nil" do + it "should create a new token after being saved" do + document_class.send(:token) + token_before = document.token + document.token = nil + document.save + expect(document.token).to_not be_nil + expect(document.token).to_not eq token_before + end + end + end + context "when the document is cloned" do + it "should set the token to nil" do + document.class.send(:token, :length => 64, :contains => :alpha_upper) + d2 = document.clone + expect(d2.token).to be_nil + end - it "should fail with an exception after 3 retries" do - Link.destroy_all - Link.create_indexes - - @first_link = Link.create(:url => "http://involved.com.au") - Link.any_instance.stub(:generate_token).and_return(@first_link.token) - @link = Link.new(:url => "http://fail.com") - - expect { @link.save! }.to raise_error(Mongoid::Token::CollisionRetriesExceeded) - - Link.count.should == 1 - Link.where(:token => @first_link.token).count.should == 1 + it "should generate a new token with the same options as the source document" do + document.class.send(:token, :length => 64, :contains => :alpha_upper) + d2 = document.clone + d2.save + expect(d2.token).to_not eq document.token + expect(d2.token).to match(/[A-Z]{64}/) + end + end end - it "tries to resolve collisions when instantiated with create!" do - link = Link.create!(url: "http://example.com/1") - - Link.any_instance.stub(:generate_token).and_return(link.token) - - expect { Link.create!(url: "http://example.com/2") }.to raise_error(Mongoid::Token::CollisionRetriesExceeded) + describe "finders" do + it "should create a custom find method" do + document_class.send(:token, :field_name => :other_token) + expect(document.class.public_methods).to include(:find_by_other_token) + end end - it "should not raise a custom error if an operation failure is thrown for another reason" do - link = Link.new - lambda{ link.save! }.should_not raise_error(Mongoid::Token::CollisionRetriesExceeded) - link.valid?.should == false + describe ".to_param" do + it "should return the token" do + document_class.send(:token) + expect(document.to_param).to eq document.token + end end - it "should not raise a custom exception if retries are set to zero" do - FailLink.destroy_all - FailLink.create_indexes - - @first_link = FailLink.create(:url => "http://involved.com.au") - Link.any_instance.stub(:generate_token).and_return(@first_link.token) - @link = FailLink.new(:url => "http://fail.com") - - lambda{ @link.save! }.should_not raise_error(Mongoid::Token::CollisionRetriesExceeded) - end - - it "should create unique indexes on embedded documents" do - @cluster = Cluster.create(:name => "CLUSTER_001") - 5.times do |index| - @cluster.nodes.create!(:name => "NODE_#{index.to_s.rjust(3, '0')}") + describe "collision resolution" do + before(:each) do + document_class.send(:token) + document_class.create_indexes end - @cluster.nodes.each do |node| - node.attributes.include?('token').should == true - node.token.match(/[0-9]{8}/).should_not == nil - end - end + context "when creating a new record" do + it "should raise an exception when collisions can't be resolved on save" do + document.token = "1234" + document.save + d2 = document.clone + d2.stub(:generate_token).and_return("1234") + expect{d2.save}.to raise_exception(Mongoid::Token::CollisionRetriesExceeded) + end - describe "with :fixed_numeric_not_null" do - it "should not start with 0" do - 1000.times do - image = Image.create(:url => "http://something.com/image.png") - image.token.should_not start_with "0" + it "should raise an exception when collisions can't be resolved on create!" do + document.token = "1234" + document.save + document_class.any_instance.stub(:generate_token).and_return("1234") + expect{document_class.create!}.to raise_exception(Mongoid::Token::CollisionRetriesExceeded) end + end + + it "should not raise a custom error if an operation failure is thrown for another reason" do + document_class.send(:field, :name) + document_class.send(:validates_presence_of, :name) + expect{document_class.create!}.to_not raise_exception(Mongoid::Token::CollisionRetriesExceeded) end end end