Skip to content

Commit

Permalink
Merge pull request #9 from commercetools/order-syncer/integration-test
Browse files Browse the repository at this point in the history
Order syncer IT
  • Loading branch information
leungkinghin-ct authored Nov 15, 2023
2 parents 02adb98 + 0ff1b76 commit db4d447
Show file tree
Hide file tree
Showing 16 changed files with 104 additions and 54 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,5 @@ jobs:
- name: Check the coding style
run: npm run lint && npm run prettier

- name: Execute unit tests
run: npm run test:unit
- name: Execute tests
run: npm run test:ci
2 changes: 0 additions & 2 deletions connect.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ deployAs:
standardConfiguration:
- key: CTP_REGION
description: region of commercetools composable commerce project
- key: CTP_ORDER_CHANGE_SUBSCRIPTION_KEY
description: Key of commercetools subscription which subscribes any change in commercetools order resources
securedConfiguration:
- key: CTP_PROJECT_KEY
description: project key from commercetools composable commerce project
Expand Down
3 changes: 2 additions & 1 deletion order-syncer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@
"lint": "node_modules/.bin/eslint src --ext .js",
"prettier": "node_modules/.bin/prettier --write '**/*.{js,ts}'",
"test": "npm run test:unit",
"test:integration": "node_modules/.bin/jest --config test/integration/jest.config.cjs --silent=false",
"test:unit": "node_modules/.bin/jest --config test/unit/jest.config.cjs",
"test:ci": "npm run test:unit"
"test:ci": "npm run test:unit && npm run test:integration"
},
"author": "",
"license": "MIT",
Expand Down
8 changes: 2 additions & 6 deletions order-syncer/src/clients/query.client.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createApiRoot } from './create.client.js';
import CustomError from '../errors/custom.error.js';
import { HTTP_STATUS_RESOURCE_NOT_FOUND } from '../constants/http.status.constants.js';
import { HTTP_STATUS_SUCCESS_ACCEPTED } from '../constants/http.status.constants.js';
const queryArgs = {
withTotal: false,
expand: ['cart'],
Expand All @@ -16,10 +16,6 @@ export async function getCartByOrderId(orderId) {
.execute()
.then((response) => response.body?.cart.obj)
.catch((error) => {
throw new CustomError(
HTTP_STATUS_RESOURCE_NOT_FOUND,
error.message,
error
);
throw new CustomError(HTTP_STATUS_SUCCESS_ACCEPTED, error.message, error);
});
}
5 changes: 1 addition & 4 deletions order-syncer/src/connectors/post-deploy.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,13 @@ const CONNECT_GCP_PROJECT_ID_KEY = 'CONNECT_GCP_PROJECT_ID';
async function postDeploy(properties) {
const topicName = properties.get(CONNECT_GCP_TOPIC_NAME_KEY);
const projectId = properties.get(CONNECT_GCP_PROJECT_ID_KEY);
const ctpOrderChangeSubscriptionKey = properties.get(
CTP_ORDER_CHANGE_SUBSCRIPTION_KEY
);

const apiRoot = createApiRoot();
await createChangedOrderSubscription(
apiRoot,
topicName,
projectId,
ctpOrderChangeSubscriptionKey
CTP_ORDER_CHANGE_SUBSCRIPTION_KEY
);
}

Expand Down
10 changes: 3 additions & 7 deletions order-syncer/src/connectors/pre-undeploy.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,14 @@ import { createApiRoot } from '../clients/create.client.js';
import { CTP_ORDER_CHANGE_SUBSCRIPTION_KEY } from '../constants/connectors.constants.js';
import { deleteChangedOrderSubscription } from './actions.js';

async function preUndeploy(properties) {
async function preUndeploy() {
const apiRoot = createApiRoot();
const ctpOrderChangeSubscriptionKey = properties.get(
CTP_ORDER_CHANGE_SUBSCRIPTION_KEY
);
await deleteChangedOrderSubscription(apiRoot, ctpOrderChangeSubscriptionKey);
await deleteChangedOrderSubscription(apiRoot, CTP_ORDER_CHANGE_SUBSCRIPTION_KEY);
}

async function run() {
try {
const properties = new Map(Object.entries(process.env));
await preUndeploy(properties);
await preUndeploy();
} catch (error) {
process.stderr.write(`Post-undeploy failed: ${error.message}\n`);
process.exitCode = 1;
Expand Down
2 changes: 1 addition & 1 deletion order-syncer/src/constants/connectors.constants.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export const MESSAGE_TYPE = ['OrderCreated'];
export const CTP_ORDER_CHANGE_SUBSCRIPTION_KEY =
'CTP_ORDER_CHANGE_SUBSCRIPTION_KEY';
'ct-connect-tax-integration-order-change-subscription';
2 changes: 0 additions & 2 deletions order-syncer/src/constants/http.status.constants.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
export const HTTP_STATUS_SUCCESS_ACCEPTED = 202;
export const HTTP_STATUS_SUCCESS_NO_CONTENT = 204;
export const HTTP_STATUS_BAD_REQUEST = 400;
export const HTTP_STATUS_RESOURCE_NOT_FOUND = 404;
export const HTTP_STATUS_SERVER_ERROR = 500;
18 changes: 4 additions & 14 deletions order-syncer/src/controllers/sync.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
HTTP_STATUS_SUCCESS_NO_CONTENT,
HTTP_STATUS_SERVER_ERROR,
HTTP_STATUS_SUCCESS_ACCEPTED,
HTTP_STATUS_BAD_REQUEST,
} from '../constants/http.status.constants.js';
import createTaxTransaction from '../extensions/stripe/clients/client.js';
import CustomError from '../errors/custom.error.js';
Expand All @@ -24,11 +23,10 @@ async function syncToTaxProvider(orderId, cart) {
export const syncHandler = async (request, response) => {
try {
// Receive the Pub/Sub message

const encodedMessageBody = request.body?.message?.data;
if (!encodedMessageBody) {
throw new CustomError(
HTTP_STATUS_BAD_REQUEST,
HTTP_STATUS_SUCCESS_ACCEPTED,
'Missing message data from incoming event message.'
);
}
Expand All @@ -37,17 +35,9 @@ export const syncHandler = async (request, response) => {
doValidation(messageBody);

const orderId = messageBody?.resource?.id;

if (orderId) {
const cart = await getCartByOrderId(orderId);
if (cart) {
await syncToTaxProvider(orderId, cart);
}
} else {
throw new CustomError(
HTTP_STATUS_BAD_REQUEST,
'Missing order ID from incoming event message.'
);
const cart = await getCartByOrderId(orderId);
if (cart) {
await syncToTaxProvider(orderId, cart);
}
} catch (err) {
logger.error(err);
Expand Down
3 changes: 2 additions & 1 deletion order-syncer/src/middlewares/auth.middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import readConfiguration from '../utils/config.util.js';
/**
* Configure Middleware. Example only. Adapt on your own
*/
const config = readConfiguration;
const config = readConfiguration();

export const authMiddlewareOptions = {
host: `https://auth.${config.region}.commercetools.com`,
projectKey: config.projectKey,
Expand Down
2 changes: 1 addition & 1 deletion order-syncer/src/middlewares/http.middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import readConfiguration from '../utils/config.util.js';
/**
* Configure Middleware. Example only. Adapt on your own
*/
const config = readConfiguration;
const config = readConfiguration();
export const httpMiddlewareOptions = {
host: `https://api.${config.region}.commercetools.com`,
};
6 changes: 1 addition & 5 deletions order-syncer/src/utils/config.util.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { getValidateMessages } from '../validators/helpers.validator.js';
*
* @returns The configuration with the correct env vars
*/
function readConfiguration() {
export default function readConfiguration() {
const envVars = {
clientId: process.env.CTP_CLIENT_ID,
clientSecret: process.env.CTP_CLIENT_SECRET,
Expand All @@ -29,7 +29,3 @@ function readConfiguration() {

return envVars;
}

export default {
readConfiguration,
};
8 changes: 8 additions & 0 deletions order-syncer/test/integration/jest.config.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module.exports = {
rootDir: '../../',
displayName: 'Tests Javascript Application - Service',
testMatch: ['**/test/integration/?(*.)+(spec|test).js?(x)'],
testEnvironment: 'node',
verbose: true,
silent: true,
};
60 changes: 60 additions & 0 deletions order-syncer/test/integration/sync.route.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { expect, describe, afterAll, it } from '@jest/globals';
import request from 'supertest';
import server from '../../src/index.js';
import { encodeJsonObject } from './utils/encoder.utils.js';
import { HTTP_STATUS_SUCCESS_ACCEPTED } from '../../src/constants/http.status.constants.js';
/** Reminder : Please put mandatory environment variables in the settings of your github repository **/
describe('Test sync.route.js', () => {
it(`When resource identifier is absent in URL, it should returns 404 http status`, async () => {
let response = {};
// Send request to the connector application with following code snippet.

response = await request(server).post(`/`);
expect(response).toBeDefined();
expect(response.statusCode).toEqual(404);
});

it(`When payload body does not exist, it should returns 400 http status`, async () => {
let response = {};
// Send request to the connector application with following code snippet.
let payload = {};
response = await request(server).post(`/orderSyncer`).send(payload);

expect(response).toBeDefined();
expect(response.statusCode).toEqual(HTTP_STATUS_SUCCESS_ACCEPTED);
});

it(`When payload body exists without correct order ID, it should returns 404 http status`, async () => {
let response = {};
// Send request to the connector application with following code snippet.
// Following incoming message data is an example. Please define incoming message based on resources identifer in your own Commercetools project
const incomingMessageData = {
notificationType: 'Message',
resource: { typeId: 'order', id: 'dummy-product-id' },
type: 'OrderCreated',
resourceUserProvidedIdentifiers: { orderNumber: 'dummy-order-number' },
version: 11,
oldVersion: 10,
modifiedAt: '2023-09-12T00:00:00.000Z',
};

const encodedMessageData = encodeJsonObject(incomingMessageData);
let payload = {
message: {
data: encodedMessageData,
},
};
response = await request(server).post(`/orderSyncer`).send(payload);

expect(response).toBeDefined();
expect(response.statusCode).toEqual(HTTP_STATUS_SUCCESS_ACCEPTED);
});

afterAll(() => {
// Enable the function below to close the application on server once all test cases are executed.

if (server) {
server.close();
}
});
});
9 changes: 9 additions & 0 deletions order-syncer/test/integration/utils/encoder.utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const encodeString = (message) => {
const buff = Buffer.from(message);
return buff.toString('base64').trim();
};

export const encodeJsonObject = (messageBody) => {
const message = JSON.stringify(messageBody);
return encodeString(message);
};
16 changes: 8 additions & 8 deletions order-syncer/test/unit/sync.controller.spec.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { expect, describe, it } from '@jest/globals';
import { expect, describe, it, afterEach } from '@jest/globals';

import sinon from 'sinon';
import { syncHandler } from '../../src/controllers/sync.controller.js';
import { HTTP_STATUS_BAD_REQUEST } from '../../src/constants/http.status.constants.js';
const sandbox = sinon.createSandbox();
import configUtil from '../../src/utils/config.util.js';
import { HTTP_STATUS_SUCCESS_ACCEPTED } from '../../src/constants/http.status.constants.js';
import * as ConfigUtil from '../../src/utils/config.util.js';

describe('sync.controller.spec', () => {
afterEach(() => {
sandbox.restore();
sinon.restore();
});

it(`should return 400 HTTP status when message data is missing in incoming event message.`, async () => {
Expand All @@ -19,7 +18,8 @@ describe('sync.controller.spec', () => {
scope: 'dummy-ctp-scope',
region: 'dummy-ctp-region',
};
sandbox.stub(configUtil, 'readConfiguration').callsFake(() => {

sinon.stub(ConfigUtil, 'default').callsFake(() => {
return dummyConfig;
});
const mockRequest = {
Expand All @@ -36,11 +36,11 @@ describe('sync.controller.spec', () => {
};
},
};
const responseStatusSpy = sandbox.spy(mockResponse, 'status');
const responseStatusSpy = sinon.spy(mockResponse, 'status');

await syncHandler(mockRequest, mockResponse);
expect(responseStatusSpy.firstCall.firstArg).toEqual(
HTTP_STATUS_BAD_REQUEST
HTTP_STATUS_SUCCESS_ACCEPTED
);
});
});

0 comments on commit db4d447

Please sign in to comment.