saucy 0.15.2 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (134) hide show
  1. data/.gitignore +2 -0
  2. data/CHANGELOG.md +3 -7
  3. data/Gemfile +1 -10
  4. data/Gemfile.lock +42 -3
  5. data/README.md +5 -0
  6. data/Rakefile +12 -3
  7. data/app/controllers/accounts_controller.rb +2 -1
  8. data/app/mailers/billing_mailer.rb +8 -0
  9. data/app/models/canceled_account.rb +12 -7
  10. data/app/views/accounts/_projects.html.erb +1 -1
  11. data/app/views/billing_mailer/cancelation_notification.text.erb +3 -0
  12. data/app/views/invitation_mailer/invitation.text.erb +1 -1
  13. data/app/views/invitations/show.html.erb +1 -1
  14. data/app/views/memberships/edit.html.erb +2 -2
  15. data/app/views/memberships/index.html.erb +1 -1
  16. data/app/views/projects/_form.html.erb +5 -5
  17. data/app/views/projects/edit.html.erb +2 -2
  18. data/app/views/projects/index.html.erb +3 -3
  19. data/app/views/projects/new.html.erb +1 -1
  20. data/app/views/shared/_project_dropdown.html.erb +3 -3
  21. data/config/routes.rb +2 -6
  22. data/cucumber.yml +1 -0
  23. data/{lib/generators/saucy/features/templates/features → features/engine}/admin_reporting.feature +0 -0
  24. data/{lib/generators/saucy/features/templates/features → features/engine}/edit_profile.feature +0 -0
  25. data/{lib/generators/saucy/features/templates/features → features/engine}/edit_project_permissions.feature +0 -0
  26. data/{lib/generators/saucy/features/templates/features → features/engine}/edit_user_permissions.feature +0 -0
  27. data/{lib/generators/saucy/features/templates/features → features/engine}/manage_account.feature +0 -0
  28. data/{lib/generators/saucy/features/templates/features → features/engine}/manage_billing.feature +1 -1
  29. data/{lib/generators/saucy/features/templates/features → features/engine}/manage_plan.feature +0 -0
  30. data/{lib/generators/saucy/features/templates/features → features/engine}/manage_projects.feature +0 -0
  31. data/{lib/generators/saucy/features/templates/features → features/engine}/manage_users.feature +0 -0
  32. data/{lib/generators/saucy/features/templates/features → features/engine}/new_account.feature +0 -0
  33. data/{lib/generators/saucy/features/templates/features → features/engine}/project_dropdown.feature +0 -0
  34. data/{lib/generators/saucy/features/templates/features → features/engine}/sign_up.feature +0 -0
  35. data/{lib/generators/saucy/features/templates/features → features/engine}/sign_up_coupon.feature +0 -0
  36. data/{lib/generators/saucy/features/templates/features → features/engine}/sign_up_paid.feature +0 -0
  37. data/{lib/generators/saucy/features/templates/features → features/engine}/trial_plans.feature +0 -0
  38. data/features/integration/run_features.feature +46 -0
  39. data/features/step_definitions/clearance_steps.rb +123 -0
  40. data/{lib/generators/saucy/features/templates/step_definitions → features/step_definitions/engine}/account_steps.rb +0 -0
  41. data/features/step_definitions/engine/braintree_steps.rb +24 -0
  42. data/{lib/generators/saucy/features/templates/step_definitions → features/step_definitions/engine}/country_steps.rb +0 -0
  43. data/{lib/generators/saucy/features/templates/step_definitions → features/step_definitions/engine}/cron_steps.rb +3 -5
  44. data/{lib/generators/saucy/features/templates/step_definitions → features/step_definitions/engine}/email_steps.rb +0 -0
  45. data/{lib/generators/saucy/features/templates/step_definitions → features/step_definitions/engine}/factory_girl_steps.rb +0 -0
  46. data/{lib/generators/saucy/features/templates/step_definitions → features/step_definitions/engine}/html_steps.rb +0 -0
  47. data/{lib/generators/saucy/features/templates/step_definitions → features/step_definitions/engine}/plan_steps.rb +0 -0
  48. data/{lib/generators/saucy/features/templates/step_definitions → features/step_definitions/engine}/project_steps.rb +0 -0
  49. data/{lib/generators/saucy/features/templates/step_definitions → features/step_definitions/engine}/session_steps.rb +0 -0
  50. data/{lib/generators/saucy/features/templates/step_definitions → features/step_definitions/engine}/user_steps.rb +0 -0
  51. data/features/step_definitions/rails_steps.rb +26 -8
  52. data/features/step_definitions/saucy_steps.rb +5 -0
  53. data/features/step_definitions/web_steps.rb +253 -0
  54. data/features/support/engine/braintree.rb +5 -0
  55. data/features/support/env.rb +18 -0
  56. data/features/support/factory_girl.rb +2 -0
  57. data/features/support/paths.rb +68 -0
  58. data/features/support/rails.rb +4 -0
  59. data/features/support/selectors.rb +11 -0
  60. data/lib/generators/saucy/features/features_generator.rb +7 -3
  61. data/lib/saucy/account.rb +55 -57
  62. data/lib/saucy/account_authorization.rb +39 -42
  63. data/lib/saucy/plan.rb +14 -16
  64. data/lib/saucy/project.rb +42 -44
  65. data/lib/saucy/projects_controller.rb +53 -55
  66. data/lib/saucy/subscription.rb +189 -191
  67. data/lib/saucy/user.rb +5 -7
  68. data/lib/saucy/version.rb +1 -1
  69. data/saucy.gemspec +21 -5
  70. data/spec/controllers/accounts_controller_spec.rb +2 -1
  71. data/spec/controllers/projects_controller_spec.rb +1 -1
  72. data/spec/dummy/Rakefile +7 -0
  73. data/spec/dummy/app/assets/javascripts/application.js +9 -0
  74. data/spec/dummy/app/assets/stylesheets/application.css +7 -0
  75. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  76. data/spec/dummy/app/controllers/homes_controller.rb +2 -0
  77. data/spec/dummy/app/controllers/projects_controller.rb +3 -0
  78. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  79. data/spec/dummy/app/mailers/.gitkeep +0 -0
  80. data/spec/dummy/app/models/.gitkeep +0 -0
  81. data/spec/dummy/app/models/account.rb +3 -0
  82. data/spec/dummy/app/models/plan.rb +3 -0
  83. data/spec/dummy/app/models/project.rb +3 -0
  84. data/spec/dummy/app/models/user.rb +4 -0
  85. data/spec/dummy/app/views/homes/show.html.erb +1 -0
  86. data/spec/dummy/app/views/layouts/application.html.erb +24 -0
  87. data/spec/dummy/config.ru +4 -0
  88. data/spec/dummy/config/application.rb +48 -0
  89. data/spec/dummy/config/boot.rb +10 -0
  90. data/spec/dummy/config/database.yml +25 -0
  91. data/spec/dummy/config/environment.rb +5 -0
  92. data/spec/dummy/config/environments/development.rb +30 -0
  93. data/spec/dummy/config/environments/production.rb +60 -0
  94. data/spec/dummy/config/environments/test.rb +42 -0
  95. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  96. data/spec/dummy/config/initializers/clearance.rb +3 -0
  97. data/spec/dummy/config/initializers/inflections.rb +10 -0
  98. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  99. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  100. data/spec/dummy/config/initializers/session_store.rb +8 -0
  101. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  102. data/spec/dummy/config/locales/en.yml +5 -0
  103. data/spec/dummy/config/routes.rb +59 -0
  104. data/spec/dummy/db/migrate/20120201000751_create_diesel_clearance_users.rb +19 -0
  105. data/spec/dummy/db/migrate/20120201001546_create_saucy_tables.rb +122 -0
  106. data/spec/dummy/db/migrate/20120201001548_create_canceled_accounts.rb +14 -0
  107. data/spec/dummy/lib/assets/.gitkeep +0 -0
  108. data/spec/dummy/log/.gitkeep +0 -0
  109. data/spec/dummy/public/404.html +26 -0
  110. data/spec/dummy/public/422.html +26 -0
  111. data/spec/dummy/public/500.html +26 -0
  112. data/spec/dummy/public/favicon.ico +0 -0
  113. data/spec/dummy/script/rails +6 -0
  114. data/spec/mailers/billing_mailer_spec.rb +12 -2
  115. data/spec/models/canceled_account_spec.rb +8 -6
  116. data/spec/models/subscription_spec.rb +47 -53
  117. data/spec/spec_helper.rb +8 -11
  118. data/spec/support/braintree.rb +2 -4
  119. data/spec/support/factories.rb +1 -0
  120. metadata +447 -125
  121. data/db/migrate/20120206155222_add_subscription_token_to_canceled_accounts.rb +0 -5
  122. data/features/run_features.feature +0 -84
  123. data/lib/generators/saucy/features/templates/step_definitions/braintree_steps.rb +0 -25
  124. data/lib/generators/saucy/features/templates/support/braintree.rb +0 -5
  125. data/lib/generators/saucy/specs/specs_generator.rb +0 -20
  126. data/lib/generators/saucy/specs/templates/support/braintree.rb +0 -5
  127. data/lib/saucy/fake_braintree.rb +0 -134
  128. data/lib/saucy/routing_extensions.rb +0 -116
  129. data/spec/dummy/db/migrate/20120206155222_add_subscription_token_to_canceled_accounts.rb +0 -5
  130. data/spec/environment.rb +0 -98
  131. data/spec/route_extensions_spec.rb +0 -51
  132. data/spec/scaffold/config/database.yml +0 -6
  133. data/spec/scaffold/config/routes.rb +0 -5
  134. data/spec/scaffold/views/layouts/application.html.erb +0 -1
@@ -0,0 +1,5 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ # Add new mime types for use in respond_to blocks:
4
+ # Mime::Type.register "text/richtext", :rtf
5
+ # Mime::Type.register_alias "text/html", :iphone
@@ -0,0 +1,7 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ # Your secret key for verifying the integrity of signed cookies.
4
+ # If you change this key, all old signed cookies will become invalid!
5
+ # Make sure the secret is at least 30 characters and all random,
6
+ # no regular words or you'll be exposed to dictionary attacks.
7
+ Dummy::Application.config.secret_token = '5a5796051b49ec05cbb9d33bafc982dcab004a668a44e3b85aa30865a8e779831eeb3e3ba4fe0de65320ccee091c1175ae5f45fa84d1ef00593d375463333016'
@@ -0,0 +1,8 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ Dummy::Application.config.session_store :cookie_store, :key => '_dummy_session'
4
+
5
+ # Use the database for sessions instead of the cookie-based default,
6
+ # which shouldn't be used to store highly confidential information
7
+ # (create the session table with "rails generate session_migration")
8
+ # Dummy::Application.config.session_store :active_record_store
@@ -0,0 +1,14 @@
1
+ # Be sure to restart your server when you modify this file.
2
+ #
3
+ # This file contains settings for ActionController::ParamsWrapper which
4
+ # is enabled by default.
5
+
6
+ # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
7
+ ActiveSupport.on_load(:action_controller) do
8
+ wrap_parameters :format => [:json]
9
+ end
10
+
11
+ # Disable root element in JSON by default.
12
+ ActiveSupport.on_load(:active_record) do
13
+ self.include_root_in_json = false
14
+ end
@@ -0,0 +1,5 @@
1
+ # Sample localization file for English. Add more files in this directory for other locales.
2
+ # See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.
3
+
4
+ en:
5
+ hello: "Hello world"
@@ -0,0 +1,59 @@
1
+ Dummy::Application.routes.draw do
2
+ # The priority is based upon order of creation:
3
+ # first created -> highest priority.
4
+
5
+ # Sample of regular route:
6
+ # match 'products/:id' => 'catalog#view'
7
+ # Keep in mind you can assign values other than :controller and :action
8
+
9
+ # Sample of named route:
10
+ # match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase
11
+ # This route can be invoked with purchase_url(:id => product.id)
12
+
13
+ # Sample resource route (maps HTTP verbs to controller actions automatically):
14
+ # resources :products
15
+
16
+ # Sample resource route with options:
17
+ # resources :products do
18
+ # member do
19
+ # get 'short'
20
+ # post 'toggle'
21
+ # end
22
+ #
23
+ # collection do
24
+ # get 'sold'
25
+ # end
26
+ # end
27
+
28
+ # Sample resource route with sub-resources:
29
+ # resources :products do
30
+ # resources :comments, :sales
31
+ # resource :seller
32
+ # end
33
+
34
+ # Sample resource route with more complex sub-resources
35
+ # resources :products do
36
+ # resources :comments
37
+ # resources :sales do
38
+ # get 'recent', :on => :collection
39
+ # end
40
+ # end
41
+
42
+ # Sample resource route within a namespace:
43
+ # namespace :admin do
44
+ # # Directs /admin/products/* to Admin::ProductsController
45
+ # # (app/controllers/admin/products_controller.rb)
46
+ # resources :products
47
+ # end
48
+
49
+ # You can have the root of your site routed with "root"
50
+ # just remember to delete public/index.html.
51
+ # root :to => 'welcome#index'
52
+
53
+ # See how all your routes lay out with "rake routes"
54
+
55
+ # This is a legacy wild controller route that's not recommended for RESTful applications.
56
+ # Note: This route will make all actions in every controller accessible via GET requests.
57
+ # match ':controller(/:action(/:id(.:format)))'
58
+ root :to => "homes#show"
59
+ end
@@ -0,0 +1,19 @@
1
+ class CreateDieselClearanceUsers < ActiveRecord::Migration
2
+ def self.up
3
+ create_table(:users) do |t|
4
+ t.string :email
5
+ t.string :encrypted_password, :limit => 128
6
+ t.string :salt, :limit => 128
7
+ t.string :confirmation_token, :limit => 128
8
+ t.string :remember_token, :limit => 128
9
+ t.timestamps
10
+ end
11
+
12
+ add_index :users, :email
13
+ add_index :users, :remember_token
14
+ end
15
+
16
+ def self.down
17
+ drop_table :users
18
+ end
19
+ end
@@ -0,0 +1,122 @@
1
+ class CreateSaucyTables < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :memberships do |table|
4
+ table.integer :account_id
5
+ table.integer :user_id
6
+ table.boolean :admin, :null => false, :default => false
7
+ table.datetime :created_at
8
+ table.datetime :updated_at
9
+ end
10
+
11
+ add_index :memberships, [:account_id, :user_id], :unique => true
12
+
13
+ create_table :accounts do |table|
14
+ table.belongs_to :plan
15
+ table.belongs_to :coupon
16
+ table.string :name
17
+ table.string :keyword
18
+ table.datetime :created_at
19
+ table.datetime :updated_at
20
+ table.string :customer_token
21
+ table.string :subscription_token
22
+ table.string :subscription_status
23
+ table.datetime :next_billing_date
24
+ table.boolean :notified_of_disabling, :default => false, :null => false
25
+ table.boolean :notified_of_expiration, :default => false, :null => false
26
+ table.boolean :notified_of_completed_trial, :default => false, :null => false
27
+ table.boolean :asked_to_activate, :default => false, :null => false
28
+ table.boolean :activated, :default => false, :null => false
29
+ table.datetime :trial_expires_at
30
+ end
31
+ add_index :accounts, :plan_id
32
+ add_index :accounts, :coupon_id
33
+ add_index :accounts, :keyword, :unique => true
34
+ add_index :accounts, :next_billing_date
35
+ add_index :accounts, :created_at
36
+ add_index :accounts, :trial_expires_at
37
+
38
+ create_table :coupons, :force => true do |t|
39
+ t.string :name, :default => "", :null => false
40
+ t.integer :free_months, :default => 1, :null => false
41
+ end
42
+
43
+ create_table :invitations do |table|
44
+ table.string :email
45
+ table.integer :account_id
46
+ table.integer :sender_id
47
+ table.boolean :admin, :null => false, :default => false
48
+ table.string :code
49
+ table.boolean :used, :default => false, :null => false
50
+ table.datetime :created_at
51
+ table.datetime :updated_at
52
+ end
53
+
54
+ add_index :invitations, [:account_id]
55
+
56
+ create_table :permissions do |table|
57
+ table.integer :membership_id
58
+ table.integer :user_id
59
+ table.integer :project_id
60
+ table.datetime :created_at
61
+ table.datetime :updated_at
62
+ end
63
+
64
+ add_index :permissions, [:user_id, :project_id]
65
+ add_index :permissions, [:membership_id, :project_id], :name => [:membership_and_project], :unique => true
66
+
67
+ create_table :projects do |table|
68
+ table.string :name
69
+ table.string :keyword
70
+ table.integer :account_id
71
+ table.boolean :archived, :default => false, :null => false
72
+ table.datetime :created_at
73
+ table.datetime :updated_at
74
+ end
75
+ add_index :projects, [:keyword, :account_id], :unique => true
76
+ add_index :projects, :archived
77
+
78
+ change_table :users do |table|
79
+ table.string :name, :default => ""
80
+ end
81
+
82
+ create_table :plans do |t|
83
+ t.string :name
84
+ t.integer :price, :null => false, :default => 0
85
+ t.boolean :trial, :default => false, :null => false
86
+
87
+ t.timestamps
88
+ end
89
+
90
+ add_index :plans, :name
91
+
92
+ create_table :limits do |t|
93
+ t.belongs_to :plan
94
+ t.string :name
95
+ t.column :value, 'BIGINT UNSIGNED', :null => false, :default => 0
96
+ t.string :value_type, :null => false, :default => "number"
97
+
98
+ t.timestamps
99
+ end
100
+
101
+ add_index :limits, :plan_id
102
+
103
+ create_table :invitations_projects, :id => false do |table|
104
+ table.integer :invitation_id, :null => false
105
+ table.integer :project_id, :null => false
106
+ end
107
+
108
+ add_index :invitations_projects, [:invitation_id, :project_id], :unique => true
109
+ end
110
+
111
+ def self.down
112
+ remove_column :users, :name
113
+ drop_table :invitations_projects
114
+ drop_table :plans
115
+ drop_table :projects
116
+ drop_table :permissions
117
+ drop_table :invitations
118
+ drop_table :accounts
119
+ drop_table :memberships
120
+ end
121
+ end
122
+
@@ -0,0 +1,14 @@
1
+ class CreateCanceledAccounts < ActiveRecord::Migration
2
+ def change
3
+ create_table :canceled_accounts do |t|
4
+ t.string :name
5
+ t.string :keyword
6
+ t.string :billing_email
7
+ t.text :memberships
8
+ t.belongs_to :plan
9
+ t.datetime :started_at
10
+
11
+ t.timestamps
12
+ end
13
+ end
14
+ end
File without changes
File without changes
@@ -0,0 +1,26 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The page you were looking for doesn't exist (404)</title>
5
+ <style type="text/css">
6
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
7
+ div.dialog {
8
+ width: 25em;
9
+ padding: 0 4em;
10
+ margin: 4em auto 0 auto;
11
+ border: 1px solid #ccc;
12
+ border-right-color: #999;
13
+ border-bottom-color: #999;
14
+ }
15
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
16
+ </style>
17
+ </head>
18
+
19
+ <body>
20
+ <!-- This file lives in public/404.html -->
21
+ <div class="dialog">
22
+ <h1>The page you were looking for doesn't exist.</h1>
23
+ <p>You may have mistyped the address or the page may have moved.</p>
24
+ </div>
25
+ </body>
26
+ </html>
@@ -0,0 +1,26 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The change you wanted was rejected (422)</title>
5
+ <style type="text/css">
6
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
7
+ div.dialog {
8
+ width: 25em;
9
+ padding: 0 4em;
10
+ margin: 4em auto 0 auto;
11
+ border: 1px solid #ccc;
12
+ border-right-color: #999;
13
+ border-bottom-color: #999;
14
+ }
15
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
16
+ </style>
17
+ </head>
18
+
19
+ <body>
20
+ <!-- This file lives in public/422.html -->
21
+ <div class="dialog">
22
+ <h1>The change you wanted was rejected.</h1>
23
+ <p>Maybe you tried to change something you didn't have access to.</p>
24
+ </div>
25
+ </body>
26
+ </html>
@@ -0,0 +1,26 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>We're sorry, but something went wrong (500)</title>
5
+ <style type="text/css">
6
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
7
+ div.dialog {
8
+ width: 25em;
9
+ padding: 0 4em;
10
+ margin: 4em auto 0 auto;
11
+ border: 1px solid #ccc;
12
+ border-right-color: #999;
13
+ border-bottom-color: #999;
14
+ }
15
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
16
+ </style>
17
+ </head>
18
+
19
+ <body>
20
+ <!-- This file lives in public/500.html -->
21
+ <div class="dialog">
22
+ <h1>We're sorry, but something went wrong.</h1>
23
+ <p>We've been notified about this issue and we'll take a look at it shortly.</p>
24
+ </div>
25
+ </body>
26
+ </html>
File without changes
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
3
+
4
+ APP_PATH = File.expand_path('../../config/application', __FILE__)
5
+ require File.expand_path('../../config/boot', __FILE__)
6
+ require 'rails/commands'
@@ -4,8 +4,8 @@ describe "with an account and transaction" do
4
4
  let(:account) { Factory(:paid_account) }
5
5
 
6
6
  before do
7
- subscription = FakeBraintree.subscriptions[account.subscription_token]
8
- subscription["transactions"] = [FakeBraintree.generated_transaction]
7
+ subscription = FakeBraintree.registry.subscriptions[account.subscription_token]
8
+ subscription["transactions"] = [FakeBraintree.generate_transaction]
9
9
  end
10
10
 
11
11
  describe BillingMailer, "receipt" do
@@ -65,4 +65,14 @@ describe "with an account and transaction" do
65
65
  subject.body.should include(notice)
66
66
  end
67
67
  end
68
+
69
+ describe BillingMailer, "cancelation notification" do
70
+ let(:canceled_account) { CanceledAccount.create(:account => account) }
71
+
72
+ subject { BillingMailer.cancelation_notification(canceled_account) }
73
+
74
+ it "includes the billing email of the canceled account" do
75
+ subject.body.should include(account.billing_email)
76
+ end
77
+ end
68
78
  end
@@ -12,9 +12,11 @@ describe CanceledAccount do
12
12
  end
13
13
 
14
14
  describe CanceledAccount, "given an account" do
15
- let(:account) { Factory(:account, :billing_email => "billing@example.com", :subscription_token => "abc123") }
15
+ let(:account) { Factory(:account, :billing_email => "billing@example.com") }
16
+ let(:mail) { stub('cancelation_notification', :deliver => true) }
16
17
 
17
18
  before do
19
+ BillingMailer.stubs(:cancelation_notification => mail)
18
20
  Factory(:membership, :account => account)
19
21
  end
20
22
 
@@ -30,11 +32,6 @@ describe CanceledAccount, "given an account" do
30
32
  subject.billing_email.should == account.customer.email
31
33
  end
32
34
 
33
- it "populates its subscription_token from the given account" do
34
- subject.subscription_token.should_not be_nil
35
- subject.subscription_token.should == account.subscription_token
36
- end
37
-
38
35
  it "saves the memberships and users as json from the given account" do
39
36
  memberships_json = account.memberships.to_json(:include => { :user => { :only => [:email, :name, :created_at]}}, :only => [:admin, :created_at])
40
37
  subject.memberships.should == memberships_json
@@ -49,4 +46,9 @@ describe CanceledAccount, "given an account" do
49
46
  it "populates the started_at from the given account's creation date" do
50
47
  subject.started_at.should == account.created_at
51
48
  end
49
+
50
+ it "sends a notification email" do
51
+ BillingMailer.should have_received(:cancelation_notification).with(subject)
52
+ mail.should have_received(:deliver)
53
+ end
52
54
  end
@@ -14,56 +14,56 @@ describe Account do
14
14
  subject { Factory(:account) }
15
15
 
16
16
  it "manifests braintree processor_declined errors as errors on number and doesn't save" do
17
- FakeBraintree.failures["4111111111111112"] = { "message" => "Do Not Honor", "code" => "2000", "status" => "processor_declined" }
17
+ FakeBraintree.registry.failures["4111111111111112"] = { "message" => "Do Not Honor", "code" => "2000", "status" => "processor_declined" }
18
18
  account = Factory.build(:paid_account,
19
19
  :card_number => "4111111111111112",
20
20
  :plan => Factory(:paid_plan))
21
21
  account.save.should_not be
22
- FakeBraintree.customers.should be_empty
22
+ FakeBraintree.registry.customers.should be_empty
23
23
  account.persisted?.should_not be
24
24
  account.errors[:card_number].any? { |e| e =~ /denied/ }.should be
25
25
  end
26
26
 
27
27
  it "manifests braintree gateway_rejected errors as errors on number and doesn't save" do
28
- FakeBraintree.failures["4111111111111112"] = { "message" => "Gateway Rejected: cvv", "code" => "N", "status" => "gateway_rejected" }
28
+ FakeBraintree.registry.failures["4111111111111112"] = { "message" => "Gateway Rejected: cvv", "code" => "N", "status" => "gateway_rejected" }
29
29
  account = Factory.build(:paid_account,
30
30
  :card_number => "4111111111111112",
31
31
  :plan => Factory(:paid_plan))
32
32
  account.save.should_not be
33
- FakeBraintree.customers.should be_empty
33
+ FakeBraintree.registry.customers.should be_empty
34
34
  account.persisted?.should_not be
35
35
  account.errors[:verification_code].any? { |e| e =~ /did not match/ }.should be
36
36
  end
37
37
 
38
38
  it "manifests braintree gateway_rejected errors as errors on number and doesn't save" do
39
- FakeBraintree.failures["4111111111111112"] = { "message" => "Credit card number is invalid.", "errors" => { "customer" => { "errors" => [], "credit-card" => { "errors" => [{ "message" => "Credit card number is invalid.", "code" => 81715, "attribute" => :number }] }}}}
39
+ FakeBraintree.registry.failures["4111111111111112"] = { "message" => "Credit card number is invalid.", "errors" => { "customer" => { "errors" => [], "credit-card" => { "errors" => [{ "message" => "Credit card number is invalid.", "code" => 81715, "attribute" => :number }] }}}}
40
40
  account = Factory.build(:paid_account,
41
41
  :card_number => "4111111111111112",
42
42
  :plan => Factory(:paid_plan))
43
43
  account.save.should_not be
44
- FakeBraintree.customers.should be_empty
44
+ FakeBraintree.registry.customers.should be_empty
45
45
  account.persisted?.should_not be
46
46
  account.errors[:card_number].any? { |e| e =~ /is invalid/ }.should be
47
47
  end
48
48
 
49
49
  it "manifests braintree gateway_rejected unknown errors and doesn't save" do
50
- FakeBraintree.failures["4111111111111112"] = { "message" => "Credit card number is invalid.", "errors" => { "customer" => { "errors" => [], "credit-card" => { "errors" => [{ "message" => "There was an unkown error.", "code" => 81715, "attribute" => :unknown }] }}}}
50
+ FakeBraintree.registry.failures["4111111111111112"] = { "message" => "Credit card number is invalid.", "errors" => { "customer" => { "errors" => [], "credit-card" => { "errors" => [{ "message" => "There was an unkown error.", "code" => 81715, "attribute" => :unknown }] }}}}
51
51
  account = Factory.build(:paid_account,
52
52
  :card_number => "4111111111111112",
53
53
  :plan => Factory(:paid_plan))
54
54
  account.save.should_not be
55
- FakeBraintree.customers.should be_empty
55
+ FakeBraintree.registry.customers.should be_empty
56
56
  account.persisted?.should_not be
57
57
  account.errors[:card_number].any? { |e| e =~ /There was an unkown error./ }.should be
58
58
  end
59
59
 
60
60
  it "manifests braintree unknown status error" do
61
- FakeBraintree.failures["4111111111111112"] = { "message" => "Do Not Honor", "code" => "2000", "status" => "unknown" }
61
+ FakeBraintree.registry.failures["4111111111111112"] = { "message" => "Do Not Honor", "code" => "2000", "status" => "unknown" }
62
62
  account = Factory.build(:paid_account,
63
63
  :card_number => "4111111111111112",
64
64
  :plan => Factory(:paid_plan))
65
65
  account.save.should_not be
66
- FakeBraintree.customers.should be_empty
66
+ FakeBraintree.registry.customers.should be_empty
67
67
  account.persisted?.should_not be
68
68
  account.errors[:card_number].any? { |e| e =~ /unknown status/ }.should be
69
69
  end
@@ -84,8 +84,8 @@ describe Account, "given free and paid plans" do
84
84
  Saucy::Subscription::REQUIRED_CUSTOMER_ATTRIBUTES.each do |attribute|
85
85
  account.errors[attribute].should_not be_blank
86
86
  end
87
- FakeBraintree.customers[account.customer_token].should_not be_nil
88
- FakeBraintree.customers[account.customer_token]["credit_cards"].should be_blank
87
+ FakeBraintree.registry.customers[account.customer_token].should_not be_nil
88
+ FakeBraintree.registry.customers[account.customer_token]["credit_cards"].should be_blank
89
89
  end
90
90
 
91
91
  it "requires a billing email when upgrading to a paid plan" do
@@ -146,16 +146,16 @@ describe Account, "with a paid plan" do
146
146
  end
147
147
 
148
148
  it "creates a braintree customer, credit card, and subscription" do
149
- FakeBraintree.customers[subject.customer_token].should_not be_nil
150
- FakeBraintree.customers[subject.customer_token]["credit_cards"].first.should_not be_nil
151
- FakeBraintree.subscriptions[subject.subscription_token].should_not be_nil
149
+ FakeBraintree.registry.customers[subject.customer_token].should_not be_nil
150
+ FakeBraintree.registry.customers[subject.customer_token]["credit_cards"].first.should_not be_nil
151
+ FakeBraintree.registry.subscriptions[subject.subscription_token].should_not be_nil
152
152
  end
153
153
 
154
154
  it "changes the subscription when the plan is changed" do
155
155
  new_plan = Factory(:paid_plan, :name => "New Plan", :price => 5)
156
156
  subject.save_customer_and_subscription!(:plan_id => new_plan.id)
157
- FakeBraintree.subscriptions[subject.subscription_token]["plan_id"].should == new_plan.id
158
- FakeBraintree.subscriptions[subject.subscription_token]["price"].should == "5"
157
+ FakeBraintree.registry.subscriptions[subject.subscription_token]["plan_id"].should == new_plan.id
158
+ FakeBraintree.registry.subscriptions[subject.subscription_token]["price"].should == "5"
159
159
  end
160
160
 
161
161
  context "updating customer credit card information when changed" do
@@ -232,7 +232,7 @@ describe Account, "with a paid plan" do
232
232
 
233
233
  it "deletes the customer when deleted" do
234
234
  subject.destroy
235
- FakeBraintree.customers[subject.customer_token].should be_nil
235
+ FakeBraintree.registry.customers[subject.customer_token].should be_nil
236
236
  end
237
237
  end
238
238
 
@@ -266,21 +266,21 @@ describe Account, "with a free plan" do
266
266
  end
267
267
 
268
268
  it "creates a braintree customer" do
269
- FakeBraintree.customers[subject.customer_token].should_not be_nil
269
+ FakeBraintree.registry.customers[subject.customer_token].should_not be_nil
270
270
  end
271
271
 
272
272
  it "doesn't create a credit card, and subscription" do
273
- FakeBraintree.customers[subject.customer_token]["credit_cards"].should be_nil
274
- FakeBraintree.subscriptions[subject.subscription_token].should be_nil
273
+ FakeBraintree.registry.customers[subject.customer_token]["credit_cards"].should be_empty
274
+ FakeBraintree.registry.subscriptions[subject.subscription_token].should be_nil
275
275
  end
276
276
 
277
277
  it "creates a credit card, subscription, and billing address when the plan is changed to a paid plan and the billing info is supplied" do
278
278
  new_plan = Factory(:paid_plan, :name => "New Plan")
279
279
  subject.save_customer_and_subscription!(Factory.attributes_for(:paid_account, :plan_id => new_plan.id))
280
280
 
281
- FakeBraintree.customers[subject.customer_token]["credit_cards"].first.should_not be_nil
282
- FakeBraintree.subscriptions[subject.subscription_token].should_not be_nil
283
- FakeBraintree.subscriptions[subject.subscription_token]["plan_id"].should == new_plan.id
281
+ FakeBraintree.registry.customers[subject.customer_token]["credit_cards"].first.should_not be_nil
282
+ FakeBraintree.registry.subscriptions[subject.subscription_token].should_not be_nil
283
+ FakeBraintree.registry.subscriptions[subject.subscription_token]["plan_id"].should == new_plan.id
284
284
  subject.credit_card.should_not be_nil
285
285
  subject.subscription.should_not be_nil
286
286
  subject.billing_address.should_not be_nil
@@ -292,7 +292,7 @@ describe Account, "with a free plan" do
292
292
  new_plan = Factory(:paid_plan, :name => "New Plan")
293
293
  subject.save_customer_and_subscription!(Factory.attributes_for(:paid_account, :plan_id => new_plan.id))
294
294
 
295
- FakeBraintree.subscriptions[subject.subscription_token]["merchant_account_id"].should == 'test'
295
+ FakeBraintree.registry.subscriptions[subject.subscription_token]["merchant_account_id"].should == 'test'
296
296
  ensure
297
297
  Saucy::Configuration.merchant_account_id = nil
298
298
  end
@@ -303,7 +303,7 @@ describe Account, "with a free plan" do
303
303
  new_plan = Factory(:paid_plan, :name => "New Plan")
304
304
  subject.save_customer_and_subscription!(Factory.attributes_for(:paid_account, :plan_id => new_plan.id))
305
305
 
306
- FakeBraintree.subscriptions[subject.subscription_token].keys.should_not include("merchant_account_id")
306
+ FakeBraintree.registry.subscriptions[subject.subscription_token].keys.should_not include("merchant_account_id")
307
307
  end
308
308
 
309
309
  it "doesn't create a credit card, subscription, and billing address when the plan is changed to a different free plan" do
@@ -352,7 +352,7 @@ describe Account, "with a paid subscription" do
352
352
 
353
353
  it "gets marked as past due and updates its next_billing_date when subscriptions are updated and it has been rejected by the gateway" do
354
354
  next_billing_date_string = 2.months.from_now.to_s(:braintree_date)
355
- subscription = FakeBraintree.subscriptions[subject.subscription_token]
355
+ subscription = FakeBraintree.registry.subscriptions[subject.subscription_token]
356
356
  subscription["status"] = Braintree::Subscription::Status::PastDue
357
357
  subscription["next_billing_date"] = next_billing_date_string
358
358
 
@@ -390,14 +390,13 @@ describe Account, "with a paid subscription" do
390
390
  end
391
391
 
392
392
  context "when billing didn't work" do
393
- let(:subscription) { FakeBraintree.subscriptions[subject.subscription_token] }
393
+ let(:subscription) { FakeBraintree.registry.subscriptions[subject.subscription_token] }
394
394
 
395
395
  before do
396
396
  subscription["status"] = Braintree::Subscription::Status::PastDue
397
397
  subscription["next_billing_date"] = 2.months.from_now.to_s(:braintree_date)
398
- FakeBraintree.transaction = { :status => Braintree::Transaction::Status::Failed,
399
- :subscription_id => subject.subscription_token }
400
- subscription["transactions"] = [FakeBraintree.generated_transaction]
398
+ subscription["transactions"] = [FakeBraintree.generate_transaction({ :status => Braintree::Transaction::Status::Failed,
399
+ :subscription_id => subject.subscription_token }) ]
401
400
  end
402
401
 
403
402
  it "receives a receipt email at it's billing email with a notice that it failed when billing didn't work" do
@@ -422,7 +421,7 @@ describe Account, "with a paid subscription" do
422
421
  end
423
422
 
424
423
  context "with multiple historical transactions and an active account" do
425
- let(:subscription) { FakeBraintree.subscriptions[subject.subscription_token] }
424
+ let(:subscription) { FakeBraintree.registry.subscriptions[subject.subscription_token] }
426
425
 
427
426
  let!(:earlier_transaction_date) { 2.months.ago }
428
427
  let!(:later_transaction_date) { 1.month.ago }
@@ -433,8 +432,7 @@ describe Account, "with a paid subscription" do
433
432
  :subscription_id => subject.subscription_token
434
433
  }
435
434
 
436
- FakeBraintree.transaction = common_transaction_attributes.merge(attributes)
437
- FakeBraintree.generated_transaction
435
+ FakeBraintree.generate_transaction(common_transaction_attributes.merge(attributes))
438
436
  end
439
437
 
440
438
  before do
@@ -461,9 +459,9 @@ describe Account, "with a paid subscription" do
461
459
 
462
460
  context "with a merchant account timezone different from the system timezone" do
463
461
  def build_subscription(account)
464
- subscription = FakeBraintree.subscriptions[account.subscription_token]
462
+ subscription = FakeBraintree.registry.subscriptions[account.subscription_token]
465
463
  subscription["status"] = Braintree::Subscription::Status::Active
466
- subscription["transactions"] = [FakeBraintree.generated_transaction]
464
+ subscription["transactions"] = [FakeBraintree.generate_transaction]
467
465
  subscription
468
466
  end
469
467
 
@@ -503,16 +501,15 @@ describe Account, "with a paid subscription" do
503
501
  end
504
502
 
505
503
  context "and an active subscription due 2 months from now" do
506
- let(:subscription) { FakeBraintree.subscriptions[subject.subscription_token] }
504
+ let(:subscription) { FakeBraintree.registry.subscriptions[subject.subscription_token] }
507
505
  let(:next_billing_date_string) { 2.months.from_now.to_s(:braintree_date) }
508
506
  let(:zone) { ActiveSupport::TimeZone[Saucy::Configuration.merchant_account_time_zone] }
509
507
 
510
508
  before do
511
509
  subscription["status"] = Braintree::Subscription::Status::Active
512
510
  subscription["next_billing_date"] = next_billing_date_string
513
- FakeBraintree.transaction = { :status => Braintree::Transaction::Status::Settled,
514
- :subscription_id => subject.subscription_token }
515
- subscription["transactions"] = [FakeBraintree.generated_transaction]
511
+ subscription["transactions"] = [FakeBraintree.generate_transaction({ :status => Braintree::Transaction::Status::Settled,
512
+ :subscription_id => subject.subscription_token })]
516
513
  end
517
514
 
518
515
  it "gets marked as not past due and updates its next_billing_date when the subscription is active after its billing date" do
@@ -572,7 +569,7 @@ describe Account, "with a paid subscription that is past due" do
572
569
  end
573
570
 
574
571
  before do
575
- subscription = FakeBraintree.subscriptions[subject.subscription_token]
572
+ subscription = FakeBraintree.registry.subscriptions[subject.subscription_token]
576
573
  subscription["status"] = Braintree::Subscription::Status::PastDue
577
574
  subscription["next_billing_date"] = 2.months.from_now.to_s(:braintree_date)
578
575
 
@@ -586,12 +583,11 @@ describe Account, "with a paid subscription that is past due" do
586
583
 
587
584
  it "retries the subscription charge and updates the subscription when the billing information is correctly updated" do
588
585
  next_billing_date_string = 1.day.from_now.to_s(:braintree_date)
589
- subscription = FakeBraintree.subscriptions[subject.subscription_token]
586
+ subscription = FakeBraintree.registry.subscriptions[subject.subscription_token]
590
587
  subscription["status"] = Braintree::Subscription::Status::Active
591
588
  subscription["next_billing_date"] = next_billing_date_string
592
- FakeBraintree.transaction = { :status => Braintree::Transaction::Status::Settled,
593
- :subscription_id => subject.subscription_token }
594
- transaction = FakeBraintree.generated_transaction
589
+ transaction = FakeBraintree.generate_transaction({ :status => Braintree::Transaction::Status::Settled,
590
+ :subscription_id => subject.subscription_token })
595
591
  subscription["transactions"] = [transaction]
596
592
 
597
593
  retry_transaction = stub(:id => "12345")
@@ -611,12 +607,11 @@ describe Account, "with a paid subscription that is past due" do
611
607
 
612
608
  it "retries the subscription charge and updates the subscription when the payment processing fails" do
613
609
  next_billing_date_string = 2.months.from_now.to_s(:braintree_date)
614
- subscription = FakeBraintree.subscriptions[subject.subscription_token]
610
+ subscription = FakeBraintree.registry.subscriptions[subject.subscription_token]
615
611
  subscription["status"] = Braintree::Subscription::Status::PastDue
616
612
  subscription["next_billing_date"] = next_billing_date_string
617
- FakeBraintree.transaction = { :status => Braintree::Transaction::Status::Failed,
618
- :subscription_id => subject.subscription_token }
619
- transaction = FakeBraintree.generated_transaction
613
+ transaction = FakeBraintree.generate_transaction({ :status => Braintree::Transaction::Status::Failed,
614
+ :subscription_id => subject.subscription_token })
620
615
  subscription["transactions"] = [transaction]
621
616
 
622
617
  retry_transaction = stub(:id => "12345", :status => "processor_declined", "processor_response_text" => "no good")
@@ -637,12 +632,11 @@ describe Account, "with a paid subscription that is past due" do
637
632
 
638
633
  it "retries the subscription charge and updates the subscription when the settlement fails" do
639
634
  next_billing_date_string = 1.day.from_now.to_s(:braintree_date)
640
- subscription = FakeBraintree.subscriptions[subject.subscription_token]
635
+ subscription = FakeBraintree.registry.subscriptions[subject.subscription_token]
641
636
  subscription["status"] = Braintree::Subscription::Status::PastDue
642
637
  subscription["next_billing_date"] = next_billing_date_string
643
- FakeBraintree.transaction = { :status => Braintree::Transaction::Status::Failed,
644
- :subscription_id => subject.subscription_token }
645
- transaction = FakeBraintree.generated_transaction
638
+ transaction = FakeBraintree.generate_transaction({ :status => Braintree::Transaction::Status::Failed,
639
+ :subscription_id => subject.subscription_token })
646
640
  subscription["transactions"] = [transaction]
647
641
 
648
642
  retry_transaction = stub(:id => "12345", :status => "")