Files
openfoodnetwork/spec/controllers/admin/subscriptions_controller_spec.rb
2021-09-20 08:39:34 +10:00

770 lines
31 KiB
Ruby

# frozen_string_literal: true
require 'spec_helper'
describe Admin::SubscriptionsController, type: :controller do
include AuthenticationHelper
include OpenFoodNetwork::EmailHelper
describe 'index' do
let!(:user) { create(:user, enterprise_limit: 10) }
let!(:shop) { create(:distributor_enterprise, enable_subscriptions: true) }
before do
allow(controller).to receive(:spree_current_user) { user }
end
context 'html' do
let(:params) { { format: :html } }
context 'as a regular user' do
it 'redirects to unauthorized' do
get :index, params: params
expect(response).to redirect_to unauthorized_path
end
end
context 'as an enterprise user' do
before { shop.update(owner: user) }
let!(:not_enabled_shop) { create(:distributor_enterprise, owner: user) }
context "where I manage a shop that is set up for subscriptions" do
let!(:subscription) { create(:subscription, shop: shop) }
it 'renders the index page with appropriate data' do
get :index, params: params
expect(response).to render_template 'index'
expect(assigns(:collection)).to eq [] # No collection loaded
expect(assigns(:shops)).to eq [shop] # Shops are loaded
end
end
context "where I don't manage a shop that is set up for subscriptions" do
it 'renders the setup_explanation page' do
get :index, params: params
expect(response).to render_template 'setup_explanation'
expect(assigns(:collection)).to eq [] # No collection loaded
expect(assigns(:shop)).to eq shop # First SO enabled shop is loaded
end
end
end
end
context 'json' do
let(:params) { { format: :json } }
let!(:subscription) { create(:subscription, shop: shop) }
context 'as a regular user' do
it 'redirects to unauthorized' do
get :index, params: params
expect(response).to redirect_to unauthorized_path
end
end
context 'as an enterprise user' do
before { shop.update(owner: user) }
let!(:shop2) { create(:distributor_enterprise, owner: user) }
let!(:subscription2) { create(:subscription, shop: shop2) }
it 'renders the collection as json' do
get :index, params: params
json_response = JSON.parse(response.body)
expect(json_response.count).to be 2
expect(json_response.map{ |so| so['id'] }).to include subscription.id, subscription2.id
end
context "when ransack predicates are submitted" do
before { params.merge!(q: { shop_id_eq: shop2.id }) }
it "restricts the list of subscriptions" do
get :index, params: params
json_response = JSON.parse(response.body)
expect(json_response.count).to be 1
ids = json_response.map{ |so| so['id'] }
expect(ids).to include subscription2.id
expect(ids).to_not include subscription.id
end
end
end
end
end
describe 'new' do
let!(:user) { create(:user) }
let!(:shop) { create(:distributor_enterprise, owner: user) }
before do
allow(controller).to receive(:spree_current_user) { user }
end
it 'loads the preloads the necessary data' do
expect(controller).to receive(:load_form_data)
get :new, params: { subscription: { shop_id: shop.id } }
expect(assigns(:subscription)).to be_a_new Subscription
expect(assigns(:subscription).shop).to eq shop
end
end
describe 'create' do
let!(:user) { create(:user) }
let!(:shop) { create(:distributor_enterprise, owner: user) }
let!(:customer) { create(:customer, enterprise: shop) }
let!(:order_cycle) { create(:simple_order_cycle, coordinator: shop) }
let!(:schedule) { create(:schedule, order_cycles: [order_cycle]) }
let!(:payment_method) { create(:payment_method, distributors: [shop]) }
let!(:shipping_method) { create(:shipping_method, distributors: [shop]) }
let(:params) { { format: :json, subscription: { shop_id: shop.id } } }
context 'as an non-manager of the specified shop' do
before do
allow(controller).to receive(:spree_current_user) {
create(:user, enterprises: [create(:enterprise)])
}
end
it 'redirects to unauthorized' do
spree_post :create, params
expect(response).to redirect_to unauthorized_path
end
end
context 'as a manager of the specified shop' do
before do
allow(controller).to receive(:spree_current_user) { user }
end
context 'when I submit insufficient params' do
it 'returns errors' do
expect{ spree_post :create, params }.to_not change{ Subscription.count }
json_response = JSON.parse(response.body)
expect(json_response['errors'].keys).to include 'schedule', 'customer', 'payment_method',
'shipping_method', 'begins_at'
end
end
context 'when I submit params containing ids of inaccessible objects' do
# As 'user' I shouldnt be able to associate a subscription with any of these.
let(:unmanaged_enterprise) { create(:enterprise) }
let(:unmanaged_schedule) {
create(:schedule,
order_cycles: [create(:simple_order_cycle, coordinator: unmanaged_enterprise)])
}
let(:unmanaged_customer) { create(:customer, enterprise: unmanaged_enterprise) }
let(:unmanaged_payment_method) {
create(:payment_method, distributors: [unmanaged_enterprise])
}
let(:unmanaged_shipping_method) {
create(:shipping_method, distributors: [unmanaged_enterprise])
}
before do
params[:subscription].merge!(
schedule_id: unmanaged_schedule.id,
customer_id: unmanaged_customer.id,
payment_method_id: unmanaged_payment_method.id,
shipping_method_id: unmanaged_shipping_method.id,
begins_at: 2.days.ago,
ends_at: 3.weeks.ago
)
end
it 'returns errors' do
expect{ spree_post :create, params }.to_not change{ Subscription.count }
json_response = JSON.parse(response.body)
expect(json_response['errors'].keys).to include 'schedule', 'customer', 'payment_method',
'shipping_method', 'ends_at'
end
end
context 'when I submit complete params with references to accessible objects' do
let!(:address) { create(:address) }
let(:variant) { create(:variant) }
before do
params[:subscription].merge!(
schedule_id: schedule.id,
customer_id: customer.id,
payment_method_id: payment_method.id,
shipping_method_id: shipping_method.id,
begins_at: 2.days.ago,
ends_at: 3.months.from_now
)
params.merge!(
bill_address: address.attributes.except('id'),
ship_address: address.attributes.except('id'),
subscription_line_items: [{ quantity: 2, variant_id: variant.id }]
)
end
context 'where the specified variants are not available from the shop' do
it 'returns an error' do
expect{ spree_post :create, params }.to_not change{ Subscription.count }
json_response = JSON.parse(response.body)
expect(json_response['errors']['subscription_line_items']).to eq ["#{variant.product.name} - #{variant.full_name} is not available from the selected schedule"]
end
end
context 'where the specified variants are available from the shop' do
let!(:exchange) {
create(:exchange, order_cycle: order_cycle, incoming: false, receiver: shop,
variants: [variant])
}
it 'creates subscription line items for the subscription' do
expect{ spree_post :create, params }.to change{ Subscription.count }.by(1)
subscription = Subscription.last
expect(subscription.schedule).to eq schedule
expect(subscription.customer).to eq customer
expect(subscription.payment_method).to eq payment_method
expect(subscription.shipping_method).to eq shipping_method
expect(subscription.bill_address.firstname).to eq address.firstname
expect(subscription.ship_address.firstname).to eq address.firstname
expect(subscription.subscription_line_items.count).to be 1
subscription_line_item = subscription.subscription_line_items.first
expect(subscription_line_item.quantity).to be 2
expect(subscription_line_item.variant).to eq variant
end
end
end
end
end
describe 'edit' do
let!(:user) { create(:user) }
let!(:shop) { create(:distributor_enterprise, owner: user) }
let!(:customer1) { create(:customer, enterprise: shop) }
let!(:order_cycle) { create(:simple_order_cycle, coordinator: shop) }
let!(:schedule) { create(:schedule, order_cycles: [order_cycle]) }
let!(:payment_method) { create(:payment_method, distributors: [shop]) }
let!(:shipping_method) { create(:shipping_method, distributors: [shop]) }
let!(:subscription) {
create(:subscription,
shop: shop,
customer: customer1,
schedule: schedule,
payment_method: payment_method,
shipping_method: shipping_method)
}
before do
allow(controller).to receive(:spree_current_user) { user }
end
it 'loads the preloads the necessary data' do
expect(controller).to receive(:load_form_data)
get :edit, params: { id: subscription.id }
expect(assigns(:subscription)).to eq subscription
end
end
describe 'update' do
let!(:user) { create(:user) }
let!(:shop) { create(:distributor_enterprise, owner: user) }
let!(:customer) { create(:customer, enterprise: shop) }
let!(:product1) { create(:product, supplier: shop) }
let!(:variant1) {
create(:variant, product: product1, unit_value: '100', price: 12.00, option_values: [])
}
let!(:enterprise_fee) { create(:enterprise_fee, amount: 1.75) }
let!(:order_cycle) {
create(:simple_order_cycle, coordinator: shop, orders_open_at: 2.days.from_now,
orders_close_at: 7.days.from_now)
}
let!(:outgoing_exchange) {
order_cycle.exchanges.create(sender: shop, receiver: shop, variants: [variant1],
enterprise_fees: [enterprise_fee])
}
let!(:schedule) { create(:schedule, order_cycles: [order_cycle]) }
let!(:payment_method) { create(:payment_method, distributors: [shop]) }
let!(:shipping_method) { create(:shipping_method, distributors: [shop]) }
let!(:subscription) {
create(:subscription,
shop: shop,
customer: customer,
schedule: schedule,
payment_method: payment_method,
shipping_method: shipping_method,
subscription_line_items: [create(:subscription_line_item, variant: variant1,
quantity: 2)])
}
let(:subscription_line_item1) { subscription.subscription_line_items.first }
let(:params) { { format: :json, id: subscription.id, subscription: {} } }
context 'as an non-manager of the subscription shop' do
before do
allow(controller).to receive(:spree_current_user) {
create(:user, enterprises: [create(:enterprise)])
}
end
it 'redirects to unauthorized' do
spree_post :update, params
expect(response).to redirect_to unauthorized_path
end
end
context 'as a manager of the subscription shop' do
before do
allow(controller).to receive(:spree_current_user) { user }
end
context 'when I submit params containing a new customer or schedule id' do
let!(:new_customer) { create(:customer, enterprise: shop) }
let!(:new_schedule) { create(:schedule, order_cycles: [order_cycle]) }
before do
params[:subscription].merge!(schedule_id: new_schedule.id, customer_id: new_customer.id)
end
it 'does not alter customer_id or schedule_id' do
spree_post :update, params
subscription.reload
expect(subscription.customer).to eq customer
expect(subscription.schedule).to eq schedule
end
end
context 'when I submit params containing ids of inaccessible objects' do
# As 'user' I shouldnt be able to associate a subscription with any of these.
let(:unmanaged_enterprise) { create(:enterprise) }
let(:unmanaged_payment_method) {
create(:payment_method, distributors: [unmanaged_enterprise])
}
let(:unmanaged_shipping_method) {
create(:shipping_method, distributors: [unmanaged_enterprise])
}
before do
params[:subscription].merge!(
payment_method_id: unmanaged_payment_method.id,
shipping_method_id: unmanaged_shipping_method.id
)
end
it 'returns errors' do
expect{ spree_post :update, params }.to_not change{ Subscription.count }
json_response = JSON.parse(response.body)
expect(json_response['errors'].keys).to include 'payment_method', 'shipping_method'
subscription.reload
expect(subscription.payment_method).to eq payment_method
expect(subscription.shipping_method).to eq shipping_method
end
end
context 'when I submit valid params' do
let!(:new_payment_method) { create(:payment_method, distributors: [shop]) }
let!(:new_shipping_method) { create(:shipping_method, distributors: [shop]) }
before do
params[:subscription].merge!(
payment_method_id: new_payment_method.id,
shipping_method_id: new_shipping_method.id
)
end
it 'updates the subscription' do
spree_post :update, params
subscription.reload
expect(subscription.schedule).to eq schedule
expect(subscription.customer).to eq customer
expect(subscription.payment_method).to eq new_payment_method
expect(subscription.shipping_method).to eq new_shipping_method
end
context 'with subscription_line_items params' do
let!(:product2) { create(:product) }
let!(:variant2) {
create(:variant, product: product2, unit_value: '1000', price: 6.00, option_values: [])
}
before do
params[:subscription_line_items] =
[{ id: subscription_line_item1.id, quantity: 1, variant_id: variant1.id },
{ quantity: 2, variant_id: variant2.id }]
end
context 'where the specified variants are not available from the shop' do
it 'returns an error' do
expect{ spree_post :update, params }.to_not change{
subscription.subscription_line_items.count
}
json_response = JSON.parse(response.body)
expect(json_response['errors']['subscription_line_items']).to eq ["#{product2.name} - #{variant2.full_name} is not available from the selected schedule"]
end
end
context 'where the specified variants are available from the shop' do
before { outgoing_exchange.update(variants: [variant1, variant2]) }
it 'creates subscription line items for the subscription' do
expect{ spree_post :update, params }.to change{
subscription.subscription_line_items.count
}.by(1)
subscription.reload
expect(subscription.subscription_line_items.count).to be 2
subscription_line_item = subscription.subscription_line_items.last
expect(subscription_line_item.quantity).to be 2
expect(subscription_line_item.variant).to eq variant2
end
end
end
end
end
end
describe 'cancel' do
let!(:user) { create(:user, enterprise_limit: 10) }
let!(:shop) { create(:distributor_enterprise) }
let!(:order_cycle) { create(:simple_order_cycle, orders_close_at: 1.day.from_now) }
let!(:subscription) { create(:subscription, shop: shop, with_items: true) }
let!(:proxy_order) {
create(:proxy_order, subscription: subscription, order_cycle: order_cycle)
}
before do
allow(controller).to receive(:spree_current_user) { user }
end
context 'json' do
let(:params) { { format: :json, id: subscription.id } }
context 'as a regular user' do
it 'redirects to unauthorized' do
spree_put :cancel, params
expect(response).to redirect_to unauthorized_path
end
end
context 'as an enterprise user' do
context "without authorisation" do
let!(:shop2) { create(:distributor_enterprise) }
before { shop2.update(owner: user) }
it 'redirects to unauthorized' do
spree_put :cancel, params
expect(response).to redirect_to unauthorized_path
end
end
context "with authorisation" do
before { shop.update(owner: user) }
context "when at least one associated order is still 'open'" do
let(:order_cycle) { subscription.order_cycles.first }
let(:proxy_order) {
create(:proxy_order, subscription: subscription, order_cycle: order_cycle)
}
let!(:order) { proxy_order.initialise_order! }
before { break unless order.next! while !order.completed? }
context "when no 'open_orders' directive has been provided" do
it "renders an error, asking what to do" do
spree_put :cancel, params
expect(response.status).to be 409
json_response = JSON.parse(response.body)
expect(json_response['errors']['open_orders']).to eq I18n.t('admin.subscriptions.confirm_cancel_open_orders_msg')
end
end
context "when 'keep' has been provided as the 'open_orders' directive" do
before { params.merge!(open_orders: 'keep') }
it 'renders the cancelled subscription as json, and does not cancel the open order' do
spree_put :cancel, params
json_response = JSON.parse(response.body)
expect(json_response['canceled_at']).to_not be nil
expect(json_response['id']).to eq subscription.id
expect(subscription.reload.canceled_at).to be_within(5.seconds).of Time.zone.now
expect(order.reload.state).to eq 'complete'
expect(proxy_order.reload.canceled_at).to be nil
end
end
context "when 'cancel' has been provided as the 'open_orders' directive" do
let(:mail_mock) { double(:mail) }
before do
params[:open_orders] = 'cancel'
allow(Spree::OrderMailer).to receive(:cancel_email) { mail_mock }
allow(mail_mock).to receive(:deliver_later)
end
it 'renders the cancelled subscription as json, and cancels the open order' do
spree_put :cancel, params
json_response = JSON.parse(response.body)
expect(json_response['canceled_at']).to_not be nil
expect(json_response['id']).to eq subscription.id
expect(subscription.reload.canceled_at).to be_within(5.seconds).of Time.zone.now
expect(order.reload.state).to eq 'canceled'
expect(proxy_order.reload.canceled_at).to be_within(5.seconds).of Time.zone.now
expect(mail_mock).to have_received(:deliver_later)
end
end
end
context "when no associated orders are still 'open'" do
it 'renders the cancelled subscription as json' do
spree_put :cancel, params
json_response = JSON.parse(response.body)
expect(json_response['canceled_at']).to_not be nil
expect(json_response['id']).to eq subscription.id
expect(subscription.reload.canceled_at).to be_within(5.seconds).of Time.zone.now
end
end
end
end
end
end
describe 'pause' do
let!(:user) { create(:user, enterprise_limit: 10) }
let!(:shop) { create(:distributor_enterprise) }
let!(:subscription) { create(:subscription, shop: shop, with_items: true) }
before do
allow(controller).to receive(:spree_current_user) { user }
end
context 'json' do
let(:params) { { format: :json, id: subscription.id } }
context 'as a regular user' do
it 'redirects to unauthorized' do
spree_put :pause, params
expect(response).to redirect_to unauthorized_path
end
end
context 'as an enterprise user' do
context "without authorisation" do
let!(:shop2) { create(:distributor_enterprise) }
before { shop2.update(owner: user) }
it 'redirects to unauthorized' do
spree_put :pause, params
expect(response).to redirect_to unauthorized_path
end
end
context "with authorisation" do
before { shop.update(owner: user) }
context "when at least one associated order is still 'open'" do
let(:order_cycle) { subscription.order_cycles.first }
let(:proxy_order) {
create(:proxy_order, subscription: subscription, order_cycle: order_cycle)
}
let!(:order) { proxy_order.initialise_order! }
before { break unless order.next! while !order.completed? }
context "when no 'open_orders' directive has been provided" do
it "renders an error, asking what to do" do
spree_put :pause, params
expect(response.status).to be 409
json_response = JSON.parse(response.body)
expect(json_response['errors']['open_orders']).to eq I18n.t('admin.subscriptions.confirm_cancel_open_orders_msg')
end
end
context "when 'keep' has been provided as the 'open_orders' directive" do
before { params.merge!(open_orders: 'keep') }
it 'renders the paused subscription as json, and does not cancel the open order' do
spree_put :pause, params
json_response = JSON.parse(response.body)
expect(json_response['paused_at']).to_not be nil
expect(json_response['id']).to eq subscription.id
expect(subscription.reload.paused_at).to be_within(5.seconds).of Time.zone.now
expect(order.reload.state).to eq 'complete'
expect(proxy_order.reload.canceled_at).to be nil
end
end
context "when 'cancel' has been provided as the 'open_orders' directive" do
let(:mail_mock) { double(:mail) }
before do
params[:open_orders] = 'cancel'
allow(Spree::OrderMailer).to receive(:cancel_email) { mail_mock }
allow(mail_mock).to receive(:deliver_later)
end
it 'renders the paused subscription as json, and cancels the open order' do
spree_put :pause, params
json_response = JSON.parse(response.body)
expect(json_response['paused_at']).to_not be nil
expect(json_response['id']).to eq subscription.id
expect(subscription.reload.paused_at).to be_within(5.seconds).of Time.zone.now
expect(order.reload.state).to eq 'canceled'
expect(proxy_order.reload.canceled_at).to be_within(5.seconds).of Time.zone.now
expect(mail_mock).to have_received(:deliver_later)
end
end
end
context "when no associated orders are still 'open'" do
it 'renders the paused subscription as json' do
spree_put :pause, params
json_response = JSON.parse(response.body)
expect(json_response['paused_at']).to_not be nil
expect(json_response['id']).to eq subscription.id
expect(subscription.reload.paused_at).to be_within(5.seconds).of Time.zone.now
end
end
end
end
end
end
describe 'unpause' do
let!(:user) { create(:user, enterprise_limit: 10) }
let!(:shop) { create(:distributor_enterprise) }
let!(:subscription) {
create(:subscription, shop: shop, paused_at: Time.zone.now, with_items: true)
}
before do
allow(controller).to receive(:spree_current_user) { user }
end
context 'json' do
let(:params) { { format: :json, id: subscription.id, subscription: {} } }
context 'as a regular user' do
it 'redirects to unauthorized' do
spree_put :unpause, params
expect(response).to redirect_to unauthorized_path
end
end
context 'as an enterprise user' do
context "without authorisation" do
let!(:shop2) { create(:distributor_enterprise) }
before { shop2.update(owner: user) }
it 'redirects to unauthorized' do
spree_put :unpause, params
expect(response).to redirect_to unauthorized_path
end
end
context "with authorisation" do
before { shop.update(owner: user) }
context "when at least one order in an open order cycle is 'complete'" do
let(:order_cycle) { subscription.order_cycles.first }
let(:proxy_order) {
create(:proxy_order, subscription: subscription, order_cycle: order_cycle)
}
let!(:order) { proxy_order.initialise_order! }
before { break unless order.next! while !order.completed? }
context "when no associated orders are 'canceled'" do
it 'renders the unpaused subscription as json, leaves the order untouched' do
spree_put :unpause, params
json_response = JSON.parse(response.body)
expect(json_response['paused_at']).to be nil
expect(json_response['id']).to eq subscription.id
expect(subscription.reload.paused_at).to be nil
expect(order.reload.state).to eq 'complete'
expect(proxy_order.reload.canceled_at).to be nil
end
end
context "when at least one associate orders is 'canceled'" do
before do
setup_email
proxy_order.cancel
end
context "when no 'canceled_orders' directive has been provided" do
it "renders a message, informing the user that canceled order can be resumed" do
spree_put :unpause, params
expect(response.status).to be 409
json_response = JSON.parse(response.body)
expect(json_response['errors']['canceled_orders']).to eq I18n.t('admin.subscriptions.resume_canceled_orders_msg')
end
end
context "when 'notified' has been provided as the 'canceled_orders' directive" do
before { params.merge!(canceled_orders: 'notified') }
it 'renders the unpaused subscription as json, leaves the order untouched' do
spree_put :unpause, params
json_response = JSON.parse(response.body)
expect(json_response['paused_at']).to be nil
expect(json_response['id']).to eq subscription.id
expect(subscription.reload.paused_at).to be nil
expect(order.reload.state).to eq 'canceled'
expect(proxy_order.reload.canceled_at).to_not be nil
end
end
end
end
context "when no associated orders are 'complete'" do
it 'renders the unpaused subscription as json' do
spree_put :unpause, params
json_response = JSON.parse(response.body)
expect(json_response['paused_at']).to be nil
expect(json_response['id']).to eq subscription.id
expect(subscription.reload.paused_at).to be nil
end
context "when there is an open OC and no associated orders exist yet for it (OC was opened when the subscription was paused)" do
it "creates an associated order" do
spree_put :unpause, params
expect(subscription.reload.paused_at).to be nil
expect(subscription.proxy_orders.size).to be 1
end
end
end
end
end
end
end
describe "#load_form_data" do
let!(:user) { create(:user) }
let!(:shop) { create(:distributor_enterprise, owner: user) }
let!(:customer1) { create(:customer, enterprise: shop) }
let!(:customer2) { create(:customer, enterprise: shop) }
let!(:order_cycle) { create(:simple_order_cycle, coordinator: shop) }
let!(:schedule) { create(:schedule, order_cycles: [order_cycle]) }
let!(:payment_method) { create(:payment_method, distributors: [shop]) }
let!(:shipping_method) { create(:shipping_method, distributors: [shop]) }
before do
allow(controller).to receive(:spree_current_user) { user }
controller.instance_variable_set(:@subscription, Subscription.new(shop: shop))
end
it "assigns data to instance variables" do
controller.send(:load_form_data)
expect(assigns(:customers)).to include customer1, customer2
expect(assigns(:schedules)).to eq [schedule]
expect(assigns(:order_cycles)).to eq [order_cycle]
expect(assigns(:payment_methods)).to eq [payment_method]
expect(assigns(:shipping_methods)).to eq [shipping_method]
end
context "when other payment methods exist" do
let!(:stripe) { create(:stripe_connect_payment_method, distributors: [shop]) }
let!(:paypal) {
Spree::Gateway::PayPalExpress.create!(name: "PayPalExpress", distributor_ids: [shop.id])
}
let!(:bogus) { create(:bogus_payment_method, distributors: [shop]) }
it "only loads Stripe and Cash payment methods" do
controller.send(:load_form_data)
expect(assigns(:payment_methods)).to include payment_method, stripe
expect(assigns(:payment_methods)).to_not include paypal, bogus
end
end
end
end