Feat:discount expiration date (#403)
* discount expiration validation and testing * integration testing * double quotes * add iso8601 package * api testing * add joi validation of start and end dates as well as valid_duration * valid_duration column * service testing * discount validation in services * integration test with invalid date interval * include valid_duration when fetching a discount * rename variable for clarity * add test for dynamic discount with expiration date * remove debug code * adjust tests to reflect valid_duration being included in default fields * additional discount update validation * fixed failing test * set ends_at on dynamic discount creation * discount integration tests * removed unused console.log * removed validation of dynamic discounts by duration and added ends_at to dynamic discount creation * integration tests for dynamic discount with and without duration * optional valid duration for dynamic discounts * allow nullable dynamic discount durations * expect assertions * fix unit test after change to dynamic discounts without duration * change to date instead of string * add assertions * error handling * addressed feedback
This commit is contained in:
@@ -158,6 +158,193 @@ describe("/admin/discounts", () => {
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("creates a discount with start and end dates", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const response = await api
|
||||
.post(
|
||||
"/admin/discounts",
|
||||
{
|
||||
code: "HELLOWORLD",
|
||||
rule: {
|
||||
description: "test",
|
||||
type: "percentage",
|
||||
value: 10,
|
||||
allocation: "total",
|
||||
},
|
||||
usage_limit: 10,
|
||||
starts_at: new Date("09/15/2021 11:50"),
|
||||
ends_at: new Date("09/15/2021 17:50"),
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data.discount).toEqual(
|
||||
expect.objectContaining({
|
||||
code: "HELLOWORLD",
|
||||
usage_limit: 10,
|
||||
starts_at: expect.any(String),
|
||||
ends_at: expect.any(String),
|
||||
})
|
||||
)
|
||||
|
||||
expect(new Date(response.data.discount.starts_at)).toEqual(
|
||||
new Date("09/15/2021 11:50")
|
||||
)
|
||||
|
||||
expect(new Date(response.data.discount.ends_at)).toEqual(
|
||||
new Date("09/15/2021 17:50")
|
||||
)
|
||||
|
||||
const updated = await api
|
||||
.post(
|
||||
`/admin/discounts/${response.data.discount.id}`,
|
||||
{
|
||||
usage_limit: 20,
|
||||
starts_at: new Date("09/14/2021 11:50"),
|
||||
ends_at: new Date("09/17/2021 17:50"),
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
|
||||
expect(updated.status).toEqual(200)
|
||||
expect(updated.data.discount).toEqual(
|
||||
expect.objectContaining({
|
||||
code: "HELLOWORLD",
|
||||
usage_limit: 20,
|
||||
starts_at: expect.any(String),
|
||||
ends_at: expect.any(String),
|
||||
})
|
||||
)
|
||||
|
||||
expect(new Date(updated.data.discount.starts_at)).toEqual(
|
||||
new Date("09/14/2021 11:50")
|
||||
)
|
||||
|
||||
expect(new Date(updated.data.discount.ends_at)).toEqual(
|
||||
new Date("09/17/2021 17:50")
|
||||
)
|
||||
})
|
||||
|
||||
it("fails to update end date to a date before start date", async () => {
|
||||
expect.assertions(6)
|
||||
|
||||
const api = useApi()
|
||||
|
||||
const response = await api
|
||||
.post(
|
||||
"/admin/discounts",
|
||||
{
|
||||
code: "HELLOWORLD",
|
||||
rule: {
|
||||
description: "test",
|
||||
type: "percentage",
|
||||
value: 10,
|
||||
allocation: "total",
|
||||
},
|
||||
usage_limit: 10,
|
||||
starts_at: new Date("09/15/2021 11:50"),
|
||||
ends_at: new Date("09/15/2021 17:50"),
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data.discount).toEqual(
|
||||
expect.objectContaining({
|
||||
code: "HELLOWORLD",
|
||||
usage_limit: 10,
|
||||
starts_at: expect.any(String),
|
||||
ends_at: expect.any(String),
|
||||
})
|
||||
)
|
||||
|
||||
expect(new Date(response.data.discount.starts_at)).toEqual(
|
||||
new Date("09/15/2021 11:50")
|
||||
)
|
||||
|
||||
expect(new Date(response.data.discount.ends_at)).toEqual(
|
||||
new Date("09/15/2021 17:50")
|
||||
)
|
||||
|
||||
await api
|
||||
.post(
|
||||
`/admin/discounts/${response.data.discount.id}`,
|
||||
{
|
||||
usage_limit: 20,
|
||||
ends_at: new Date("09/11/2021 17:50"),
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
expect(err.response.status).toEqual(400)
|
||||
expect(err.response.data.message).toEqual(
|
||||
`"ends_at" must be greater than "starts_at"`
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it("fails to create discount with end date before start date", async () => {
|
||||
expect.assertions(2)
|
||||
const api = useApi()
|
||||
|
||||
const response = await api
|
||||
.post(
|
||||
"/admin/discounts",
|
||||
{
|
||||
code: "HELLOWORLD",
|
||||
rule: {
|
||||
description: "test",
|
||||
type: "percentage",
|
||||
value: 10,
|
||||
allocation: "total",
|
||||
},
|
||||
usage_limit: 10,
|
||||
starts_at: new Date("09/15/2021 11:50"),
|
||||
ends_at: new Date("09/14/2021 17:50"),
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
expect(err.response.status).toEqual(400)
|
||||
expect(err.response.data.message).toEqual([
|
||||
expect.objectContaining({
|
||||
message: `"ends_at" must be greater than "ref:starts_at"`,
|
||||
}),
|
||||
])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("testing for soft-deletion + uniqueness on discount codes", () => {
|
||||
@@ -286,6 +473,21 @@ describe("/admin/discounts", () => {
|
||||
is_dynamic: true,
|
||||
is_disabled: false,
|
||||
rule_id: "test-discount-rule",
|
||||
valid_duration: "P2Y",
|
||||
})
|
||||
await manager.insert(DiscountRule, {
|
||||
id: "test-discount-rule1",
|
||||
description: "Dynamic rule",
|
||||
type: "percentage",
|
||||
value: 10,
|
||||
allocation: "total",
|
||||
})
|
||||
await manager.insert(Discount, {
|
||||
id: "test-discount1",
|
||||
code: "DYNAMICCode",
|
||||
is_dynamic: true,
|
||||
is_disabled: false,
|
||||
rule_id: "test-discount-rule1",
|
||||
})
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
@@ -298,7 +500,7 @@ describe("/admin/discounts", () => {
|
||||
await db.teardown()
|
||||
})
|
||||
|
||||
it("creates a dynamic discount", async () => {
|
||||
it("creates a dynamic discount with ends_at", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const response = await api
|
||||
@@ -318,6 +520,40 @@ describe("/admin/discounts", () => {
|
||||
})
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data.discount).toEqual(
|
||||
expect.objectContaining({
|
||||
code: "HELLOWORLD",
|
||||
ends_at: expect.any(String),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("creates a dynamic discount without ends_at", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const response = await api
|
||||
.post(
|
||||
"/admin/discounts/test-discount1/dynamic-codes",
|
||||
{
|
||||
code: "HELLOWORLD",
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
// console.log(err)
|
||||
})
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data.discount).toEqual(
|
||||
expect.objectContaining({
|
||||
code: "HELLOWORLD",
|
||||
ends_at: null,
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -131,6 +131,7 @@ describe("/store/carts", () => {
|
||||
})
|
||||
|
||||
it("fails on apply discount if limit has been reached", async () => {
|
||||
expect.assertions(2)
|
||||
const api = useApi()
|
||||
|
||||
try {
|
||||
@@ -145,6 +146,62 @@ describe("/store/carts", () => {
|
||||
}
|
||||
})
|
||||
|
||||
it("fails to apply expired discount", async () => {
|
||||
expect.assertions(2)
|
||||
const api = useApi()
|
||||
|
||||
try {
|
||||
await api.post("/store/carts/test-cart", {
|
||||
discounts: [{ code: "EXP_DISC" }],
|
||||
})
|
||||
} catch (error) {
|
||||
expect(error.response.status).toEqual(400)
|
||||
expect(error.response.data.message).toEqual("Discount is expired")
|
||||
}
|
||||
})
|
||||
|
||||
it("fails on discount before start day", async () => {
|
||||
expect.assertions(2)
|
||||
const api = useApi()
|
||||
|
||||
try {
|
||||
await api.post("/store/carts/test-cart", {
|
||||
discounts: [{ code: "PREM_DISC" }],
|
||||
})
|
||||
} catch (error) {
|
||||
expect(error.response.status).toEqual(400)
|
||||
expect(error.response.data.message).toEqual("Discount is not valid yet")
|
||||
}
|
||||
})
|
||||
|
||||
it("fails on apply invalid dynamic discount", async () => {
|
||||
const api = useApi()
|
||||
|
||||
try {
|
||||
await api.post("/store/carts/test-cart", {
|
||||
discounts: [{ code: "INV_DYN_DISC" }],
|
||||
})
|
||||
} catch (error) {
|
||||
expect(error.response.status).toEqual(400)
|
||||
expect(error.response.data.message).toEqual("Discount is expired")
|
||||
}
|
||||
})
|
||||
|
||||
it("Applies dynamic discount to cart correctly", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const cart = await api.post(
|
||||
"/store/carts/test-cart",
|
||||
{
|
||||
discounts: [{ code: "DYN_DISC" }],
|
||||
},
|
||||
{ withCredentials: true }
|
||||
)
|
||||
|
||||
expect(cart.data.cart.shipping_total).toBe(1000)
|
||||
expect(cart.status).toEqual(200)
|
||||
})
|
||||
|
||||
it("updates cart customer id", async () => {
|
||||
const api = useApi()
|
||||
|
||||
@@ -425,13 +482,15 @@ describe("/store/carts", () => {
|
||||
)
|
||||
|
||||
// Add a 10% discount to the cart
|
||||
const cartWithGiftcard = await api.post(
|
||||
"/store/carts/test-cart",
|
||||
{
|
||||
discounts: [{ code: "10PERCENT" }],
|
||||
},
|
||||
{ withCredentials: true }
|
||||
)
|
||||
const cartWithGiftcard = await api
|
||||
.post(
|
||||
"/store/carts/test-cart",
|
||||
{
|
||||
discounts: [{ code: "10PERCENT" }],
|
||||
},
|
||||
{ withCredentials: true }
|
||||
)
|
||||
.catch((err) => console.log(err))
|
||||
|
||||
// Ensure that the discount is only applied to the standard item
|
||||
expect(cartWithGiftcard.data.cart.total).toBe(1900) // 1000 (giftcard) + 900 (standard item with 10% discount)
|
||||
|
||||
@@ -17,6 +17,18 @@ const {
|
||||
} = require("@medusajs/medusa")
|
||||
|
||||
module.exports = async (connection, data = {}) => {
|
||||
const yesterday = ((today) => new Date(today.setDate(today.getDate() - 1)))(
|
||||
new Date()
|
||||
)
|
||||
const tomorrow = ((today) => new Date(today.setDate(today.getDate() + 1)))(
|
||||
new Date()
|
||||
)
|
||||
const tenDaysAgo = ((today) => new Date(today.setDate(today.getDate() - 10)))(
|
||||
new Date()
|
||||
)
|
||||
const tenDaysFromToday = ((today) =>
|
||||
new Date(today.setDate(today.getDate() + 10)))(new Date())
|
||||
|
||||
const manager = connection.manager
|
||||
|
||||
const defaultProfile = await manager.findOne(ShippingProfile, {
|
||||
@@ -88,6 +100,8 @@ module.exports = async (connection, data = {}) => {
|
||||
code: "10PERCENT",
|
||||
is_dynamic: false,
|
||||
is_disabled: false,
|
||||
starts_at: tenDaysAgo,
|
||||
ends_at: tenDaysFromToday,
|
||||
})
|
||||
|
||||
tenPercent.regions = [r]
|
||||
@@ -114,6 +128,92 @@ module.exports = async (connection, data = {}) => {
|
||||
|
||||
await manager.save(d)
|
||||
|
||||
const expiredRule = manager.create(DiscountRule, {
|
||||
id: "expiredRule",
|
||||
description: "expired rule",
|
||||
type: "fixed",
|
||||
value: 100,
|
||||
allocation: "total",
|
||||
})
|
||||
|
||||
const expiredDisc = manager.create(Discount, {
|
||||
id: "expiredDisc",
|
||||
code: "EXP_DISC",
|
||||
is_dynamic: false,
|
||||
is_disabled: false,
|
||||
starts_at: tenDaysAgo,
|
||||
ends_at: yesterday,
|
||||
})
|
||||
|
||||
expiredDisc.regions = [r]
|
||||
expiredDisc.rule = expiredRule
|
||||
await manager.save(expiredDisc)
|
||||
|
||||
const prematureRule = manager.create(DiscountRule, {
|
||||
id: "prematureRule",
|
||||
description: "premature rule",
|
||||
type: "fixed",
|
||||
value: 100,
|
||||
allocation: "total",
|
||||
})
|
||||
|
||||
const prematureDiscount = manager.create(Discount, {
|
||||
id: "prematureDiscount",
|
||||
code: "PREM_DISC",
|
||||
is_dynamic: false,
|
||||
is_disabled: false,
|
||||
starts_at: tomorrow,
|
||||
ends_at: tenDaysFromToday,
|
||||
})
|
||||
|
||||
prematureDiscount.regions = [r]
|
||||
prematureDiscount.rule = prematureRule
|
||||
await manager.save(prematureDiscount)
|
||||
|
||||
const invalidDynamicRule = manager.create(DiscountRule, {
|
||||
id: "invalidDynamicRule",
|
||||
description: "invalidDynamic rule",
|
||||
type: "fixed",
|
||||
value: 100,
|
||||
allocation: "total",
|
||||
})
|
||||
|
||||
const invalidDynamicDiscount = manager.create(Discount, {
|
||||
id: "invalidDynamicDiscount",
|
||||
code: "INV_DYN_DISC",
|
||||
is_dynamic: true,
|
||||
is_disabled: false,
|
||||
starts_at: tenDaysAgo,
|
||||
ends_at: tenDaysFromToday,
|
||||
valid_duration: "P1D", // one day
|
||||
})
|
||||
|
||||
invalidDynamicDiscount.regions = [r]
|
||||
invalidDynamicDiscount.rule = invalidDynamicRule
|
||||
await manager.save(invalidDynamicDiscount)
|
||||
|
||||
const DynamicRule = manager.create(DiscountRule, {
|
||||
id: "DynamicRule",
|
||||
description: "Dynamic rule",
|
||||
type: "fixed",
|
||||
value: 10000,
|
||||
allocation: "total",
|
||||
})
|
||||
|
||||
const DynamicDiscount = manager.create(Discount, {
|
||||
id: "DynamicDiscount",
|
||||
code: "DYN_DISC",
|
||||
is_dynamic: true,
|
||||
is_disabled: false,
|
||||
starts_at: tenDaysAgo,
|
||||
ends_at: tenDaysFromToday,
|
||||
valid_duration: "P1M", //one month
|
||||
})
|
||||
|
||||
DynamicDiscount.regions = [r]
|
||||
DynamicDiscount.rule = DynamicRule
|
||||
await manager.save(DynamicDiscount)
|
||||
|
||||
await manager.query(
|
||||
`UPDATE "country" SET region_id='test-region' WHERE iso_2 = 'us'`
|
||||
)
|
||||
@@ -304,6 +404,11 @@ module.exports = async (connection, data = {}) => {
|
||||
data: {},
|
||||
})
|
||||
|
||||
await manager.save(pay)
|
||||
|
||||
cart2.payment = pay
|
||||
|
||||
await manager.save(cart2)
|
||||
const swapPay = manager.create(Payment, {
|
||||
id: "test-swap-payment",
|
||||
amount: 10000,
|
||||
|
||||
Reference in New Issue
Block a user