saucy 0.15.2 → 0.16.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/CHANGELOG.md +3 -7
- data/Gemfile +1 -10
- data/Gemfile.lock +42 -3
- data/README.md +5 -0
- data/Rakefile +12 -3
- data/app/controllers/accounts_controller.rb +2 -1
- data/app/mailers/billing_mailer.rb +8 -0
- data/app/models/canceled_account.rb +12 -7
- data/app/views/accounts/_projects.html.erb +1 -1
- data/app/views/billing_mailer/cancelation_notification.text.erb +3 -0
- data/app/views/invitation_mailer/invitation.text.erb +1 -1
- data/app/views/invitations/show.html.erb +1 -1
- data/app/views/memberships/edit.html.erb +2 -2
- data/app/views/memberships/index.html.erb +1 -1
- data/app/views/projects/_form.html.erb +5 -5
- data/app/views/projects/edit.html.erb +2 -2
- data/app/views/projects/index.html.erb +3 -3
- data/app/views/projects/new.html.erb +1 -1
- data/app/views/shared/_project_dropdown.html.erb +3 -3
- data/config/routes.rb +2 -6
- data/cucumber.yml +1 -0
- data/{lib/generators/saucy/features/templates/features → features/engine}/admin_reporting.feature +0 -0
- data/{lib/generators/saucy/features/templates/features → features/engine}/edit_profile.feature +0 -0
- data/{lib/generators/saucy/features/templates/features → features/engine}/edit_project_permissions.feature +0 -0
- data/{lib/generators/saucy/features/templates/features → features/engine}/edit_user_permissions.feature +0 -0
- data/{lib/generators/saucy/features/templates/features → features/engine}/manage_account.feature +0 -0
- data/{lib/generators/saucy/features/templates/features → features/engine}/manage_billing.feature +1 -1
- data/{lib/generators/saucy/features/templates/features → features/engine}/manage_plan.feature +0 -0
- data/{lib/generators/saucy/features/templates/features → features/engine}/manage_projects.feature +0 -0
- data/{lib/generators/saucy/features/templates/features → features/engine}/manage_users.feature +0 -0
- data/{lib/generators/saucy/features/templates/features → features/engine}/new_account.feature +0 -0
- data/{lib/generators/saucy/features/templates/features → features/engine}/project_dropdown.feature +0 -0
- data/{lib/generators/saucy/features/templates/features → features/engine}/sign_up.feature +0 -0
- data/{lib/generators/saucy/features/templates/features → features/engine}/sign_up_coupon.feature +0 -0
- data/{lib/generators/saucy/features/templates/features → features/engine}/sign_up_paid.feature +0 -0
- data/{lib/generators/saucy/features/templates/features → features/engine}/trial_plans.feature +0 -0
- data/features/integration/run_features.feature +46 -0
- data/features/step_definitions/clearance_steps.rb +123 -0
- data/{lib/generators/saucy/features/templates/step_definitions → features/step_definitions/engine}/account_steps.rb +0 -0
- data/features/step_definitions/engine/braintree_steps.rb +24 -0
- data/{lib/generators/saucy/features/templates/step_definitions → features/step_definitions/engine}/country_steps.rb +0 -0
- data/{lib/generators/saucy/features/templates/step_definitions → features/step_definitions/engine}/cron_steps.rb +3 -5
- data/{lib/generators/saucy/features/templates/step_definitions → features/step_definitions/engine}/email_steps.rb +0 -0
- data/{lib/generators/saucy/features/templates/step_definitions → features/step_definitions/engine}/factory_girl_steps.rb +0 -0
- data/{lib/generators/saucy/features/templates/step_definitions → features/step_definitions/engine}/html_steps.rb +0 -0
- data/{lib/generators/saucy/features/templates/step_definitions → features/step_definitions/engine}/plan_steps.rb +0 -0
- data/{lib/generators/saucy/features/templates/step_definitions → features/step_definitions/engine}/project_steps.rb +0 -0
- data/{lib/generators/saucy/features/templates/step_definitions → features/step_definitions/engine}/session_steps.rb +0 -0
- data/{lib/generators/saucy/features/templates/step_definitions → features/step_definitions/engine}/user_steps.rb +0 -0
- data/features/step_definitions/rails_steps.rb +26 -8
- data/features/step_definitions/saucy_steps.rb +5 -0
- data/features/step_definitions/web_steps.rb +253 -0
- data/features/support/engine/braintree.rb +5 -0
- data/features/support/env.rb +18 -0
- data/features/support/factory_girl.rb +2 -0
- data/features/support/paths.rb +68 -0
- data/features/support/rails.rb +4 -0
- data/features/support/selectors.rb +11 -0
- data/lib/generators/saucy/features/features_generator.rb +7 -3
- data/lib/saucy/account.rb +55 -57
- data/lib/saucy/account_authorization.rb +39 -42
- data/lib/saucy/plan.rb +14 -16
- data/lib/saucy/project.rb +42 -44
- data/lib/saucy/projects_controller.rb +53 -55
- data/lib/saucy/subscription.rb +189 -191
- data/lib/saucy/user.rb +5 -7
- data/lib/saucy/version.rb +1 -1
- data/saucy.gemspec +21 -5
- data/spec/controllers/accounts_controller_spec.rb +2 -1
- data/spec/controllers/projects_controller_spec.rb +1 -1
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/assets/javascripts/application.js +9 -0
- data/spec/dummy/app/assets/stylesheets/application.css +7 -0
- data/spec/dummy/app/controllers/application_controller.rb +5 -0
- data/spec/dummy/app/controllers/homes_controller.rb +2 -0
- data/spec/dummy/app/controllers/projects_controller.rb +3 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/.gitkeep +0 -0
- data/spec/dummy/app/models/.gitkeep +0 -0
- data/spec/dummy/app/models/account.rb +3 -0
- data/spec/dummy/app/models/plan.rb +3 -0
- data/spec/dummy/app/models/project.rb +3 -0
- data/spec/dummy/app/models/user.rb +4 -0
- data/spec/dummy/app/views/homes/show.html.erb +1 -0
- data/spec/dummy/app/views/layouts/application.html.erb +24 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +48 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +30 -0
- data/spec/dummy/config/environments/production.rb +60 -0
- data/spec/dummy/config/environments/test.rb +42 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/clearance.rb +3 -0
- data/spec/dummy/config/initializers/inflections.rb +10 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +59 -0
- data/spec/dummy/db/migrate/20120201000751_create_diesel_clearance_users.rb +19 -0
- data/spec/dummy/db/migrate/20120201001546_create_saucy_tables.rb +122 -0
- data/spec/dummy/db/migrate/20120201001548_create_canceled_accounts.rb +14 -0
- data/spec/dummy/lib/assets/.gitkeep +0 -0
- data/spec/dummy/log/.gitkeep +0 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +26 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/mailers/billing_mailer_spec.rb +12 -2
- data/spec/models/canceled_account_spec.rb +8 -6
- data/spec/models/subscription_spec.rb +47 -53
- data/spec/spec_helper.rb +8 -11
- data/spec/support/braintree.rb +2 -4
- data/spec/support/factories.rb +1 -0
- metadata +447 -125
- data/db/migrate/20120206155222_add_subscription_token_to_canceled_accounts.rb +0 -5
- data/features/run_features.feature +0 -84
- data/lib/generators/saucy/features/templates/step_definitions/braintree_steps.rb +0 -25
- data/lib/generators/saucy/features/templates/support/braintree.rb +0 -5
- data/lib/generators/saucy/specs/specs_generator.rb +0 -20
- data/lib/generators/saucy/specs/templates/support/braintree.rb +0 -5
- data/lib/saucy/fake_braintree.rb +0 -134
- data/lib/saucy/routing_extensions.rb +0 -116
- data/spec/dummy/db/migrate/20120206155222_add_subscription_token_to_canceled_accounts.rb +0 -5
- data/spec/environment.rb +0 -98
- data/spec/route_extensions_spec.rb +0 -51
- data/spec/scaffold/config/database.yml +0 -6
- data/spec/scaffold/config/routes.rb +0 -5
- data/spec/scaffold/views/layouts/application.html.erb +0 -1
@@ -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,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.
|
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"
|
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
|
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
|
-
|
399
|
-
|
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.
|
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.
|
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
|
-
|
514
|
-
|
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
|
-
|
593
|
-
|
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
|
-
|
618
|
-
|
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
|
-
|
644
|
-
|
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 => "")
|