Created
October 29, 2018 12:28
-
-
Save cored/ac235ee4e0d8ba9936ce9cbacd418776 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# frozen_string_literal: true | |
require "rails_helper" | |
RSpec.describe Groupments::SwapForBundleProducts do | |
subject(:shipments_out) do | |
described_class.call(shipments_in, decision_matrix_class) | |
end | |
let(:product_a) { create(:product, sku: "SKUA") } | |
let(:product_b) { create(:product, sku: "SKUB") } | |
let(:product_c) { create(:product, sku: "SKUC", variation_count: 1) } | |
let(:warehouse) { create(:warehouse) } | |
let(:user) {create(:user) } | |
let(:bundle_variation) { product_c.variations.first } | |
let(:inventory_totals) { "2" } | |
let(:decision_matrix_class) do | |
class_double("WarehouseAssignment::DecisionMatrix", new: decision_matrix).as_null_object | |
end | |
let(:decision_matrix) do | |
instance_double("WarehouseAssignment::DecisionMatrix", table: [matrix_option]) | |
end | |
let(:matrix_option) do | |
WarehouseAssignment::MatrixOption.new( | |
warehouse: nil, | |
carrier: nil, | |
carrier_service_option: nil, | |
rate: 100, | |
date: Date.parse(complaince_date), | |
ship_date: Date.parse("2018-10-26"), | |
in_stock: true, | |
cheapest: true, | |
fastest: false, | |
) | |
end | |
let(:complaince_date) { "2018-10-29" } | |
before do | |
::Inventory::Update::SetTotals.new( | |
{ | |
warehouse_id: warehouse.id, | |
user_id: user.id, | |
variations_new_totals: { | |
bundle_variation.id => {new_total: inventory_totals}, | |
}, | |
} | |
).create_inventory_movements_to_realize_totals | |
allow(Datadog::IncrementMetric).to receive(:call) | |
Product.create_product_bundling!( | |
bundle_sku: product_c.sku, | |
component_skus: [product_a.sku, product_b.sku] | |
) | |
allow(FeatureGuard).to receive(:bundling_enabled?).and_return(true) | |
end | |
context "when there are no shipments containing bundleable products" do | |
let(:shipments_in) { [build(:shipment, products: [product])] } | |
let(:product) { build(:product) } | |
let(:complaince_date) { "2018-10-30" } | |
it "returns the shipments unchanged" do | |
expect(shipments_out).to match_array(shipments_in) | |
expect(shipments_out.first.products).to match_array(product) | |
end | |
it "does not increment the Swaps-Considered and -Completed counts" do | |
shipments_out | |
expect(Datadog::IncrementMetric).not_to have_received(:call) | |
.with(key: described_class::METRIC_PRODUCT_BUNDLE_SWAP_CONSIDERED, tags: []) | |
expect(Datadog::IncrementMetric).not_to have_received(:call) | |
.with(key: described_class::METRIC_PRODUCT_BUNDLE_SWAP_COMPLETED, tags: []) | |
end | |
end | |
context "when there are shipments that have bundleable products" do | |
let(:shipments_in) { [shipment1, shipment2] } | |
let(:shipment1) { build(:shipment, products: [product_a]) } | |
let(:shipment2) { build(:shipment, products: [product_b]) } | |
it "returns the bundling product instead of the bundleable ones" do | |
expect(shipments_out.first.products).to match_array(product_c) | |
end | |
it "does not return the unused shipment" do | |
expect(shipments_out.count).to eq 1 | |
end | |
it "records 1 swap was made from 1 possible swap" do | |
shipments_out | |
expect(Datadog::IncrementMetric).to have_received(:call) | |
.with(key: described_class::METRIC_PRODUCT_BUNDLE_SWAP_CONSIDERED, tags: []) | |
.once | |
expect(Datadog::IncrementMetric).to have_received(:call) | |
.with(key: described_class::METRIC_PRODUCT_BUNDLE_SWAP_COMPLETED, tags: []) | |
.once | |
end | |
context "with the shipment items in reverse order" do | |
let(:shipments_in) { [shipment2, shipment1] } | |
it "still swaps in the bundling product" do | |
expect(shipments_out.first.products).to match_array(product_c) | |
end | |
end | |
context "with different shipping preferences" do | |
let(:shipment1) { build(:shipment, shipping_preference: "parcel", products: [product_a]) } | |
let(:shipment2) { build(:shipment, shipping_preference: "courier", products: [product_b]) } | |
it "does not swap for the bundling product" do | |
expect(shipments_out).to match_array(shipments_in) | |
expect(products_skus_in_shipments(shipments_out)).to match_array( | |
[ | |
[product_a.sku], | |
[product_b.sku], | |
] | |
) | |
end | |
end | |
context "with only part of a bundle" do | |
let(:shipments_in) { [shipment1] } | |
it "does not return the bundling product" do | |
expect(shipments_out.first.products).to match_array(product_a) | |
end | |
end | |
context "with the feature flag off" do | |
before { allow(FeatureGuard).to receive(:bundling_enabled?).and_return(false) } | |
it "returns the shipments with the unbundled products" do | |
expect(shipments_out).to match_array(shipments_in) | |
expect(products_skus_in_shipments(shipments_out)).to match_array( | |
[ | |
[product_a.sku], | |
[product_b.sku], | |
] | |
) | |
end | |
end | |
end | |
context "when there are empty shipments" do | |
let(:shipments_in) { [empty_shipment] } | |
let(:empty_shipment) { build :shipment, products: [] } | |
it "just passes the empty shipments through" do | |
expect(shipments_out).to include empty_shipment | |
end | |
context "when a swap occurs" do | |
let(:shipments_in) { [empty_shipment, shipment1, shipment2] } | |
let(:shipment1) { build(:shipment, products: [product_a]) } | |
let(:shipment2) { build(:shipment, products: [product_b]) } | |
it "still just passes the empty shipments through" do | |
expect(shipments_out).to include empty_shipment | |
end | |
end | |
end | |
context "when there are shipments containing split variations" do | |
let(:shipments_in) { [split_variation_shipment] } | |
let(:split_variation_shipment) do | |
build( | |
:shipment, | |
products: [split_variation_product] | |
) | |
end | |
let(:split_variation_product) { build(:adjustable_bedframe_product) } | |
it "passes the shipments through unchanged" do | |
expect(shipments_out).to match_array(shipments_in) | |
expected = shipments_out.map do |s| | |
s.shipment_items.map{ |si| [si.product.sku, si.requested_variation_sku] } | |
end | |
expect(expected).to match_array( | |
[split_variation_product.variations.map{|v| [v.product.sku, v.sku]}] | |
) | |
end | |
end | |
context "when there are multiple sets of bundleable products" do | |
let(:shipments_in) { [shipment1, shipment2, shipment3, shipment4] } | |
let(:shipment1) { build(:shipment, products: [product_a]) } | |
let(:shipment2) { build(:shipment, products: [product_b]) } | |
let(:shipment3) { build(:shipment, products: [product_a]) } | |
let(:shipment4) { build(:shipment, products: [product_b]) } | |
it "swaps each set of the bundle for the bundling product" do | |
expect(products_skus_in_shipments(shipments_out)).to match_array( | |
[ | |
[product_c.sku], | |
[product_c.sku], | |
] | |
) | |
end | |
it "records 2 swaps were made from 2 possible swaps" do | |
shipments_out | |
expect(Datadog::IncrementMetric).to have_received(:call) | |
.with(key: described_class::METRIC_PRODUCT_BUNDLE_SWAP_CONSIDERED, tags: []) | |
.twice | |
expect(Datadog::IncrementMetric).to have_received(:call) | |
.with(key: described_class::METRIC_PRODUCT_BUNDLE_SWAP_COMPLETED, tags: []) | |
.twice | |
end | |
context "with one extra component of a bundle that can't be bundled" do | |
let(:shipments_in) { [shipment1, shipment2, shipment3] } | |
let(:shipment1) { build(:shipment, products: [product_a]) } | |
let(:shipment2) { build(:shipment, products: [product_b]) } | |
let(:shipment3) { build(:shipment, products: [product_a]) } | |
it "swaps only complete sets of the bundle for the bundling product" do | |
expect(products_skus_in_shipments(shipments_out)).to match_array( | |
[ | |
[product_c.sku], | |
[product_a.sku], | |
] | |
) | |
end | |
it "records 1 swap was made from 1 possible swap" do | |
shipments_out | |
expect(Datadog::IncrementMetric).to have_received(:call) | |
.with(key: described_class::METRIC_PRODUCT_BUNDLE_SWAP_CONSIDERED, tags: []) | |
.once | |
expect(Datadog::IncrementMetric).to have_received(:call) | |
.with(key: described_class::METRIC_PRODUCT_BUNDLE_SWAP_COMPLETED, tags: []) | |
.once | |
end | |
end | |
end | |
context "when a shipment has an unrelated product in addition to a bundleable product" do | |
let(:shipments_in) { [shipment1, shipment2] } | |
let(:shipment1) { build(:shipment, products: [product_a, product_e]) } | |
let(:shipment2) { build(:shipment, products: [product_b]) } | |
let(:product_e) { create :product, sku: "SKUE" } | |
it "returns the bundling product alongside the unrelated product" do | |
expect(products_skus_in_shipments(shipments_out)).to contain_exactly( | |
contain_exactly(product_c.sku, product_e.sku) | |
) | |
end | |
context "with products in different order" do | |
let(:shipment1) { build(:shipment, products: [product_e, product_a]) } | |
it "returns the bundling product alongside the unrelated product" do | |
expect(products_skus_in_shipments(shipments_out)).to contain_exactly( | |
contain_exactly(product_c.sku, product_e.sku) | |
) | |
end | |
end | |
end | |
context "when parts of a bundleable product come in on the same shipment" do | |
let(:shipments_in) { [shipment1, shipment2, shipment3] } | |
let(:shipment1) { build(:shipment, products: [product_a, product_a]) } | |
let(:shipment2) { build(:shipment, products: [product_b]) } | |
let(:shipment3) { build(:shipment, products: [product_b]) } | |
it "returns the bundling products in separate shipments" do | |
expect(products_skus_in_shipments(shipments_out)).to contain_exactly( | |
[product_c.sku], | |
[product_c.sku], | |
) | |
end | |
end | |
context "when bundleable products come in all on the same shipment with corresponding empty shipments" do | |
let(:shipments_in) { [shipment1, shipment2, shipment3, shipment4] } | |
let(:shipment1) { build(:shipment, products: [product_a, product_b, product_a, product_b]) } | |
let(:shipment2) { build(:shipment, products: []) } | |
let(:shipment3) { build(:shipment, products: []) } | |
let(:shipment4) { build(:shipment, products: []) } | |
let(:product_e) { create :product, sku: "SKUE" } | |
it "returns the bundling product in one shipment, while passing through any empty shipments" do | |
expect(products_skus_in_shipments(shipments_out)).to contain_exactly( | |
[product_c.sku, product_c.sku], | |
[], | |
[], | |
[], | |
) | |
end | |
end | |
context "when one component of a bundle shows up multiple times" do | |
let(:shipments_in) { [shipment1, shipment2] } | |
let(:shipment1) { build(:shipment, products: [product_a]) } | |
let(:shipment2) { build(:shipment, products: [product_a]) } | |
it "does not change the contents of the shipments in" do | |
expect(products_skus_in_shipments(shipments_out)).to match_array( | |
[ | |
[product_a.sku], | |
[product_a.sku], | |
] | |
) | |
end | |
end | |
context "when the potential product bundle is in stock" do | |
let(:shipments_in) { [shipment1, shipment2] } | |
let(:shipment1) { build(:shipment, products: [product_a]) } | |
let(:shipment2) { build(:shipment, products: [product_b]) } | |
it "returns a shipment with the in stock product bundle" do | |
expect(products_skus_in_shipments(shipments_out)).to match_array( | |
[ | |
[product_c.sku], | |
] | |
) | |
end | |
context "when there are several shipments with one component of a bundle" do | |
let(:shipments_in) { [shipment1, shipment2, shipment3, shipment4] } | |
let(:shipment1) { build(:shipment, products: [product_a]) } | |
let(:shipment2) { build(:shipment, products: [product_a]) } | |
let(:shipment3) { build(:shipment, products: [product_a]) } | |
let(:shipment4) { build(:shipment, products: [product_b]) } | |
it "returns a shipment with the in stock product and two shipments with each individual products" do | |
expect(products_skus_in_shipments(shipments_out)).to match_array( | |
[ | |
[product_c.sku], | |
[product_a.sku], | |
[product_a.sku], | |
] | |
) | |
end | |
end | |
context "when there are two componsable shipment bundles in 4 shipments" do | |
let(:shipments_in) { [shipment1, shipment2, shipment3, shipment4] } | |
let(:shipment1) { build(:shipment, products: [product_a]) } | |
let(:shipment2) { build(:shipment, products: [product_b]) } | |
let(:shipment3) { build(:shipment, products: [product_a]) } | |
let(:shipment4) { build(:shipment, products: [product_b]) } | |
let(:inventory_totals) { "2" } | |
it "returns two shipment bundles" do | |
expect(products_skus_in_shipments(shipments_out)).to match_array( | |
[ | |
[product_c.sku], | |
[product_c.sku], | |
] | |
) | |
end | |
end | |
context "when several product bundles come in the order but we fullfill a leaser amount" do | |
let(:shipments_in) { [shipment1, shipment2, shipment3, shipment4] } | |
let(:shipment1) { build(:shipment, products: [product_a]) } | |
let(:shipment2) { build(:shipment, products: [product_b]) } | |
let(:shipment3) { build(:shipment, products: [product_a]) } | |
let(:shipment4) { build(:shipment, products: [product_b]) } | |
let(:inventory_totals) { "1" } | |
it "returns one shipment bundle and the rest of shipments" do | |
expect(products_skus_in_shipments(shipments_out)).to match_array( | |
[ | |
[product_a.sku], | |
[product_b.sku], | |
[product_a.sku], | |
[product_b.sku], | |
] | |
) | |
end | |
end | |
end | |
context "when product bundles are not in stock in any warehouse" do | |
let(:shipments_in) { [shipment1, shipment2] } | |
let(:shipment1) { build(:shipment, products: [product_a]) } | |
let(:shipment2) { build(:shipment, products: [product_b]) } | |
let(:inventory_totals) { "0" } | |
it "returns the shipments with the component bundles" do | |
expect(products_skus_in_shipments(shipments_out)).to match_array( | |
[ | |
[product_a.sku], | |
[product_b.sku], | |
] | |
) | |
end | |
end | |
context "when one shipment product bundle cant be deliver in 3 days" do | |
let(:shipments_in) { [shipment1, shipment2] } | |
let(:shipment1) { build(:shipment, products: [product_a]) } | |
let(:shipment2) { build(:shipment, products: [product_b]) } | |
let(:inventory_totals) { "1" } | |
before do | |
allow(decision_matrix).to receive(:table).and_return([matrix_option]) | |
end | |
it "returns the shipments with the component bundles" do | |
expect(products_skus_in_shipments(shipments_out)).to match_array( | |
[ | |
[product_a.sku], | |
[product_b.sku], | |
] | |
) | |
end | |
end | |
end | |
def products_skus_in_shipments(shipments) | |
shipments.map{ |shipment| shipment.products.map(&:sku) } | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment