Skip to content

Instantly share code, notes, and snippets.

@cored
Created October 29, 2018 12:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cored/ac235ee4e0d8ba9936ce9cbacd418776 to your computer and use it in GitHub Desktop.
Save cored/ac235ee4e0d8ba9936ce9cbacd418776 to your computer and use it in GitHub Desktop.
# 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