feature/IO-2885-IntelliPay-App-Postback
- Add Tests
This commit is contained in:
48
package-lock.json
generated
48
package-lock.json
generated
@@ -75,6 +75,7 @@
|
|||||||
"eslint": "^9.23.0",
|
"eslint": "^9.23.0",
|
||||||
"eslint-plugin-react": "^7.37.4",
|
"eslint-plugin-react": "^7.37.4",
|
||||||
"globals": "^15.15.0",
|
"globals": "^15.15.0",
|
||||||
|
"mock-require": "^3.0.3",
|
||||||
"p-limit": "^3.1.0",
|
"p-limit": "^3.1.0",
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.5.3",
|
||||||
"source-map-explorer": "^2.5.2",
|
"source-map-explorer": "^2.5.2",
|
||||||
@@ -8402,9 +8403,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/is-core-module": {
|
"node_modules/is-core-module": {
|
||||||
"version": "2.15.1",
|
"version": "2.16.1",
|
||||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz",
|
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
|
||||||
"integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==",
|
"integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -9535,6 +9536,40 @@
|
|||||||
"mkdirp": "bin/cmd.js"
|
"mkdirp": "bin/cmd.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/mock-require": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/mock-require/-/mock-require-3.0.3.tgz",
|
||||||
|
"integrity": "sha512-lLzfLHcyc10MKQnNUCv7dMcoY/2Qxd6wJfbqCcVk3LDb8An4hF6ohk5AztrvgKhJCqj36uyzi/p5se+tvyD+Wg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"get-caller-file": "^1.0.2",
|
||||||
|
"normalize-path": "^2.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mock-require/node_modules/get-caller-file": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
|
"node_modules/mock-require/node_modules/normalize-path": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"remove-trailing-separator": "^1.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/module-details-from-path": {
|
"node_modules/module-details-from-path": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz",
|
||||||
@@ -10709,6 +10744,13 @@
|
|||||||
"proxy-from-env": "^1.1.0"
|
"proxy-from-env": "^1.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/remove-trailing-separator": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/require-directory": {
|
"node_modules/require-directory": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||||
|
|||||||
@@ -81,6 +81,7 @@
|
|||||||
"eslint": "^9.23.0",
|
"eslint": "^9.23.0",
|
||||||
"eslint-plugin-react": "^7.37.4",
|
"eslint-plugin-react": "^7.37.4",
|
||||||
"globals": "^15.15.0",
|
"globals": "^15.15.0",
|
||||||
|
"mock-require": "^3.0.3",
|
||||||
"p-limit": "^3.1.0",
|
"p-limit": "^3.1.0",
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.5.3",
|
||||||
"source-map-explorer": "^2.5.2",
|
"source-map-explorer": "^2.5.2",
|
||||||
|
|||||||
152
server/intellipay/lib/tests/handleCommentBasedPayment.test.js
Normal file
152
server/intellipay/lib/tests/handleCommentBasedPayment.test.js
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
import mockRequire from "mock-require";
|
||||||
|
|
||||||
|
const gqlRequestMock = { request: vi.fn() };
|
||||||
|
const getPaymentTypeMock = vi.fn(() => "American Express");
|
||||||
|
const sendPaymentNotificationEmailMock = vi.fn();
|
||||||
|
|
||||||
|
let handleCommentBasedPayment;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.resetModules();
|
||||||
|
vi.clearAllMocks();
|
||||||
|
|
||||||
|
// Mock dependencies using mock-require BEFORE requiring the target module
|
||||||
|
mockRequire("../../../graphql-client/graphql-client", {
|
||||||
|
client: gqlRequestMock
|
||||||
|
});
|
||||||
|
|
||||||
|
mockRequire("../getPaymentType", getPaymentTypeMock);
|
||||||
|
mockRequire("../sendPaymentNotificationEmail", sendPaymentNotificationEmailMock);
|
||||||
|
|
||||||
|
// Now require the module under test
|
||||||
|
handleCommentBasedPayment = require("../handleCommentBasedPayment");
|
||||||
|
|
||||||
|
// Chain your GraphQL mocks
|
||||||
|
gqlRequestMock.request
|
||||||
|
.mockResolvedValueOnce({
|
||||||
|
jobs: [
|
||||||
|
{
|
||||||
|
id: "c1ffe09c-e7d4-46b3-aac5-f23e39563181",
|
||||||
|
shopid: "bfec8c8c-b7f1-49e0-be4c-524455f4e582"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.mockResolvedValueOnce({
|
||||||
|
bodyshops_by_pk: {
|
||||||
|
id: "bfec8c8c-b7f1-49e0-be4c-524455f4e582",
|
||||||
|
intellipay_config: {
|
||||||
|
payment_map: {
|
||||||
|
amex: "American Express"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.mockResolvedValueOnce({
|
||||||
|
insert_payments: {
|
||||||
|
returning: [{ id: "5dfda3c4-c0a6-4b09-a73d-176ed0ac6499" }]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("handleCommentBasedPayment", () => {
|
||||||
|
const mockLogger = { log: vi.fn() };
|
||||||
|
const mockRes = { sendStatus: vi.fn() };
|
||||||
|
|
||||||
|
const values = {
|
||||||
|
authcode: "5557301",
|
||||||
|
total: "0.01",
|
||||||
|
origin: "Dejavoo",
|
||||||
|
paymentid: "24294378",
|
||||||
|
cardtype: "Amex"
|
||||||
|
};
|
||||||
|
|
||||||
|
const decodedComment = {
|
||||||
|
payments: [{ jobid: "c1ffe09c-e7d4-46b3-aac5-f23e39563181", amount: 0.01 }],
|
||||||
|
userEmail: "test@example.com"
|
||||||
|
};
|
||||||
|
|
||||||
|
const logMeta = { op: "xyz123" };
|
||||||
|
|
||||||
|
it("processes comment-based payment and returns 200", async () => {
|
||||||
|
await handleCommentBasedPayment(values, decodedComment, mockLogger, logMeta, mockRes);
|
||||||
|
|
||||||
|
expect(gqlRequestMock.request).toHaveBeenCalledTimes(3);
|
||||||
|
expect(getPaymentTypeMock).toHaveBeenCalledWith({ amex: "American Express" }, "Amex");
|
||||||
|
expect(sendPaymentNotificationEmailMock).not.toHaveBeenCalled();
|
||||||
|
expect(mockRes.sendStatus).toHaveBeenCalledWith(200);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("sends notification if origin is OneLink and userEmail exists", async () => {
|
||||||
|
const oneLinkValues = { ...values, origin: "OneLink" };
|
||||||
|
|
||||||
|
await handleCommentBasedPayment(oneLinkValues, decodedComment, mockLogger, logMeta, mockRes);
|
||||||
|
|
||||||
|
expect(sendPaymentNotificationEmailMock).toHaveBeenCalledWith(
|
||||||
|
"test@example.com",
|
||||||
|
expect.anything(),
|
||||||
|
expect.anything(),
|
||||||
|
mockLogger,
|
||||||
|
logMeta
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(mockRes.sendStatus).toHaveBeenCalledWith(200);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("handles decodedComment as a direct array", async () => {
|
||||||
|
const arrayComment = [{ jobid: "c1ffe09c-e7d4-46b3-aac5-f23e39563181", amount: 0.01 }];
|
||||||
|
|
||||||
|
await handleCommentBasedPayment(values, arrayComment, mockLogger, logMeta, mockRes);
|
||||||
|
|
||||||
|
expect(gqlRequestMock.request).toHaveBeenCalledTimes(3);
|
||||||
|
expect(mockRes.sendStatus).toHaveBeenCalledWith(200);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not send email if origin is OneLink but userEmail is missing", async () => {
|
||||||
|
const commentWithoutEmail = {
|
||||||
|
payments: decodedComment.payments
|
||||||
|
// no userEmail
|
||||||
|
};
|
||||||
|
|
||||||
|
const oneLinkValues = { ...values, origin: "OneLink" };
|
||||||
|
|
||||||
|
await handleCommentBasedPayment(oneLinkValues, commentWithoutEmail, mockLogger, logMeta, mockRes);
|
||||||
|
|
||||||
|
expect(sendPaymentNotificationEmailMock).not.toHaveBeenCalled();
|
||||||
|
expect(mockRes.sendStatus).toHaveBeenCalledWith(200);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("logs important stages of the process", async () => {
|
||||||
|
await handleCommentBasedPayment(values, decodedComment, mockLogger, logMeta, mockRes);
|
||||||
|
|
||||||
|
const logCalls = mockLogger.log.mock.calls.map(([tag]) => tag);
|
||||||
|
|
||||||
|
expect(logCalls).toContain("intellipay-postback-parsed-comment");
|
||||||
|
expect(logCalls).toContain("intellipay-postback-payment-success");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("handles missing payment_map safely", async () => {
|
||||||
|
gqlRequestMock.request.mockReset(); // 🧹 Clear previous .mockResolvedValueOnce calls
|
||||||
|
|
||||||
|
gqlRequestMock.request
|
||||||
|
.mockResolvedValueOnce({
|
||||||
|
jobs: [{ id: "job1", shopid: "shop1" }]
|
||||||
|
})
|
||||||
|
.mockResolvedValueOnce({
|
||||||
|
bodyshops_by_pk: {
|
||||||
|
id: "shop1",
|
||||||
|
intellipay_config: null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.mockResolvedValueOnce({
|
||||||
|
insert_payments: {
|
||||||
|
returning: [{ id: "payment1" }]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await handleCommentBasedPayment(values, decodedComment, mockLogger, logMeta, mockRes);
|
||||||
|
|
||||||
|
expect(getPaymentTypeMock).toHaveBeenCalledWith(undefined, "Amex");
|
||||||
|
expect(mockRes.sendStatus).toHaveBeenCalledWith(200);
|
||||||
|
});
|
||||||
|
});
|
||||||
129
server/intellipay/lib/tests/handleInvoiceBasedPayment.test.js
Normal file
129
server/intellipay/lib/tests/handleInvoiceBasedPayment.test.js
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
import mockRequire from "mock-require";
|
||||||
|
|
||||||
|
const gqlRequestMock = { request: vi.fn() };
|
||||||
|
const getPaymentTypeMock = vi.fn(() => "Visa");
|
||||||
|
const handlePaymentValidationErrorMock = vi.fn();
|
||||||
|
|
||||||
|
let handleInvoiceBasedPayment;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.resetModules();
|
||||||
|
vi.clearAllMocks();
|
||||||
|
|
||||||
|
mockRequire("../../../graphql-client/graphql-client", {
|
||||||
|
client: gqlRequestMock
|
||||||
|
});
|
||||||
|
|
||||||
|
mockRequire("../getPaymentType", getPaymentTypeMock);
|
||||||
|
mockRequire("../handlePaymentValidationError", handlePaymentValidationErrorMock);
|
||||||
|
|
||||||
|
handleInvoiceBasedPayment = require("../handleInvoiceBasedPayment");
|
||||||
|
|
||||||
|
gqlRequestMock.request
|
||||||
|
.mockResolvedValueOnce({
|
||||||
|
jobs: [
|
||||||
|
{
|
||||||
|
id: "job123",
|
||||||
|
bodyshop: {
|
||||||
|
id: "shop123",
|
||||||
|
intellipay_config: {
|
||||||
|
payment_map: {
|
||||||
|
visa: "Visa"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.mockResolvedValueOnce({
|
||||||
|
id: "payment123"
|
||||||
|
})
|
||||||
|
.mockResolvedValueOnce({
|
||||||
|
insert_payment_response: {
|
||||||
|
returning: [{ id: "response123" }]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("handleInvoiceBasedPayment", () => {
|
||||||
|
const mockLogger = { log: vi.fn() };
|
||||||
|
const mockRes = { sendStatus: vi.fn() };
|
||||||
|
|
||||||
|
const values = {
|
||||||
|
merchantid: "m123",
|
||||||
|
invoice: "INV-001",
|
||||||
|
total: 100.0,
|
||||||
|
authcode: "AUTH123",
|
||||||
|
cardtype: "visa",
|
||||||
|
paymentid: "P789"
|
||||||
|
};
|
||||||
|
|
||||||
|
const logMeta = { op: "abc123" };
|
||||||
|
|
||||||
|
it("processes a valid invoice-based payment", async () => {
|
||||||
|
await handleInvoiceBasedPayment(values, mockLogger, logMeta, mockRes);
|
||||||
|
|
||||||
|
expect(gqlRequestMock.request).toHaveBeenCalledTimes(3);
|
||||||
|
expect(getPaymentTypeMock).toHaveBeenCalledWith({ visa: "Visa" }, "visa");
|
||||||
|
expect(mockRes.sendStatus).toHaveBeenCalledWith(200);
|
||||||
|
expect(handlePaymentValidationErrorMock).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("handles missing merchantid with validation error", async () => {
|
||||||
|
const invalidValues = { ...values, merchantid: undefined };
|
||||||
|
|
||||||
|
await handleInvoiceBasedPayment(invalidValues, mockLogger, logMeta, mockRes);
|
||||||
|
|
||||||
|
expect(handlePaymentValidationErrorMock).toHaveBeenCalledWith(
|
||||||
|
mockRes,
|
||||||
|
mockLogger,
|
||||||
|
"intellipay-postback-no-merchantid",
|
||||||
|
"Merchant ID is missing",
|
||||||
|
logMeta
|
||||||
|
);
|
||||||
|
expect(gqlRequestMock.request).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("handles job not found with validation error", async () => {
|
||||||
|
gqlRequestMock.request.mockReset();
|
||||||
|
gqlRequestMock.request.mockResolvedValueOnce({ jobs: [] });
|
||||||
|
|
||||||
|
await handleInvoiceBasedPayment(values, mockLogger, logMeta, mockRes);
|
||||||
|
|
||||||
|
expect(handlePaymentValidationErrorMock).toHaveBeenCalledWith(
|
||||||
|
mockRes,
|
||||||
|
mockLogger,
|
||||||
|
"intellipay-postback-job-not-found",
|
||||||
|
"Job not found",
|
||||||
|
logMeta
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("handles missing bodyshop with validation error", async () => {
|
||||||
|
gqlRequestMock.request.mockReset();
|
||||||
|
gqlRequestMock.request.mockResolvedValueOnce({
|
||||||
|
jobs: [{ id: "job123", bodyshop: null }]
|
||||||
|
});
|
||||||
|
|
||||||
|
await handleInvoiceBasedPayment(values, mockLogger, logMeta, mockRes);
|
||||||
|
|
||||||
|
expect(handlePaymentValidationErrorMock).toHaveBeenCalledWith(
|
||||||
|
mockRes,
|
||||||
|
mockLogger,
|
||||||
|
"intellipay-postback-bodyshop-not-found",
|
||||||
|
"Bodyshop not found",
|
||||||
|
logMeta
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("logs all expected stages of the process", async () => {
|
||||||
|
await handleInvoiceBasedPayment(values, mockLogger, logMeta, mockRes);
|
||||||
|
|
||||||
|
const logTags = mockLogger.log.mock.calls.map(([tag]) => tag);
|
||||||
|
|
||||||
|
expect(logTags).toContain("intellipay-postback-invoice-job-fetched");
|
||||||
|
expect(logTags).toContain("intellipay-postback-invoice-payment-success");
|
||||||
|
expect(logTags).toContain("intellipay-postback-invoice-response-success");
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||||
|
|
||||||
const getPaymentType = require("./lib/getPaymentType");
|
const getPaymentType = require("../lib/getPaymentType");
|
||||||
const decodeComment = require("./lib/decodeComment");
|
const decodeComment = require("../lib/decodeComment");
|
||||||
const getCptellerUrl = require("./lib/getCptellerUrl");
|
const getCptellerUrl = require("../lib/getCptellerUrl");
|
||||||
const handlePaymentValidationError = require("./lib/handlePaymentValidationError");
|
const handlePaymentValidationError = require("../lib/handlePaymentValidationError");
|
||||||
const getShopCredentials = require("./lib/getShopCredentials");
|
const getShopCredentials = require("../lib/getShopCredentials");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description Decode a base64-encoded JSON comment
|
* @description Decode a base64-encoded JSON comment
|
||||||
Reference in New Issue
Block a user