Skip to content

Commit

Permalink
feat: return error when coupon can't applied
Browse files Browse the repository at this point in the history
Signed-off-by: vanpho93 <vanpho02@gmail.com>
  • Loading branch information
vanpho93 committed Jan 5, 2023
1 parent b698c5d commit 00059cc
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,73 @@ test("should throw error when more than one coupon have same code", async () =>
})).rejects.toThrow(expectedError);
});

test("should throw error if promotion expired", async () => {
const cart = { _id: "cartId" };
const promotion = {
_id: "promotionId",
type: "explicit"
};
const coupon = {
_id: "couponId",
code: "CODE",
promotionId: "promotionId"
};
mockContext.collections.Promotions = {
findOne: jest.fn().mockResolvedValueOnce(promotion)
};
mockContext.collections.Cart = {
findOne: jest.fn().mockResolvedValueOnce(cart)
};
mockContext.collections.Coupons = {
find: jest.fn().mockReturnValue({
toArray: jest.fn().mockResolvedValueOnce([coupon])
})
};

const expectedError = new ReactionError("not-found", "The coupon CODE is not found");

expect(applyCouponToCart(mockContext, {
shopId: "_shopId",
cartId: "_id",
couponCode: "CODE",
cartToken: "anonymousToken"
})).rejects.toThrow(expectedError);
});

test("should throw error when more than one coupon have same code", async () => {
const cart = { _id: "cartId" };
const promotion = {
_id: "promotionId",
type: "explicit"
};
const coupon = {
_id: "couponId",
code: "CODE",
promotionId: "promotionId"
};

mockContext.collections.Promotions = {
findOne: jest.fn().mockResolvedValueOnce(promotion)
};
mockContext.collections.Cart = {
findOne: jest.fn().mockResolvedValueOnce(cart)
};
mockContext.collections.Coupons = {
find: jest.fn().mockReturnValue({
toArray: jest.fn().mockResolvedValueOnce([coupon, coupon])
})
};

const expectedError = new ReactionError("not-found", "The coupon have duplicate with other promotion. Please contact admin for more information");

expect(applyCouponToCart(mockContext, {
shopId: "_shopId",
cartId: "_id",
couponCode: "CODE",
cartToken: "anonymousToken"
})).rejects.toThrow(expectedError);
});

test("should throw error if promotion already exists on the cart", async () => {
const now = new Date();
const cart = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ test("throws error when coupon code already created", async () => {
find: jest.fn().mockReturnValue({
toArray: jest.fn().mockResolvedValueOnce(Promise.resolve([coupon]))
}),
insertOne: jest.fn().mockResolvedValueOnce(Promise.resolve({ insertedId: "123", insertedCount: 1 }))
// eslint-disable-next-line id-length
insertOne: jest.fn().mockResolvedValueOnce(Promise.resolve({ insertedId: "123", result: { n: 1 } }))
}
};

Expand Down Expand Up @@ -93,8 +94,8 @@ test("should insert a new coupon and return the created results", async () => {
find: jest.fn().mockReturnValue({
toArray: jest.fn().mockResolvedValueOnce([])
}),
findOne: jest.fn().mockResolvedValueOnce(Promise.resolve(null)),
insertOne: jest.fn().mockResolvedValueOnce(Promise.resolve({ insertedId: "123", insertedCount: 1 }))
// eslint-disable-next-line id-length
insertOne: jest.fn().mockResolvedValueOnce(Promise.resolve({ insertedId: "123", result: { n: 1 } }))
},
Promotions: {
findOne: jest.fn().mockResolvedValueOnce(Promise.resolve(promotion))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ export default async function applyExplicitPromotion(context, cart, promotion) {
if (!Array.isArray(cart.appliedPromotions)) {
cart.appliedPromotions = [];
}
cart.appliedPromotions.push(promotion);
cart.appliedPromotions.push({
...promotion,
newlyAdded: true
});
const updatedCart = await context.mutations.saveCart(context, cart);
return updatedCart;
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ test("call applyPromotions function", async () => {

applyExplicitPromotion(context, cart, promotion);

const expectedCart = { ...cart, appliedPromotions: [promotion] };
const expectedCart = {
...cart,
appliedPromotions: [
{
...promotion,
newlyAdded: true
}
]
};
expect(mockSaveCartMutation).toHaveBeenCalledWith(context, expectedCart);
});
14 changes: 12 additions & 2 deletions packages/api-plugin-promotions/src/handlers/applyPromotions.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { createRequire } from "module";
import Logger from "@reactioncommerce/logger";
import Random from "@reactioncommerce/random";
import ReactionError from "@reactioncommerce/reaction-error";
import _ from "lodash";
import canBeApplied from "../utils/canBeApplied.js";
import enhanceCart from "../utils/enhanceCart.js";
Expand Down Expand Up @@ -96,10 +97,13 @@ export default async function applyPromotions(context, cart) {

const unqualifiedPromotions = promotions.concat(_.map(explicitPromotions, (promotion) => {
const existsPromotion = _.find(cart.appliedPromotions || [], { _id: promotion._id });
if (existsPromotion) promotion.relatedCoupon = existsPromotion.relatedCoupon;
if (existsPromotion) promotion.relatedCoupon = existsPromotion.relatedCoupon || undefined;
if (typeof existsPromotion?.newlyAdded !== "undefined") promotion.newlyAdded = existsPromotion.newlyAdded;
return promotion;
}));

const newlyAddedPromotionId = _.find(unqualifiedPromotions, "newlyAdded")?._id;

for (const { cleanup } of pluginPromotions.actions) {
cleanup && await cleanup(context, cart);
}
Expand Down Expand Up @@ -195,7 +199,13 @@ export default async function applyPromotions(context, cart) {
}
}

enhancedCart.appliedPromotions = appliedPromotions;
// If a explicit promotion was just applied, throw an error so that the client can display the message
if (newlyAddedPromotionId) {
const message = _.find(cartMessages, ({ metaFields }) => metaFields.promotionId === newlyAddedPromotionId);
if (message) throw new ReactionError("invalid-params", message.message);
}

enhancedCart.appliedPromotions = _.map(appliedPromotions, (promotion) => _.omit(promotion, "newlyAdded"));

// Remove messages that are no longer relevant
const cleanedMessages = _.filter(cartMessages, (message) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -288,3 +288,52 @@ test("should not have promotion message when the promotion already message added

expect(cart.messages.length).toEqual(1);
});

test("throw error when explicit promotion is newly applied and conflict with other", async () => {
isPromotionExpired.mockReturnValue(false);
canBeApplied.mockReturnValue({ qualifies: false });

const promotion = {
...testPromotion,
_id: "promotionId",
triggerType: "implicit"
};
const secondPromotion = {
...testPromotion,
_id: "promotionId2",
triggerType: "explicit",
newlyApplied: true,
relatedCoupon: {
couponCode: "couponCode",
couponId: "couponId"
},
stackability: {
key: "none",
parameters: {}
}
};
const cart = {
_id: "cartId",
appliedPromotions: [promotion, secondPromotion]
};

mockContext.collections.Promotions = {
find: () => ({
toArray: jest.fn().mockResolvedValueOnce([promotion, secondPromotion])
})
};

testTrigger.mockReturnValue(Promise.resolve(true));
testAction.mockReturnValue(Promise.resolve({ affected: true }));

mockContext.promotions = { ...pluginPromotion };
mockContext.simpleSchemas = {
Cart: { clean: jest.fn() }
};

try {
await applyPromotions(mockContext, cart);
} catch (error) {
expect(error.error).toEqual("invalid-params");
}
});

0 comments on commit 00059cc

Please sign in to comment.