require 'spec_helper' describe SubscriptionFu::Subscription do class << self def should_build_valid_successor(expected_plan_key, start_time_instance, billing_start_time_instance) it "should be valid subscription successor" do @succ.subject.should == @sub.subject @succ.prev_subscription.should == @sub @succ.plan_key.should == expected_plan_key @succ.starts_at.should == instance_variable_get("@#{start_time_instance}") @succ.billing_starts_at.should == instance_variable_get("@#{billing_start_time_instance}") end end def should_build_valid_activation_transaction(sub_instance, trans_instance, first_sub) it "should build valid activation transaction" do sub = instance_variable_get("@#{sub_instance}") trans = instance_variable_get("@#{trans_instance}") trans.subscription.should == sub trans.initiator.should == @initiator trans.action.should == "activation" trans.status.should == "initiated" trans.identifier.should be_nil if first_sub trans.related_transactions.should be_empty else trans.related_transactions.size.should == 1 rel_trans = trans.related_transactions.first rel_trans.subscription.should == sub.prev_subscription rel_trans.initiator.should == @initiator rel_trans.action.should == "cancellation" rel_trans.status.should == "initiated" rel_trans.identifier.should be_nil end end end def should_build_free_activation_transaction it "should be activation transaction for free sub" do @trans.gateway.should == "nogw" end end def should_build_paypal_activation_transaction it "should be activation transaction for paypal sub" do @trans.gateway.should == "paypal" end end def should_activate_subscription(sub_instance) it "should activate subscription" do sub = instance_variable_get("@#{sub_instance}").reload sub.should be_activated sub.transactions.first.status.should == "complete" end end def should_cancel_previous_subscription(sub_instance) it "should cancel previous sub" do sub = instance_variable_get("@#{sub_instance}").prev_subscription.reload sub.canceled_at.should be_present sub.transactions.last.status.should == "complete" end end def should_cancel_subscription(sub_instance) it "should cancel sub" do sub = instance_variable_get("@#{sub_instance}").reload sub.canceled_at.should be_present sub.transactions.last.status.should == "complete" end end def should_have_free_activation_flow(sub_instance, first_sub) context "activation" do before { @trans = instance_variable_get("@#{sub_instance}").initiate_activation(@initiator) } should_build_valid_activation_transaction(sub_instance, :trans, first_sub) should_build_free_activation_transaction context "authorize" do before { @redirect_target = @trans.start_checkout("http://return.to", "http://cancel.to") } it("should redirect to return URL") { @redirect_target.should == "http://return.to" } it("should be pending transaction on subject") do instance_variable_get("@#{sub_instance}").subject.pending_transaction(@trans.identifier).should == @trans end context "complete" do before { mock_paypal_delete_profile("fgsga564aa") } unless first_sub before { @trans.complete } should_activate_subscription(sub_instance) should_cancel_previous_subscription(sub_instance) unless first_sub end end end end def should_have_paid_activation_flow(sub_instance, first_sub, prev_sub_is_free = false) context "activation" do before { @trans = instance_variable_get("@#{sub_instance}").initiate_activation(@initiator) } should_build_valid_activation_transaction(sub_instance, :trans, first_sub) should_build_paypal_activation_transaction context "authorization" do before do mock_paypal_express_checkout @redirect_target = @trans.start_checkout("http://return.to", "http://cancel.to") end it("should redirect to return URL") do @redirect_target.should == "https://www.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token=token123" end it("should be pending transaction on subject") do instance_variable_get("@#{sub_instance}").subject.pending_transaction("token123").should == @trans end context "complete" do before { mock_paypal_create_profile("token123", :new_profile_id => "6bvsaksd9j") } before { mock_paypal_delete_profile("fgsga564aa") } unless first_sub before { @trans.complete } should_activate_subscription(sub_instance) should_cancel_previous_subscription(sub_instance) unless first_sub end context "complete with error in cancel" do before { mock_paypal_create_profile("token123", :new_profile_id => "6bvsaksd9j") } before { mock_paypal_delete_profile_with_error("fgsga564aa") } before { @trans.complete } should_activate_subscription(sub_instance) it "should cancel previous sub with failure" do sub = instance_variable_get("@#{sub_instance}").prev_subscription.reload sub.canceled_at.should be_present sub.transactions.last.status.should == "complete" end end unless first_sub || prev_sub_is_free context "complete with error in create" do before { mock_paypal_create_profile_with_error("token123") } before { mock_paypal_delete_profile("fgsga564aa") } unless first_sub before { @trans.complete } it "should not activate subscription" do sub = instance_variable_get("@#{sub_instance}").reload sub.should_not be_activated sub.transactions.first.status.should == "failed" end it "should not cancel previous subscription" do sub = instance_variable_get("@#{sub_instance}").prev_subscription.reload sub.canceled_at.should be_blank sub.transactions.last.status.should == "aborted" end unless first_sub end end end end end it { should belong_to :subject } it { should have_many :transactions } it { should validate_presence_of :subject } it { should validate_presence_of :plan_key } %w( free profess premium basic ).each {|p| it { should allow_value(p).for(:plan_key) } } it { should_not allow_value("blah").for(:plan_key) } it { should validate_presence_of :starts_at } context "base" do before { @initiator = Factory(:initiator) } context "free subscription" do before { at_time("2010-01-10 00:00 UTC") { @sub = Factory(:subscription, :plan_key => "free") } } it("should indicate it isn't a paid subscription") { @sub.should_not be_paid_subscription } it("should return human name") do pending do @sub.human_description.should == "MyApp Free subscription for Subject, 0 JPY per month" end end it("should return no next_billing_date") { @sub.next_billing_date.should be_nil } should_have_free_activation_flow(:sub, true) context "basic successor" do before do @now = Time.now @sub.update_attribute :activated_at, @now - 53.hours at_time(@now) { @succ = @sub.subject.build_next_subscription('basic') ; @succ.save! } end should_build_valid_successor("basic", :now, :now) should_have_paid_activation_flow(:succ, false, true) end end context "basic subscription" do before { @sub = Factory(:subscription, :plan_key => "basic", :paypal_profile_id => "bg5431ddf") } it("should indicate it is a paid subscription") { @sub.should be_paid_subscription } it("should return human name") do pending do @sub.human_description.should == "MyApp Basic subscription for Subject, 1050 JPY per month" end end end context "premium subscription" do before do @now = Time.parse("2010-01-12 11:45") @sub_start = Time.parse("2010-01-10 00:00 UTC") @next_billing = Time.parse("2010-02-10 00:00 UTC") at_time(@sub_start) do @sub = Factory(:subscription, :plan_key => "premium", :paypal_profile_id => "fgsga564aa", :starts_at => @sub_start, :billing_starts_at => @sub_start, :activated_at => @sub_start) end end context "active on paypal" do before { mock_paypal_profile_details("fgsga564aa", "Active", "2010-01-10", "2010-02-10") } it("should return next_billing_date") { @sub.next_billing_date.should == @next_billing } it("should return last_billing_date") { @sub.last_billing_date.should == Time.parse("2010-01-10 00:00 UTC") } it("should return estimated_next_billing_date") { @sub.estimated_next_billing_date.should == @next_billing } it("should return end_date_when_canceled") { @sub.end_date_when_canceled.should == @next_billing } context "profess successor" do before { at_time(@now) { @succ = @sub.subject.build_next_subscription('profess'); @succ.save! } } should_build_valid_successor("profess", :now, :next_billing) should_have_paid_activation_flow(:succ, false) end context "basic successor" do before { at_time(@now) { @succ = @sub.subject.build_next_subscription('basic'); @succ.save! } } should_build_valid_successor("basic", :next_billing, :next_billing) should_have_paid_activation_flow(:succ, false) end context "free successor" do before { at_time(@now) { @succ = @sub.subject.build_next_subscription('free'); @succ.save! } } should_build_valid_successor("free", :next_billing, :next_billing) should_have_free_activation_flow(:succ, false) end end context "canceled on paypal" do before { mock_paypal_profile_details("fgsga564aa", "Cancelled", "2010-01-10", nil) } it("should return next_billing_date") { @sub.next_billing_date.should be_nil } it("should return last_billing_date") { @sub.last_billing_date.should == Time.parse("2010-01-10 00:00 UTC") } it("should return estimated_next_billing_date") { @sub.estimated_next_billing_date.should == @next_billing } it("should return end_date_when_canceled") { @sub.end_date_when_canceled.should == @next_billing } context "profess successor" do before { at_time(@now) { @succ = @sub.subject.build_next_subscription('profess'); @succ.save! } } should_build_valid_successor("profess", :now, :next_billing) end context "basic successor" do before { at_time(@now) { @succ = @sub.subject.build_next_subscription('basic'); @succ.save! } } should_build_valid_successor("basic", :next_billing, :next_billing) end context "free successor" do before { at_time(@now) { @succ = @sub.subject.build_next_subscription('free'); @succ.save! } } should_build_valid_successor("free", :next_billing, :next_billing) end context "sync" do before { mock_paypal_delete_profile_with_error("fgsga564aa") } before { at_time(@now) { @sub.sync_from_gateway! } } it "should mark subscription as cancelled" do @sub.reload.should be_canceled @sub.canceled_at.should == Time.parse("2010-02-10 00:00 UTC") @sub.cacnel_reason.should == "gwcacnel" @sub.transactions.last.status.should == "complete" end end end context "canceled on paypal, no payments made" do before { mock_paypal_profile_details("fgsga564aa", "Cancelled", nil, nil) } it("should return next_billing_date") { @sub.next_billing_date.should be_nil } it("should return last_billing_date") { @sub.last_billing_date.should be_nil } it("should return estimated_next_billing_date") { @sub.estimated_next_billing_date.should be_nil } it("should return end_date_when_canceled") { at_time(@now) { @sub.end_date_when_canceled.should == @now } } context "profess successor" do before { at_time(@now) { @succ = @sub.subject.build_next_subscription('profess'); @succ.save! } } should_build_valid_successor("profess", :now, :now) end context "basic successor" do before { at_time(@now) { @succ = @sub.subject.build_next_subscription('basic'); @succ.save! } } should_build_valid_successor("basic", :now, :now) end context "free successor" do before { at_time(@now) { @succ = @sub.subject.build_next_subscription('free'); @succ.save! } } should_build_valid_successor("free", :now, :now) end end context "canceled on our side" do before { @sub.update_attributes(:canceled_at => @next_billing, :cancel_reason => 'admin') } it("should return end_date_when_canceled") { @sub.end_date_when_canceled.should == @next_billing } context "profess successor" do before { at_time(@now) { @succ = @sub.subject.build_next_subscription('profess'); @succ.save! } } should_build_valid_successor("profess", :now, :next_billing) end context "basic successor" do before { at_time(@now) { @succ = @sub.subject.build_next_subscription('basic'); @succ.save! } } should_build_valid_successor("basic", :next_billing, :next_billing) end context "free successor" do before { at_time(@now) { @succ = @sub.subject.build_next_subscription('free'); @succ.save! } } should_build_valid_successor("free", :next_billing, :next_billing) end end end end end