Files
medusa-store/packages/medusa/src/services/__tests__/discount.js
T
Oli Juhlandcarlos-r-l-rodrigues d1e298f5dc chore: Remove FlagRouter from core (#4710)
* Allow nested feature flags

* clean up

* Create loud-wombats-shave.md

* add warning

* Add more tests

* Address PR comments

* add back boolean check

* Replace core flag router with utils one

* lol - actually remove it

* Create eleven-doors-roll.md

---------

Co-authored-by: carlos-r-l-rodrigues <rodrigolr@gmail.com>
2023-08-08 13:05:50 +02:00

1339 lines
35 KiB
JavaScript

import { IdMap, MockManager, MockRepository } from "medusa-test-utils"
import { FlagRouter } from "@medusajs/utils"
import DiscountService from "../discount"
import { TotalsServiceMock } from "../__mocks__/totals"
import { newTotalsServiceMock } from "../__mocks__/new-totals"
import { In } from "typeorm"
const featureFlagRouter = new FlagRouter({})
describe("DiscountService", () => {
describe("create", () => {
const discountRepository = MockRepository({})
const discountRuleRepository = MockRepository({})
const regionService = {
retrieve: () => {
return {
id: IdMap.getId("france"),
}
},
withTransaction: function () {
return this
},
}
const discountService = new DiscountService({
manager: MockManager,
discountRepository,
discountRuleRepository,
regionService,
featureFlagRouter,
})
beforeEach(() => {
jest.clearAllMocks()
})
it("fails to create a fixed discount with multiple regions", async () => {
expect.assertions(3)
try {
await discountService.create({
code: "test",
rule: {
type: "fixed",
allocation: "total",
value: 20,
},
regions: [IdMap.getId("france"), IdMap.getId("Italy")],
})
} catch (err) {
expect(err.type).toEqual("invalid_data")
expect(err.message).toEqual("Fixed discounts can have one region")
expect(discountRepository.create).toHaveBeenCalledTimes(0)
}
})
it("fails to create a discount without regions", async () => {
const err = await discountService
.create({
code: "test",
rule: {
type: "fixed",
allocation: "total",
value: 20,
},
})
.catch((e) => e)
expect(err.type).toEqual("invalid_data")
expect(err.message).toEqual("Discount must have atleast 1 region")
expect(discountRepository.create).toHaveBeenCalledTimes(0)
})
it("successfully creates discount", async () => {
await discountService.create({
code: "test",
rule: {
type: "percentage",
allocation: "total",
value: 20,
},
regions: [IdMap.getId("france")],
})
expect(discountRuleRepository.create).toHaveBeenCalledTimes(1)
expect(discountRuleRepository.create).toHaveBeenCalledWith({
type: "percentage",
allocation: "total",
value: 20,
})
expect(discountRuleRepository.save).toHaveBeenCalledTimes(1)
expect(discountRepository.create).toHaveBeenCalledTimes(1)
expect(discountRepository.create).toHaveBeenCalledWith({
code: "test",
rule: expect.anything(),
regions: [{ id: IdMap.getId("france") }],
})
expect(discountRepository.save).toHaveBeenCalledTimes(1)
})
it("successfully creates discount with start and end dates", async () => {
await discountService.create({
code: "test",
rule: {
type: "percentage",
allocation: "total",
value: 20,
},
starts_at: new Date("03/14/2021"),
ends_at: new Date("03/15/2021"),
regions: [IdMap.getId("france")],
})
expect(discountRuleRepository.create).toHaveBeenCalledTimes(1)
expect(discountRuleRepository.create).toHaveBeenCalledWith({
type: "percentage",
allocation: "total",
value: 20,
})
expect(discountRuleRepository.save).toHaveBeenCalledTimes(1)
expect(discountRepository.create).toHaveBeenCalledTimes(1)
expect(discountRepository.create).toHaveBeenCalledWith({
code: "test",
rule: expect.anything(),
regions: [{ id: IdMap.getId("france") }],
starts_at: new Date("03/14/2021"),
ends_at: new Date("03/15/2021"),
})
expect(discountRepository.save).toHaveBeenCalledTimes(1)
})
it("successfully creates discount with start date and a valid duration", async () => {
await discountService.create({
code: "test",
rule: {
type: "percentage",
allocation: "total",
value: 20,
},
starts_at: new Date("03/14/2021"),
valid_duration: "P0Y0M1D",
regions: [IdMap.getId("france")],
})
expect(discountRuleRepository.create).toHaveBeenCalledTimes(1)
expect(discountRuleRepository.create).toHaveBeenCalledWith({
type: "percentage",
allocation: "total",
value: 20,
})
expect(discountRuleRepository.save).toHaveBeenCalledTimes(1)
expect(discountRepository.create).toHaveBeenCalledTimes(1)
expect(discountRepository.create).toHaveBeenCalledWith({
code: "test",
rule: expect.anything(),
regions: [{ id: IdMap.getId("france") }],
starts_at: new Date("03/14/2021"),
valid_duration: "P0Y0M1D",
})
expect(discountRepository.save).toHaveBeenCalledTimes(1)
})
})
describe("retrieve", () => {
const discountRepository = MockRepository({
findOne: (query) => {
if (query.where.id) {
return Promise.resolve({ id: IdMap.getId("total10") })
}
return Promise.resolve(undefined)
},
})
const discountService = new DiscountService({
manager: MockManager,
discountRepository,
featureFlagRouter,
})
beforeEach(() => {
jest.clearAllMocks()
})
it("successfully retrieves discount", async () => {
await discountService.retrieve(IdMap.getId("total10"))
expect(discountRepository.findOne).toHaveBeenCalledTimes(1)
expect(discountRepository.findOne).toHaveBeenCalledWith({
where: {
id: IdMap.getId("total10"),
},
})
})
it("throws on invalid discount id", async () => {
try {
await discountService.retrieve(IdMap.getId("invalid"))
} catch (error) {
expect(error.message).toBe(
`Discount with ${IdMap.getId("invalid")} was not found`
)
}
})
})
describe("retrieveByCode", () => {
const discountRepository = MockRepository({
findOne: (query) => {
if (query.where.code === "10%OFF") {
return Promise.resolve({ id: IdMap.getId("total10"), code: "10%OFF" })
}
if (query.where.code === "DYNAMIC") {
return Promise.resolve({ id: IdMap.getId("total10"), code: "10%OFF" })
}
return Promise.resolve(undefined)
},
})
const discountService = new DiscountService({
manager: MockManager,
discountRepository,
featureFlagRouter,
})
beforeEach(() => {
jest.clearAllMocks()
})
it("successfully finds discount by code", async () => {
await discountService.retrieveByCode("10%OFF")
expect(discountRepository.findOne).toHaveBeenCalledTimes(1)
expect(discountRepository.findOne).toHaveBeenCalledWith({
where: {
code: "10%OFF",
},
})
})
it("successfully trims, uppdercases, and finds discount by code", async () => {
await discountService.retrieveByCode(" 10%Off ")
expect(discountRepository.findOne).toHaveBeenCalledTimes(1)
expect(discountRepository.findOne).toHaveBeenCalledWith({
where: {
code: "10%OFF",
},
})
})
})
describe("listByCodes", () => {
const discountRepository = MockRepository({
find: (query) => {
if (query.where.code.value.includes("10%OFF")) {
return Promise.resolve([
{ id: IdMap.getId("total10"), code: "10%OFF" },
])
}
if (query.where.code.value.includes("DYNAMIC")) {
return Promise.resolve([
{ id: IdMap.getId("total10"), code: "10%OFF" },
])
}
return Promise.resolve([])
},
})
const discountService = new DiscountService({
manager: MockManager,
discountRepository,
featureFlagRouter,
})
beforeEach(() => {
jest.clearAllMocks()
})
it("successfully finds discount by code", async () => {
await discountService.listByCodes(["10%OFF"])
expect(discountRepository.find).toHaveBeenCalledTimes(1)
expect(discountRepository.find).toHaveBeenCalledWith({
where: {
code: In(["10%OFF"]),
},
})
})
it("successfully trims, uppdercases, and finds discount by code", async () => {
await discountService.listByCodes([" 10%Off "])
expect(discountRepository.find).toHaveBeenCalledTimes(1)
expect(discountRepository.find).toHaveBeenCalledWith({
where: {
code: In(["10%OFF"]),
},
})
})
})
describe("update", () => {
const discountRepository = MockRepository({
findOne: () =>
Promise.resolve({
id: IdMap.getId("total10"),
code: "10%OFF",
rule: { type: "fixed" },
}),
})
const discountRuleRepository = MockRepository({
create: (values) => values,
})
const regionService = {
retrieve: () => {
return {
id: IdMap.getId("france"),
}
},
}
const discountService = new DiscountService({
manager: MockManager,
discountRepository,
discountRuleRepository,
regionService,
featureFlagRouter,
})
beforeEach(() => {
jest.clearAllMocks()
})
it("fails to update a fixed discount with multiple regions", async () => {
expect.assertions(3)
try {
await discountService.update(IdMap.getId("total10"), {
code: "test",
regions: [IdMap.getId("france"), IdMap.getId("Italy")],
})
} catch (err) {
expect(err.type).toEqual("invalid_data")
expect(err.message).toEqual("Fixed discounts can have one region")
expect(discountRepository.create).toHaveBeenCalledTimes(0)
}
})
it("successfully updates discount", async () => {
await discountService.update(IdMap.getId("total10"), {
code: "test",
regions: [IdMap.getId("france")],
})
expect(discountRepository.save).toHaveBeenCalledTimes(1)
expect(discountRepository.save).toHaveBeenCalledWith({
id: IdMap.getId("total10"),
code: "TEST",
rule: { type: "fixed" },
regions: [{ id: IdMap.getId("france") }],
})
})
it("successfully updates discount rule", async () => {
await discountService.update(IdMap.getId("total10"), {
rule: { type: "fixed", value: 10, allocation: "total" },
})
expect(discountRepository.save).toHaveBeenCalledTimes(1)
expect(discountRepository.save).toHaveBeenCalledWith({
id: IdMap.getId("total10"),
code: "10%OFF",
rule: { type: "fixed", value: 10, allocation: "total" },
})
})
it("successfully updates metadata", async () => {
await discountService.update(IdMap.getId("total10"), {
metadata: { testKey: "testValue" },
})
expect(discountRepository.save).toHaveBeenCalledTimes(1)
expect(discountRepository.save).toHaveBeenCalledWith({
id: IdMap.getId("total10"),
rule: { type: "fixed" },
code: "10%OFF",
metadata: { testKey: "testValue" },
})
})
})
describe("addRegion", () => {
const discountRepository = MockRepository({
findOne: (q) => {
if (q.where.id === "fixed") {
return Promise.resolve({
id: IdMap.getId("total10"),
regions: [{ id: IdMap.getId("test-region") }],
rule: {
type: "fixed",
},
})
}
return Promise.resolve({
id: IdMap.getId("total10"),
regions: [{ id: IdMap.getId("test-region") }],
rule: {
type: "percentage",
},
})
},
})
const discountRuleRepository = MockRepository({})
const regionService = {
retrieve: () => {
return {
id: IdMap.getId("test-region-2"),
}
},
}
const discountService = new DiscountService({
manager: MockManager,
discountRepository,
discountRuleRepository,
regionService,
featureFlagRouter,
})
beforeEach(() => {
jest.clearAllMocks()
})
it("fails to add a region to a fixed discount with an existing region", async () => {
expect.assertions(3)
try {
await discountService.addRegion("fixed", IdMap.getId("test-region-2"))
} catch (err) {
expect(err.type).toEqual("invalid_data")
expect(err.message).toEqual("Fixed discounts can have one region")
expect(discountRepository.save).toHaveBeenCalledTimes(0)
}
})
it("successfully adds a region", async () => {
await discountService.addRegion(
IdMap.getId("total10"),
IdMap.getId("test-region-2")
)
expect(discountRepository.save).toHaveBeenCalledTimes(1)
expect(discountRepository.save).toHaveBeenCalledWith({
id: IdMap.getId("total10"),
regions: [
{ id: IdMap.getId("test-region") },
{ id: IdMap.getId("test-region-2") },
],
rule: {
type: "percentage",
},
})
})
it("successfully resolves if region already exists", async () => {
await discountService.addRegion(
IdMap.getId("total10"),
IdMap.getId("test-region")
)
expect(discountRepository.save).toHaveBeenCalledTimes(0)
})
})
describe("createDynamicDiscount", () => {
const discountRepository = MockRepository({
create: (d) => d,
findOne: () =>
Promise.resolve({
id: "parent",
is_dynamic: true,
rule_id: "parent_rule",
valid_duration: "P1Y",
}),
})
const discountRuleRepository = MockRepository({})
const regionService = {
retrieve: () => {
return {
id: IdMap.getId("test-region"),
}
},
}
const discountService = new DiscountService({
manager: MockManager,
discountRepository,
discountRuleRepository,
regionService,
featureFlagRouter,
})
beforeEach(() => {
jest.clearAllMocks()
})
it("successfully removes a region", async () => {
await discountService.createDynamicCode("former", {
code: "hi",
})
expect(discountRepository.save).toHaveBeenCalledTimes(1)
expect(discountRepository.save).toHaveBeenCalledWith({
is_dynamic: true,
is_disabled: false,
rule_id: "parent_rule",
parent_discount_id: "parent",
code: "HI",
usage_limit: undefined,
ends_at: expect.any(Date),
})
})
})
describe("removeRegion", () => {
const discountRepository = MockRepository({
findOne: () =>
Promise.resolve({
id: IdMap.getId("total10"),
regions: [{ id: IdMap.getId("test-region") }],
}),
})
const discountRuleRepository = MockRepository({})
const regionService = {
retrieve: () => {
return {
id: IdMap.getId("test-region"),
}
},
}
const discountService = new DiscountService({
manager: MockManager,
discountRepository,
discountRuleRepository,
regionService,
featureFlagRouter,
})
beforeEach(() => {
jest.clearAllMocks()
})
it("successfully removes a region", async () => {
await discountService.removeRegion(
IdMap.getId("total10"),
IdMap.getId("test-region")
)
expect(discountRepository.save).toHaveBeenCalledTimes(1)
expect(discountRepository.save).toHaveBeenCalledWith({
id: IdMap.getId("total10"),
regions: [],
})
})
it("successfully resolve if region does not exist", async () => {
await discountService.removeRegion(
IdMap.getId("total10"),
IdMap.getId("test-region-2")
)
expect(discountRepository.save).toHaveBeenCalledTimes(0)
})
})
describe("listAndCount", () => {
const discountRepository = MockRepository({
findAndCount: () =>
Promise.resolve([
{
id: IdMap.getId("total10"),
code: "OLITEST",
},
]),
})
const discountService = new DiscountService({
manager: MockManager,
discountRepository,
featureFlagRouter,
})
beforeEach(() => {
jest.clearAllMocks()
})
it("calls repository function with query and default config", async () => {
await discountService.listAndCount({ q: "OLI" })
expect(discountRepository.findAndCount).toHaveBeenCalledTimes(1)
expect(discountRepository.findAndCount).toHaveBeenCalledWith({
where: expect.anything(),
skip: 0,
take: 20,
order: { created_at: "DESC" },
})
})
it("calls repository function specified query", async () => {
await discountService.listAndCount(
{},
{ skip: 50, take: 50, order: { created_at: "ASC" } }
)
expect(discountRepository.findAndCount).toHaveBeenCalledTimes(1)
expect(discountRepository.findAndCount).toHaveBeenCalledWith({
where: {},
skip: 50,
take: 50,
order: { created_at: "ASC" },
})
})
})
describe("calculateDiscountForLineItem", () => {
const discountRepository = MockRepository({
findOne: ({ where }) => {
if (where.id === "disc_percentage") {
return Promise.resolve({
code: "MEDUSA",
rule: {
type: "percentage",
allocation: "total",
value: 15,
},
})
}
if (where.id === "disc_fixed_total") {
return Promise.resolve({
code: "MEDUSA",
rule: {
type: "fixed",
allocation: "total",
value: 400,
},
})
}
return Promise.resolve({
id: "disc_fixed",
code: "MEDUSA",
rule: {
type: "fixed",
allocation: "item",
value: 200,
},
})
},
})
const totalsService = {
...TotalsServiceMock,
getSubtotal: async () => {
return 1100
},
}
const newTotalsService = {
...newTotalsServiceMock,
getLineItemTotals: async () => {
return [
{
subtotal: 1100,
},
]
},
}
const discountService = new DiscountService({
manager: MockManager,
discountRepository,
totalsService,
newTotalsService,
featureFlagRouter,
})
beforeEach(() => {
jest.clearAllMocks()
})
it("correctly calculates fixed + item discount", async () => {
const adjustment = await discountService.calculateDiscountForLineItem(
"disc_fixed",
{
unit_price: 300,
quantity: 2,
allow_discounts: true,
}
)
expect(adjustment).toBe(400)
})
it("correctly calculates fixed + total discount", async () => {
let item = {
unit_price: 400,
quantity: 2,
allow_discounts: true,
}
const adjustment1 = await discountService.calculateDiscountForLineItem(
"disc_fixed_total",
item,
{
items: [item],
}
)
item = {
unit_price: 300,
quantity: 1,
allow_discounts: true,
}
const adjustment2 = await discountService.calculateDiscountForLineItem(
"disc_fixed_total",
item,
{
items: [item],
}
)
// The sum of both is equal to the expected 400
expect(adjustment1).toBeCloseTo(291, 0)
expect(adjustment2).toBeCloseTo(109, 0)
})
it("returns line item amount if discount exceeds lime item price", async () => {
const adjustment = await discountService.calculateDiscountForLineItem(
"disc_fixed",
{
unit_price: 100,
quantity: 1,
allow_discounts: true,
}
)
expect(adjustment).toBe(100)
})
it("correctly calculates percentage discount", async () => {
const adjustment = await discountService.calculateDiscountForLineItem(
"disc_percentage",
{
unit_price: 400,
quantity: 2,
allow_discounts: true,
}
)
expect(adjustment).toBe(120)
})
it("returns full amount if exceeds total line item amount", async () => {
const adjustment = await discountService.calculateDiscountForLineItem(
"disc_fixed",
{
unit_price: 50,
quantity: 2,
allow_discounts: true,
}
)
expect(adjustment).toBe(100)
})
it("returns early if discounts are not allowed", async () => {
const adjustment = await discountService.calculateDiscountForLineItem(
"disc_percentage",
{
unit_price: 400,
quantity: 2,
allow_discounts: false,
}
)
expect(adjustment).toBe(0)
})
})
describe("validateDiscountForCartOrThrow", () => {
const discount = { code: "TEST" }
const getCart = (id) => {
if (id === "with-d") {
return {
id: "cart",
discounts: [
{
code: "1234",
rule: {
type: "fixed",
},
},
{
code: "FS1234",
rule: {
type: "free_shipping",
},
},
],
region_id: "good",
items: [
{
id: "li1",
quantity: 2,
unit_price: 1000,
},
{
id: "li2",
quantity: 1,
unit_price: 500,
},
],
}
}
if (id === "with-d-and-customer") {
return {
id: "with-d-and-customer",
discounts: [
{
code: "ApplicableForCustomer",
rule: {
type: "fixed",
},
},
],
region_id: "good",
customer_id: "test-customer",
}
}
return {
id: "cart",
discounts: [
{
code: "CODE",
rule: {
type: "percentage",
},
},
],
region_id: "good",
items: [
{
id: "li1",
quantity: 2,
unit_price: 1000,
},
{
id: "li2",
quantity: 1,
unit_price: 500,
},
],
}
}
let discountService
beforeEach(async () => {
discountService = new DiscountService({
manager: MockManager,
featureFlagRouter,
})
const hasReachedLimitMock = jest.fn().mockImplementation(() => false)
const isDisabledMock = jest.fn().mockImplementation(() => false)
const isValidForRegionMock = jest
.fn()
.mockImplementation(() => Promise.resolve(true))
const canApplyForCustomerMock = jest.fn().mockImplementation(() => {
return Promise.resolve(true)
})
discountService.hasReachedLimit = hasReachedLimitMock
discountService.isDisabled = isDisabledMock
discountService.canApplyForCustomer = canApplyForCustomerMock
discountService.isValidForRegion = isValidForRegionMock
})
it("calls all validation methods when cart includes a customer_id and doesn't throw", async () => {
const discount = {
id: "10off",
code: "10%OFF",
regions: [{ id: "good" }],
rule: {
id: "10off-rule",
type: "percentage",
},
}
const cart = getCart("with-d-and-customer")
const result = await discountService.validateDiscountForCartOrThrow(
cart,
discount
)
expect(result).toBeUndefined()
expect(discountService.hasReachedLimit).toHaveBeenCalledTimes(1)
expect(discountService.hasReachedLimit).toHaveBeenCalledWith(discount)
expect(discountService.isDisabled).toHaveBeenCalledTimes(1)
expect(discountService.isDisabled).toHaveBeenCalledWith(discount)
expect(discountService.isValidForRegion).toHaveBeenCalledTimes(1)
expect(discountService.isValidForRegion).toHaveBeenCalledWith(
discount,
cart.region_id
)
expect(discountService.canApplyForCustomer).toHaveBeenCalledTimes(1)
expect(discountService.canApplyForCustomer).toHaveBeenCalledWith(
discount.rule.id,
cart.customer_id
)
})
it("throws when hasReachedLimit returns true", async () => {
const cart = getCart("with-d-and-customer")
discountService.hasReachedLimit = jest.fn().mockImplementation(() => true)
expect(
discountService.validateDiscountForCartOrThrow(cart, discount)
).rejects.toThrow({
message: `Discount ${discount.code} has been used maximum allowed times`,
})
})
it("throws when hasNotStarted returns true", async () => {
const cart = getCart("with-d-and-customer")
discountService.hasNotStarted = jest.fn().mockImplementation(() => true)
expect(
discountService.validateDiscountForCartOrThrow(cart, discount)
).rejects.toThrow({
message: `Discount ${discount.code} is not valid yet`,
})
})
it("throws when hasExpired returns true", async () => {
const cart = getCart("with-d-and-customer")
discountService.hasExpired = jest.fn().mockImplementation(() => true)
expect(
discountService.validateDiscountForCartOrThrow(cart, discount)
).rejects.toThrow({
message: `Discount ${discount.code} is expired`,
})
})
it("throws when isDisabled returns true", async () => {
const cart = getCart("with-d-and-customer")
discountService.isDisabled = jest.fn().mockImplementation(() => true)
expect(
discountService.validateDiscountForCartOrThrow(cart, discount)
).rejects.toThrow({
message: `The discount code ${discount.code} is disabled`,
})
})
it("throws when isValidForRegion returns false", async () => {
const discount = {}
const cart = getCart("with-d-and-customer")
discountService.isValidForRegion = jest
.fn()
.mockImplementation(() => Promise.resolve(false))
expect(
discountService.validateDiscountForCartOrThrow(cart, discount)
).rejects.toThrow({
message: "The discount is not available in current region",
})
})
it("throws when canApplyForCustomer returns false", async () => {
const discount_ = { code: discount.code, rule: { id: "" } }
const cart = getCart("with-d-and-customer")
discountService.canApplyForCustomer = jest
.fn()
.mockImplementation(() => Promise.resolve(false))
expect(
discountService.validateDiscountForCartOrThrow(cart, discount_)
).rejects.toThrow({
message: `Discount ${discount.code} is not valid for customer`,
})
})
})
describe("hasReachedLimit", () => {
const discountService = new DiscountService({
featureFlagRouter,
})
it("returns true if discount limit is reached", () => {
const discount = {
id: "limit-reached",
code: "limit-reached",
regions: [{ id: "good" }],
rule: {},
usage_count: 2,
usage_limit: 2,
}
const hasReachedLimit = discountService.hasReachedLimit(discount)
expect(hasReachedLimit).toBe(true)
})
it("returns false if discount limit is not reached", () => {
const discount = {
id: "limit-reached",
code: "limit-reached",
regions: [{ id: "good" }],
rule: {},
usage_count: 1,
usage_limit: 100,
}
const hasReachedLimit = discountService.hasReachedLimit(discount)
expect(hasReachedLimit).toBe(false)
})
it("returns false if discount limit is not set", () => {
const discount = {
id: "limit-reached",
code: "limit-reached",
regions: [{ id: "good" }],
rule: {},
usage_count: 1,
}
const hasReachedLimit = discountService.hasReachedLimit(discount)
expect(hasReachedLimit).toBe(false)
})
})
describe("isDisabled", () => {
const discountService = new DiscountService({
featureFlagRouter,
})
it("returns false if discount not disabled", async () => {
const discount = {
id: "10off",
code: "10%OFF",
regions: [{ id: "good" }],
rule: {
id: "10off-rule",
type: "percentage",
},
is_disabled: false,
}
const isDisabled = discountService.isDisabled(discount)
expect(isDisabled).toBe(false)
})
it("returns true if discount is disabled", async () => {
const discount = {
id: "10off",
code: "10%OFF",
regions: [{ id: "good" }],
rule: {
id: "10off-rule",
type: "percentage",
},
is_disabled: true,
}
const isDisabled = discountService.isDisabled(discount)
expect(isDisabled).toBe(true)
})
})
describe("hasNotStarted", () => {
const discountService = new DiscountService({
featureFlagRouter,
})
it("returns true if discount has a future starts_at date", async () => {
const discount = {
id: "10off",
code: "10%OFF",
regions: [{ id: "good" }],
rule: {
type: "percentage",
},
starts_at: getOffsetDate(1),
ends_at: getOffsetDate(10),
}
const hasNotStarted = discountService.hasNotStarted(discount)
expect(hasNotStarted).toBe(true)
})
it("returns false if discount has a past starts_at date", async () => {
const discount = {
id: "10off",
code: "10%OFF",
regions: [{ id: "good" }],
rule: {
type: "percentage",
},
starts_at: getOffsetDate(-2),
ends_at: getOffsetDate(10),
}
const hasNotStarted = discountService.hasNotStarted(discount)
expect(hasNotStarted).toBe(false)
})
})
describe("hasExpired", () => {
const discountService = new DiscountService({
featureFlagRouter,
})
it("returns false if discount has a future ends_at date", async () => {
const discount = {
id: "10off",
code: "10%OFF",
regions: [{ id: "good" }],
rule: {
type: "percentage",
},
ends_at: getOffsetDate(10),
starts_at: getOffsetDate(-1),
}
const hasExpired = discountService.hasExpired(discount)
expect(hasExpired).toBe(false)
})
it("returns true if discount has a past ends_at date", async () => {
const discount = {
id: "10off",
code: "10%OFF",
regions: [{ id: "good" }],
rule: {
type: "percentage",
},
starts_at: getOffsetDate(-10),
ends_at: getOffsetDate(-1),
}
const hasExpired = discountService.hasExpired(discount)
expect(hasExpired).toBe(true)
})
})
describe("isValidForRegion", () => {
const retrieveMock = jest.fn().mockImplementation((id) => {
if (id === "parent-discount-us") {
return Promise.resolve({
id,
regions: [{ id: "us" }],
rule: {
id: "10off-rule",
type: "percentage",
},
})
} else if (id === "parent-discount-dk") {
return Promise.resolve({
id,
regions: [{ id: "dk" }],
rule: {
id: "10off-rule",
type: "percentage",
},
})
}
})
const discountService = new DiscountService({
manager: MockManager,
featureFlagRouter,
})
discountService.retrieve = retrieveMock
beforeEach(() => {
jest.clearAllMocks()
})
it("returns false if discount is not available in a given region", async () => {
const discount = {
id: "10off",
code: "10%OFF",
regions: [{ id: "us" }],
rule: {
id: "10off-rule",
type: "percentage",
},
}
const isValidForRegion = await discountService.isValidForRegion(
discount,
"dk"
)
expect(retrieveMock).toBeCalledTimes(0)
expect(isValidForRegion).toBe(false)
})
it("returns true if discount is available in a given region", async () => {
const discount = {
id: "10off",
code: "10%OFF",
regions: [{ id: "us" }],
rule: {
id: "10off-rule",
type: "percentage",
},
}
const isValidForRegion = await discountService.isValidForRegion(
discount,
"us"
)
expect(retrieveMock).toBeCalledTimes(0)
expect(isValidForRegion).toBe(true)
})
it("returns false if discount has a parent discount and is not available in region", async () => {
const discount = {
id: "10off",
code: "10%OFF",
parent_discount_id: "parent-discount-us",
}
const isValidForRegion = await discountService.isValidForRegion(
discount,
"dk"
)
expect(retrieveMock).toBeCalledTimes(1)
expect(retrieveMock).toBeCalledWith(discount.parent_discount_id, {
relations: ["rule", "regions"],
})
expect(isValidForRegion).toBe(false)
})
it("returns true if discount has a parent discount and is available in region", async () => {
const discount = {
id: "10off",
code: "10%OFF",
parent_discount_id: "parent-discount-dk",
}
const isValidForRegion = await discountService.isValidForRegion(
discount,
"dk"
)
expect(retrieveMock).toBeCalledTimes(1)
expect(retrieveMock).toBeCalledWith(discount.parent_discount_id, {
relations: ["rule", "regions"],
})
expect(isValidForRegion).toBe(true)
})
})
describe("canApplyForCustomer", () => {
const discountConditionRepository = {
canApplyForCustomer: jest
.fn()
.mockImplementation(() => Promise.resolve(true)),
}
const customerService = {
withTransaction: function () {
return this
},
retrieve: jest.fn().mockImplementation((id) => {
if (id === "customer-no-groups") {
return Promise.resolve({ id: "customer-no-groups" })
}
if (id === "customer-with-groups") {
return Promise.resolve({
id: "customer-with-groups",
groups: [{ id: "group-1" }],
})
}
}),
}
const discountService = new DiscountService({
manager: MockManager,
discountConditionRepository,
customerService,
featureFlagRouter,
})
it("returns false on undefined customer id", async () => {
const res = await discountService.canApplyForCustomer("rule-1")
expect(res).toBe(false)
expect(
discountConditionRepository.canApplyForCustomer
).toHaveBeenCalledTimes(0)
})
it("returns true on customer with groups", async () => {
const res = await discountService.canApplyForCustomer(
"rule-1",
"customer-with-groups"
)
expect(res).toBe(true)
expect(
discountConditionRepository.canApplyForCustomer
).toHaveBeenCalledTimes(1)
expect(
discountConditionRepository.canApplyForCustomer
).toHaveBeenCalledWith("rule-1", "customer-with-groups")
})
})
})
const getOffsetDate = (offset) => {
const date = new Date()
date.setDate(date.getDate() + offset)
return date
}