require 'spec_helper' describe Account do subject { Factory(:account) } it { should have_many(:memberships) } it { should have_many(:users).through(:memberships) } it { should have_many(:projects) } it { should belong_to(:plan) } it { should validate_uniqueness_of(:name) } it { should validate_uniqueness_of(:keyword) } it { should validate_presence_of( :name) } it { should validate_presence_of(:keyword) } it { should validate_presence_of(:plan_id) } it { should_not allow_mass_assignment_of(:id) } it { should_not allow_mass_assignment_of(:updated_at) } it { should_not allow_mass_assignment_of(:created_at) } it { should allow_mass_assignment_of(:keyword) } [nil, "", "a b", "a.b", "a%b"].each do |value| it { should_not allow_value(value).for(:keyword).with_message(/letters/i) } end ["foo", "f00", "37signals"].each do |value| it { should allow_value(value).for(:keyword) } end it "should give its keyword for to_param" do subject.to_param.should == subject.keyword end it "finds admin users" do admins = [Factory(:user), Factory(:user)] non_admin = Factory(:user) non_member = Factory(:user) admins.each do |admin| Factory(:membership, :user => admin, :account => subject, :admin => true) end Factory(:membership, :user => non_admin, :account => subject, :admin => false) result = subject.admins result.to_a.should =~ admins end it "has a member with a membership" do membership = Factory(:membership, :account => subject) should have_member(membership.user) end it "doesn't have a member without a membership" do membership = Factory(:membership, :account => subject) should_not have_member(Factory(:user)) end it "finds memberships by name" do expected = 'expected result' memberships = stub('memberships', :by_name => expected) account = Factory.stub(:account) account.stubs(:memberships => memberships) result = account.memberships_by_name result.should == expected end it "manifests braintree processor_declined errors as errors on number and doesn't save" do FakeBraintree.failures["4111111111111112"] = { "message" => "Do Not Honor", "code" => "2000", "status" => "processor_declined" } account = Factory.build(:account, :cardholder_name => "Ralph Robot", :billing_email => "ralph@example.com", :card_number => "4111111111111112", :expiration_month => 5, :expiration_year => 2012, :plan => Factory(:paid_plan)) account.save.should_not be FakeBraintree.customers.should be_empty account.persisted?.should_not be account.errors[:card_number].any? { |e| e =~ /denied/ }.should be end it "manifests braintree gateway_rejected errors as errors on number and doesn't save" do FakeBraintree.failures["4111111111111112"] = { "message" => "Gateway Rejected: cvv", "code" => "N", "status" => "gateway_rejected" } account = Factory.build(:account, :cardholder_name => "Ralph Robot", :billing_email => "ralph@example.com", :card_number => "4111111111111112", :expiration_month => 5, :expiration_year => 2012, :verification_code => 200, :plan => Factory(:paid_plan)) account.save.should_not be FakeBraintree.customers.should be_empty account.persisted?.should_not be account.errors[:verification_code].any? { |e| e =~ /did not match/ }.should be end it "manifests braintree gateway_rejected errors as errors on number and doesn't save" do FakeBraintree.failures["4111111111111111"] = { "message" => "Credit card number is invalid.", "errors" => { "customer" => { "errors" => [], "credit-card" => { "errors" => [{ "message" => "Credit card number is invalid.", "code" => 81715, "attribute" => :number }] }}}} account = Factory.build(:account, :cardholder_name => "Ralph Robot", :billing_email => "ralph@example.com", :card_number => "4111111111111111", :expiration_month => 5, :expiration_year => 2012, :verification_code => 123, :plan => Factory(:paid_plan)) account.save.should_not be FakeBraintree.customers.should be_empty account.persisted?.should_not be account.errors[:card_number].any? { |e| e =~ /is invalid/ }.should be end end describe Account, "with a paid plan" do subject do Factory(:account, :cardholder_name => "Ralph Robot", :billing_email => "ralph@example.com", :card_number => "4111111111111111", :verification_code => "123", :expiration_month => 5, :expiration_year => 2012, :plan => Factory(:paid_plan)) end it "has a customer_token" do subject.customer_token.should_not be_nil end it "has a subscription_token" do subject.subscription_token.should_not be_nil end it "has a customer" do subject.customer.should_not be_nil end it "has a credit card" do subject.credit_card.should_not be_nil end it "has a subscription" do subject.subscription.should_not be_nil end it "has a next_billing_date" do subject.next_billing_date.should_not be_nil end it "has an active subscription status" do subject.subscription_status.should == Braintree::Subscription::Status::Active end it "is not past due" do subject.past_due?.should_not be end it "creates a braintree customer, credit card, and subscription" do FakeBraintree.customers[subject.customer_token].should_not be_nil FakeBraintree.customers[subject.customer_token]["credit_cards"].first.should_not be_nil FakeBraintree.subscriptions[subject.subscription_token].should_not be_nil end it "changes the subscription when the plan is changed" do new_plan = Factory(:paid_plan, :name => "New Plan") subject.save_braintree!(:plan_id => new_plan.id) FakeBraintree.subscriptions[subject.subscription_token]["plan_id"].should == new_plan.id end it "updates the customer and credit card information when changed" do subject.save_braintree!(:billing_email => "jrobot@example.com", :cardholder_name => "Jim Robot", :card_number => "4111111111111115", :verification_code => "123", :expiration_month => 5, :expiration_year => 2013) subject.customer.email.should == "jrobot@example.com" subject.credit_card.cardholder_name.should == "Jim Robot" end end describe Account, "with a free plan" do subject do Factory(:account, :plan => Factory(:plan)) end it "has a customer_token" do subject.customer_token.should_not be_nil end it "has a customer" do subject.customer.should_not be_nil end it "doesn't have a credit_card" do subject.credit_card.should be_nil end it "doesn't have a subscription_token" do subject.subscription_token.should be_nil end it "doesn't have a subscription" do subject.subscription.should be_nil end it "creates a braintree customer" do FakeBraintree.customers[subject.customer_token].should_not be_nil end it "doesn't create a credit card, and subscription" do FakeBraintree.customers[subject.customer_token]["credit_cards"].should be_nil FakeBraintree.subscriptions[subject.subscription_token].should be_nil end it "creates a credit card, and subscription when the plan is changed to a paid plan and the billing info is supplied" do new_plan = Factory(:paid_plan, :name => "New Plan") subject.save_braintree!(:plan_id => new_plan.id, :cardholder_name => "Ralph Robot", :billing_email => "ralph@example.com", :card_number => "4111111111111111", :verification_code => "123", :expiration_month => 5, :expiration_year => 2012) FakeBraintree.customers[subject.customer_token]["credit_cards"].first.should_not be_nil FakeBraintree.subscriptions[subject.subscription_token].should_not be_nil FakeBraintree.subscriptions[subject.subscription_token]["plan_id"].should == new_plan.id subject.credit_card.should_not be_nil subject.subscription.should_not be_nil end it "doesn't create a credit card, and subscription when the plan is changed to a different free plan" do new_plan = Factory(:plan, :name => "New Plan") subject.save_braintree!(:plan_id => new_plan.id) subject.credit_card.should be_nil subject.subscription.should be_nil end end describe Account, "with a plan and limits, and other plans" do subject { Factory(:account) } before do Factory(:limit, :name => "users", :value => 1, :plan => subject.plan) Factory(:limit, :name => "projects", :value => 1, :plan => subject.plan) Factory(:limit, :name => "ssl", :value => 1, :value_type => :boolean, :plan => subject.plan) @can_switch = Factory(:plan) Factory(:limit, :name => "users", :value => 1, :plan => @can_switch) Factory(:limit, :name => "projects", :value => 1, :plan => @can_switch) Factory(:limit, :name => "ssl", :value => 0, :value_type => :boolean, :plan => @can_switch) @cannot_switch = Factory(:plan) Factory(:limit, :name => "users", :value => 0, :plan => @cannot_switch) Factory(:limit, :name => "projects", :value => 0, :plan => @cannot_switch) Factory(:limit, :name => "ssl", :value => 1, :value_type => :boolean, :plan => @cannot_switch) Factory(:membership, :account => subject) Factory(:project, :account => subject) end it "indicates whether the account can switch to another plan" do subject.can_change_plan_to?(@can_switch).should be subject.can_change_plan_to?(@cannot_switch).should_not be end end describe Account, "with a paid subscription" do subject do Factory(:account, :cardholder_name => "Ralph Robot", :billing_email => "ralph@example.com", :card_number => "4111111111111111", :verification_code => "123", :expiration_month => 5, :expiration_year => 2012, :plan => Factory(:paid_plan)) end 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 subscription = FakeBraintree.subscriptions[subject.subscription_token] subscription["status"] = Braintree::Subscription::Status::PastDue subscription["next_billing_date"] = 2.months.from_now Timecop.travel(subject.next_billing_date + 1.day) do Account.update_subscriptions! subject.reload.subscription_status.should == Braintree::Subscription::Status::PastDue subject.next_billing_date.to_s.should == subscription["next_billing_date"].to_s subject.past_due?.should be end end it "gets marked as not past due and updates its next_billing_date when the subscription is active after its billing date" do subscription = FakeBraintree.subscriptions[subject.subscription_token] subscription["status"] = Braintree::Subscription::Status::Active subscription["next_billing_date"] = 2.months.from_now FakeBraintree.transaction = { :status => Braintree::Transaction::Status::Settled, :subscription_id => subject.subscription_token } subscription["transactions"] = [FakeBraintree.generated_transaction] Timecop.travel(subject.next_billing_date + 1.day) do Account.update_subscriptions! subject.reload.subscription_status.should == Braintree::Subscription::Status::Active subject.next_billing_date.to_s.should == subscription["next_billing_date"].to_s end end it "receives a receipt email at it's billing email with transaction details" do subscription = FakeBraintree.subscriptions[subject.subscription_token] subscription["status"] = Braintree::Subscription::Status::Active subscription["next_billing_date"] = 2.months.from_now FakeBraintree.transaction = { :status => Braintree::Transaction::Status::Settled, :subscription_id => subject.subscription_token } subscription["transactions"] = [FakeBraintree.generated_transaction] Timecop.travel(subject.next_billing_date + 1.day) do ActionMailer::Base.deliveries.clear Account.update_subscriptions! ActionMailer::Base.deliveries.any? do |email| email.to == [subject.billing_email] && email.subject =~ /receipt/i end.should be end end it "receives a receipt email at it's billing email with a notice that it failed when billing didn't work" do subscription = FakeBraintree.subscriptions[subject.subscription_token] subscription["status"] = Braintree::Subscription::Status::PastDue subscription["next_billing_date"] = 2.months.from_now FakeBraintree.transaction = { :status => Braintree::Transaction::Status::Failed, :subscription_id => subject.subscription_token } subscription["transactions"] = [FakeBraintree.generated_transaction] Timecop.travel(subject.next_billing_date + 1.day) do ActionMailer::Base.deliveries.clear Account.update_subscriptions! ActionMailer::Base.deliveries.any? do |email| email.to == [subject.billing_email] && email.subject =~ /problem/i end.should be end end end