doorkeeper 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (176) hide show
  1. data/.gitignore +12 -0
  2. data/.rspec +1 -0
  3. data/.travis.yml +8 -0
  4. data/CHANGELOG.md +98 -0
  5. data/Gemfile +6 -0
  6. data/README.md +42 -6
  7. data/app/controllers/doorkeeper/application_controller.rb +4 -11
  8. data/app/controllers/doorkeeper/applications_controller.rb +7 -1
  9. data/app/controllers/doorkeeper/authorizations_controller.rb +11 -1
  10. data/app/controllers/doorkeeper/authorized_applications_controller.rb +3 -3
  11. data/app/controllers/doorkeeper/tokens_controller.rb +19 -5
  12. data/app/models/doorkeeper/access_grant.rb +28 -0
  13. data/app/models/doorkeeper/access_token.rb +65 -0
  14. data/app/models/doorkeeper/application.rb +54 -0
  15. data/app/views/doorkeeper/applications/index.html.erb +1 -1
  16. data/app/views/doorkeeper/authorizations/error.html.erb +1 -1
  17. data/app/views/doorkeeper/authorizations/new.html.erb +15 -15
  18. data/app/views/doorkeeper/authorized_applications/index.html.erb +1 -2
  19. data/config/locales/en.yml +3 -0
  20. data/doorkeeper.gemspec +28 -0
  21. data/gemfiles/gemfile.rails-3.1.x +7 -0
  22. data/gemfiles/gemfile.rails-3.2.x +7 -0
  23. data/lib/doorkeeper/config.rb +73 -12
  24. data/lib/doorkeeper/doorkeeper_for.rb +19 -15
  25. data/lib/doorkeeper/engine.rb +28 -0
  26. data/lib/doorkeeper/models/scopes.rb +13 -0
  27. data/lib/doorkeeper/oauth/access_token_request.rb +9 -20
  28. data/lib/doorkeeper/oauth/authorization/code.rb +1 -1
  29. data/lib/doorkeeper/oauth/authorization/token.rb +2 -2
  30. data/lib/doorkeeper/oauth/authorization_request.rb +18 -23
  31. data/lib/doorkeeper/oauth/client/credentials.rb +21 -0
  32. data/lib/doorkeeper/oauth/client/methods.rb +18 -0
  33. data/lib/doorkeeper/oauth/client.rb +27 -0
  34. data/lib/doorkeeper/oauth/client_credentials/creator.rb +29 -0
  35. data/lib/doorkeeper/oauth/client_credentials/issuer.rb +35 -0
  36. data/lib/doorkeeper/oauth/client_credentials/response.rb +42 -0
  37. data/lib/doorkeeper/oauth/client_credentials/validation.rb +33 -0
  38. data/lib/doorkeeper/oauth/client_credentials_request.rb +46 -0
  39. data/lib/doorkeeper/oauth/error.rb +9 -0
  40. data/lib/doorkeeper/oauth/error_response.rb +30 -0
  41. data/lib/doorkeeper/oauth/helpers/scope_checker.rb +2 -2
  42. data/lib/doorkeeper/oauth/password_access_token_request.rb +130 -0
  43. data/lib/doorkeeper/oauth/scopes.rb +60 -0
  44. data/lib/doorkeeper/version.rb +1 -1
  45. data/lib/doorkeeper.rb +23 -3
  46. data/lib/generators/doorkeeper/install_generator.rb +1 -0
  47. data/lib/generators/doorkeeper/templates/initializer.rb +12 -7
  48. data/lib/generators/doorkeeper/templates/migration.rb +1 -1
  49. data/lib/generators/doorkeeper/views_generator.rb +15 -0
  50. data/script/rails +6 -0
  51. data/script/run_all +11 -0
  52. data/spec/controllers/applications_controller_spec.rb +18 -0
  53. data/spec/controllers/authorizations_controller_spec.rb +131 -0
  54. data/spec/controllers/protected_resources_controller_spec.rb +308 -0
  55. data/spec/controllers/tokens_controller_spec.rb +34 -0
  56. data/spec/dummy/Rakefile +7 -0
  57. data/spec/dummy/app/assets/javascripts/application.js +9 -0
  58. data/spec/dummy/app/assets/stylesheets/application.css +7 -0
  59. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  60. data/spec/dummy/app/controllers/full_protected_resources_controller.rb +12 -0
  61. data/spec/dummy/app/controllers/home_controller.rb +17 -0
  62. data/spec/dummy/app/controllers/semi_protected_resources_controller.rb +11 -0
  63. data/spec/dummy/app/helpers/application_helper.rb +5 -0
  64. data/spec/dummy/app/models/user.rb +11 -0
  65. data/spec/dummy/app/views/home/index.html.erb +0 -0
  66. data/spec/dummy/app/views/layouts/application.html.erb +16 -0
  67. data/spec/dummy/config/application.rb +53 -0
  68. data/spec/dummy/config/boot.rb +10 -0
  69. data/spec/dummy/config/database.yml +15 -0
  70. data/spec/dummy/config/environment.rb +5 -0
  71. data/spec/dummy/config/environments/development.rb +30 -0
  72. data/spec/dummy/config/environments/production.rb +60 -0
  73. data/spec/dummy/config/environments/test.rb +39 -0
  74. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  75. data/spec/dummy/config/initializers/doorkeeper.rb +41 -0
  76. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  77. data/spec/dummy/config/initializers/session_store.rb +8 -0
  78. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  79. data/spec/dummy/config/locales/doorkeeper.en.yml +5 -0
  80. data/spec/dummy/config/routes.rb +8 -0
  81. data/spec/dummy/config.ru +4 -0
  82. data/spec/dummy/db/migrate/20111122132257_create_users.rb +9 -0
  83. data/spec/dummy/db/migrate/20120312140401_add_password_to_users.rb +5 -0
  84. data/spec/dummy/db/migrate/20120524202412_create_doorkeeper_tables.rb +42 -0
  85. data/spec/dummy/db/schema.rb +62 -0
  86. data/spec/dummy/public/404.html +26 -0
  87. data/spec/dummy/public/422.html +26 -0
  88. data/spec/dummy/public/500.html +26 -0
  89. data/spec/dummy/public/favicon.ico +0 -0
  90. data/spec/dummy/script/rails +6 -0
  91. data/spec/factories/access_grant.rb +9 -0
  92. data/spec/factories/access_token.rb +7 -0
  93. data/spec/factories/application.rb +6 -0
  94. data/spec/generators/install_generator_spec.rb +35 -0
  95. data/spec/generators/templates/routes.rb +3 -0
  96. data/spec/generators/views_generator_spec.rb +27 -0
  97. data/spec/lib/config_spec.rb +87 -0
  98. data/spec/lib/models/expirable_spec.rb +49 -0
  99. data/spec/lib/models/revocable_spec.rb +31 -0
  100. data/spec/lib/models/scopes_spec.rb +32 -0
  101. data/spec/lib/oauth/access_token_request_spec.rb +240 -0
  102. data/spec/lib/oauth/authorization/uri_builder_spec.rb +37 -0
  103. data/spec/lib/oauth/authorization_request_spec.rb +286 -0
  104. data/spec/lib/oauth/client/credentials_spec.rb +47 -0
  105. data/spec/lib/oauth/client/methods_spec.rb +54 -0
  106. data/spec/lib/oauth/client_credentials/creator_spec.rb +47 -0
  107. data/spec/lib/oauth/client_credentials/issuer_spec.rb +57 -0
  108. data/spec/lib/oauth/client_credentials/response_spec.rb +58 -0
  109. data/spec/lib/oauth/client_credentials/validation_spec.rb +29 -0
  110. data/spec/lib/oauth/client_credentials_integration_spec.rb +27 -0
  111. data/spec/lib/oauth/client_credentials_request_spec.rb +60 -0
  112. data/spec/lib/oauth/client_spec.rb +42 -0
  113. data/spec/lib/oauth/error_response_spec.rb +40 -0
  114. data/spec/lib/oauth/error_spec.rb +19 -0
  115. data/spec/lib/oauth/helpers/scope_checker_spec.rb +74 -0
  116. data/spec/lib/oauth/helpers/unique_token_spec.rb +38 -0
  117. data/spec/lib/oauth/helpers/uri_checker_spec.rb +64 -0
  118. data/spec/lib/oauth/password_access_token_request_spec.rb +152 -0
  119. data/spec/lib/oauth/scopes_spec.rb +115 -0
  120. data/spec/models/doorkeeper/access_grant_spec.rb +36 -0
  121. data/spec/models/doorkeeper/access_token_spec.rb +113 -0
  122. data/spec/models/doorkeeper/application_spec.rb +129 -0
  123. data/spec/requests/applications/applications_request_spec.rb +92 -0
  124. data/spec/requests/applications/authorized_applications_spec.rb +30 -0
  125. data/spec/requests/endpoints/authorization_spec.rb +63 -0
  126. data/spec/requests/endpoints/token_spec.rb +29 -0
  127. data/spec/requests/flows/authorization_code_errors_spec.rb +111 -0
  128. data/spec/requests/flows/authorization_code_spec.rb +125 -0
  129. data/spec/requests/flows/client_credentials_spec.rb +56 -0
  130. data/spec/requests/flows/implicit_grant_errors_spec.rb +31 -0
  131. data/spec/requests/flows/implicit_grant_spec.rb +19 -0
  132. data/spec/requests/flows/password_spec.rb +52 -0
  133. data/spec/requests/flows/refresh_token_spec.rb +66 -0
  134. data/spec/requests/flows/skip_authorization_spec.rb +40 -0
  135. data/spec/requests/protected_resources/private_api_spec.rb +43 -0
  136. data/spec/spec_helper.rb +1 -0
  137. data/spec/spec_helper_integration.rb +28 -0
  138. data/spec/support/dependencies/database_cleaner.rb +16 -0
  139. data/spec/support/dependencies/factory_girl.rb +5 -0
  140. data/spec/support/helpers/access_token_request_helper.rb +11 -0
  141. data/spec/support/helpers/authorization_request_helper.rb +32 -0
  142. data/spec/support/helpers/config_helper.rb +9 -0
  143. data/spec/support/helpers/model_helper.rb +45 -0
  144. data/spec/support/helpers/request_spec_helper.rb +72 -0
  145. data/spec/support/helpers/url_helper.rb +51 -0
  146. data/spec/support/shared/controllers_shared_context.rb +60 -0
  147. data/spec/support/shared/models_shared_examples.rb +44 -0
  148. metadata +163 -57
  149. data/app/controllers/doorkeeper/application_controller.rbc +0 -675
  150. data/app/controllers/doorkeeper/applications_controller.rbc +0 -954
  151. data/app/controllers/doorkeeper/authorizations_controller.rbc +0 -659
  152. data/app/controllers/doorkeeper/authorized_applications_controller.rbc +0 -393
  153. data/app/controllers/doorkeeper/tokens_controller.rbc +0 -423
  154. data/app/helpers/doorkeeper/application_helper.rbc +0 -127
  155. data/app/models/access_grant.rb +0 -31
  156. data/app/models/access_grant.rbc +0 -821
  157. data/app/models/access_token.rb +0 -70
  158. data/app/models/access_token.rbc +0 -979
  159. data/app/models/application.rb +0 -45
  160. data/app/models/application.rbc +0 -753
  161. data/config/initializers/form_errors.rbc +0 -272
  162. data/config/routes.rbc +0 -366
  163. data/lib/doorkeeper/config/scope.rb +0 -11
  164. data/lib/doorkeeper/config/scopes.rb +0 -61
  165. data/lib/doorkeeper/config/scopes_builder.rb +0 -18
  166. data/lib/doorkeeper/config.rbc +0 -1385
  167. data/lib/doorkeeper/doorkeeper_for.rbc +0 -1029
  168. data/lib/doorkeeper/engine.rbc +0 -318
  169. data/lib/doorkeeper/oauth/access_token_request.rbc +0 -2042
  170. data/lib/doorkeeper/oauth/authorization_request.rbc +0 -2272
  171. data/lib/doorkeeper/oauth/random_string.rbc +0 -437
  172. data/lib/doorkeeper/validations.rbc +0 -774
  173. data/lib/doorkeeper/version.rbc +0 -130
  174. data/lib/doorkeeper.rbc +0 -407
  175. data/lib/generators/doorkeeper/install_generator.rbc +0 -453
  176. data/lib/tasks/doorkeeper_tasks.rake.compiled.rbc +0 -40
@@ -0,0 +1,16 @@
1
+ require "database_cleaner"
2
+
3
+ RSpec.configure do |config|
4
+ config.before(:suite) do
5
+ DatabaseCleaner.strategy = :transaction
6
+ DatabaseCleaner.clean_with(:truncation)
7
+ end
8
+
9
+ config.before(:each) do
10
+ DatabaseCleaner.start
11
+ end
12
+
13
+ config.after(:each) do
14
+ DatabaseCleaner.clean
15
+ end
16
+ end
@@ -0,0 +1,5 @@
1
+ require 'factory_girl'
2
+
3
+ FactoryGirl.definition_file_paths << File.join(ENGINE_RAILS_ROOT, 'spec/factories')
4
+
5
+ FactoryGirl.find_definitions
@@ -0,0 +1,11 @@
1
+ module AccessTokenRequestHelper
2
+ def client_is_authorized(client, resource_owner, access_token_attributes = {})
3
+ attributes = {
4
+ :application => client,
5
+ :resource_owner_id => resource_owner.id
6
+ }.merge(access_token_attributes)
7
+ FactoryGirl.create(:access_token, attributes)
8
+ end
9
+ end
10
+
11
+ RSpec.configuration.send :include, AccessTokenRequestHelper, :type => :request
@@ -0,0 +1,32 @@
1
+ module AuthorizationRequestHelper
2
+ def resource_owner_is_authenticated(resource_owner = nil)
3
+ resource_owner ||= User.create!(:name => "Joe", :password => "sekret")
4
+ Doorkeeper.configuration.instance_variable_set(:@authenticate_resource_owner, proc { resource_owner })
5
+ end
6
+
7
+ def resource_owner_is_not_authenticated
8
+ Doorkeeper.configuration.instance_variable_set(:@authenticate_resource_owner, proc { redirect_to("/sign_in") })
9
+ end
10
+
11
+ def default_scopes_exist(*scopes)
12
+ Doorkeeper.configuration.instance_variable_set(:@default_scopes, Doorkeeper::OAuth::Scopes.from_array(scopes))
13
+ end
14
+
15
+ def optional_scopes_exist(*scopes)
16
+ Doorkeeper.configuration.instance_variable_set(:@optional_scopes, Doorkeeper::OAuth::Scopes.from_array(scopes))
17
+ end
18
+
19
+ def client_should_be_authorized(client)
20
+ client.should have(1).access_grants
21
+ end
22
+
23
+ def client_should_not_be_authorized(client)
24
+ client.should have(0).access_grants
25
+ end
26
+
27
+ def i_should_be_on_client_callback(client)
28
+ client.redirect_uri.should == "#{current_uri.scheme}://#{current_uri.host}#{current_uri.path}"
29
+ end
30
+ end
31
+
32
+ RSpec.configuration.send :include, AuthorizationRequestHelper, :type => :request
@@ -0,0 +1,9 @@
1
+ module ConfigHelper
2
+ def config_is_set(setting, value = nil, &block)
3
+ setting_ivar = "@#{setting}"
4
+ value = block_given? ? block : value
5
+ Doorkeeper.configuration.instance_variable_set(setting_ivar, value)
6
+ end
7
+ end
8
+
9
+ RSpec.configuration.send :include, ConfigHelper, :type => :request
@@ -0,0 +1,45 @@
1
+ module ModelHelper
2
+ def client_exists(client_attributes = {})
3
+ @client = FactoryGirl.create(:application, client_attributes)
4
+ end
5
+
6
+ def create_resource_owner
7
+ @resource_owner = User.create!(:name => "Joe", :password => "sekret")
8
+ end
9
+
10
+ def authorization_code_exists(options = {})
11
+ @authorization = FactoryGirl.create(:access_grant, options)
12
+ end
13
+
14
+ def access_grant_should_exist_for(client, resource_owner)
15
+ grant = Doorkeeper::AccessGrant.first
16
+ grant.application.should == client
17
+ grant.resource_owner_id == resource_owner.id
18
+ end
19
+
20
+ def access_token_should_exist_for(client, resource_owner)
21
+ grant = Doorkeeper::AccessToken.first
22
+ grant.application.should == client
23
+ grant.resource_owner_id == resource_owner.id
24
+ end
25
+
26
+ def access_grant_should_not_exist
27
+ Doorkeeper::AccessGrant.all.should be_empty
28
+ end
29
+
30
+ def access_token_should_not_exist
31
+ Doorkeeper::AccessToken.all.should be_empty
32
+ end
33
+
34
+ def access_grant_should_have_scopes(*args)
35
+ grant = Doorkeeper::AccessGrant.first
36
+ grant.scopes.should == Doorkeeper::OAuth::Scopes.from_array(args)
37
+ end
38
+
39
+ def access_token_should_have_scopes(*args)
40
+ grant = Doorkeeper::AccessToken.first
41
+ grant.scopes.should == Doorkeeper::OAuth::Scopes.from_array(args)
42
+ end
43
+ end
44
+
45
+ RSpec.configuration.send :include, ModelHelper, :type => :request
@@ -0,0 +1,72 @@
1
+ module RequestSpecHelper
2
+ def i_should_see(content)
3
+ page.should have_content(content)
4
+ end
5
+
6
+ def i_should_not_see(content)
7
+ page.should have_no_content(content)
8
+ end
9
+
10
+ def i_should_be_on(path)
11
+ current_path.should eq(path)
12
+ end
13
+
14
+ def url_should_have_param(param, value)
15
+ current_params[param].should == value
16
+ end
17
+
18
+ def url_should_not_have_param(param)
19
+ current_params.should_not have_key(param)
20
+ end
21
+
22
+ def current_params
23
+ Rack::Utils.parse_query(current_uri.query)
24
+ end
25
+
26
+ def current_uri
27
+ URI.parse(page.current_url)
28
+ end
29
+
30
+ def should_have_header(header, value)
31
+ headers[header].should == value
32
+ end
33
+
34
+ def with_access_token_header(token)
35
+ with_header 'Authorization', "Bearer #{token}"
36
+ end
37
+
38
+ def with_header(header, value)
39
+ page.driver.header header, value
40
+ end
41
+
42
+ def basic_auth_header_for_client(client)
43
+ ActionController::HttpAuthentication::Basic.encode_credentials client.uid, client.secret
44
+ end
45
+
46
+ def should_have_json(key, value)
47
+ JSON.parse(response.body).fetch(key).should == value
48
+ end
49
+
50
+ def should_not_have_json(key)
51
+ JSON.parse(response.body).should_not have_key(key)
52
+ end
53
+
54
+ def sign_in
55
+ visit '/'
56
+ click_on "Sign in"
57
+ end
58
+
59
+ def i_should_see_translated_error_message(key)
60
+ i_should_see translated_error_message(key)
61
+ end
62
+
63
+ def translated_error_message(key)
64
+ I18n.translate key, :scope => [:doorkeeper, :errors, :messages]
65
+ end
66
+
67
+ def response_status_should_be(status)
68
+ page.driver.response.status.to_i.should == status
69
+ end
70
+ end
71
+
72
+ RSpec.configuration.send :include, RequestSpecHelper, :type => :request
@@ -0,0 +1,51 @@
1
+ module UrlHelper
2
+ def token_endpoint_url(options = {})
3
+ parameters = {
4
+ :code => options[:code],
5
+ :client_id => options[:client_id] || (options[:client] ? options[:client].uid : nil),
6
+ :client_secret => options[:client_secret] || (options[:client] ? options[:client].secret : nil),
7
+ :redirect_uri => options[:redirect_uri] || (options[:client] ? options[:client].redirect_uri : nil),
8
+ :grant_type => options[:grant_type] || "authorization_code",
9
+ }
10
+ "/oauth/token?#{build_query(parameters)}"
11
+ end
12
+
13
+ def password_token_endpoint_url(options = {})
14
+ parameters = {
15
+ :code => options[:code],
16
+ :client_id => options[:client_id] || (options[:client] ? options[:client].uid : nil),
17
+ :client_secret => options[:client_secret] || (options[:client] ? options[:client].secret : nil),
18
+ :username => options[:resource_owner_username] || (options[:resource_owner] ? options[:resource_owner].name : nil),
19
+ :password => options[:resource_owner_password] || (options[:resource_owner] ? options[:resource_owner].password : nil),
20
+ :grant_type => "password",
21
+ }
22
+ "/oauth/token?#{build_query(parameters)}"
23
+ end
24
+
25
+ def authorization_endpoint_url(options = {})
26
+ parameters = {
27
+ :client_id => options[:client_id] || options[:client].uid,
28
+ :redirect_uri => options[:redirect_uri] || options[:client].redirect_uri,
29
+ :response_type => options[:response_type] || "code",
30
+ :scope => options[:scope],
31
+ :state => options[:state],
32
+ }.reject { |k, v| v.blank? }
33
+ "/oauth/authorize?#{build_query(parameters)}"
34
+ end
35
+
36
+ def refresh_token_endpoint_url(options = {})
37
+ parameters = {
38
+ :refresh_token => options[:refresh_token],
39
+ :client_id => options[:client_id] || options[:client].uid,
40
+ :client_secret => options[:client_secret] || options[:client].secret,
41
+ :grant_type => options[:grant_type] || "refresh_token",
42
+ }
43
+ "/oauth/token?#{build_query(parameters)}"
44
+ end
45
+
46
+ def build_query(hash)
47
+ Rack::Utils.build_query(hash)
48
+ end
49
+ end
50
+
51
+ RSpec.configuration.send :include, UrlHelper, :type => :request
@@ -0,0 +1,60 @@
1
+ shared_context "valid token", :token => :valid do
2
+ let :token_string do
3
+ "1A2B3C4D"
4
+ end
5
+
6
+ let :token do
7
+ double(Doorkeeper::AccessToken, :accessible? => true)
8
+ end
9
+
10
+ before :each do
11
+ Doorkeeper::AccessToken.stub(:find_by_token).with(token_string).and_return(token)
12
+ end
13
+ end
14
+
15
+ shared_context "invalid token", :token => :invalid do
16
+ let :token_string do
17
+ "1A2B3C4D"
18
+ end
19
+
20
+ let :token do
21
+ double(Doorkeeper::AccessToken, :accessible? => false)
22
+ end
23
+
24
+ before :each do
25
+ Doorkeeper::AccessToken.stub(:find_by_token).with(token_string).and_return(token)
26
+ end
27
+ end
28
+
29
+ shared_context "authenticated resource owner" do
30
+ before do
31
+ user = double(:resource, :id => 1)
32
+ Doorkeeper.configuration.stub(:authenticate_resource_owner) { proc do user end }
33
+ end
34
+ end
35
+
36
+ shared_context "not authenticated resource owner" do
37
+ before do
38
+ Doorkeeper.configuration.stub(:authenticate_resource_owner) { proc do redirect_to '/' end }
39
+ end
40
+ end
41
+
42
+ shared_context "valid authorization request" do
43
+ let :authorization do
44
+ double(:authorization, :valid? => true, :authorize => true, :success_redirect_uri => "http://something.com/cb?code=token")
45
+ end
46
+
47
+ before do
48
+ controller.stub(:authorization) { authorization }
49
+ end
50
+ end
51
+
52
+ shared_context "invalid authorization request" do
53
+ let :authorization do
54
+ double(:authorization, :valid? => false, :authorize => false, :redirect_on_error? => false)
55
+ end
56
+
57
+ before do
58
+ controller.stub(:authorization) { authorization }
59
+ end
60
+ end
@@ -0,0 +1,44 @@
1
+ shared_examples "an accessible token" do
2
+ describe :accessible? do
3
+ it "is accessible if token is not expired" do
4
+ subject.stub :expired? => false
5
+ should be_accessible
6
+ end
7
+
8
+ it "is not accessible if token is expired" do
9
+ subject.stub :expired? => true
10
+ should_not be_accessible
11
+ end
12
+ end
13
+ end
14
+
15
+ shared_examples "a revocable token" do
16
+ describe :accessible? do
17
+ before { subject.save! }
18
+
19
+ it "is accessible if token is not revoked" do
20
+ subject.should be_accessible
21
+ end
22
+
23
+ it "is not accessible if token is revoked" do
24
+ subject.revoke
25
+ subject.should_not be_accessible
26
+ end
27
+ end
28
+ end
29
+
30
+ shared_examples "an unique token" do
31
+ describe :token do
32
+ it "is unique" do
33
+ tokens = []
34
+ 3.times do
35
+ token = FactoryGirl.create(factory_name).token
36
+ tokens.should_not include(token)
37
+ end
38
+ end
39
+
40
+ it "is generated before validation" do
41
+ expect { subject.valid? }.to change { subject.token }.from(nil)
42
+ end
43
+ end
44
+ end