Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DEVEXP-337: Mocked tests setup #97

Merged
merged 11 commits into from
Jun 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
"ignoreRegExpLiterals": true,
"ignorePattern": "^import.+|test"
}
]
],
"new-cap": "off"
}
}
43 changes: 43 additions & 0 deletions .github/workflows/run-ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,49 @@ jobs:
- run: yarn run build
- run: yarn run test

e2e:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Checkout sinch-sdk-mockserver repository
uses: actions/checkout@v3
with:
repository: sinch/sinch-sdk-mockserver
token: ${{ secrets.PAT_CI }}
fetch-depth: 0
path: sinch-sdk-mockserver
- name: Build custom Docker image
run: |
cd sinch-sdk-mockserver
docker build -t sinch-sdk-mockserver -f Dockerfile .
- name: Start mock servers with Docker Compose
run: |
cd sinch-sdk-mockserver
docker-compose up -d
- name: Wait for the mock servers to be healthy
run: |
cd sinch-sdk-mockserver
while ! docker inspect --format='{{json .State.Health.Status}}' $(docker-compose ps -q authentication-server) | grep -q '"healthy"'; do
echo "Waiting for authentication-server to be healthy..."
sleep 2
done
while ! docker inspect --format='{{json .State.Health.Status}}' $(docker-compose ps -q fax-server) | grep -q '"healthy"'; do
echo "Waiting for fax-server to be healthy..."
sleep 2
done
- name: Create target directories for feature files
run: |
mkdir -p ./packages/fax/tests/e2e/features
- name: Copy feature files
run: |
cp sinch-sdk-mockserver/features/fax/*.feature ./packages/fax/tests/e2e/features/

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it having sense to add the feature files onto Docker image and access them from container when mounted ? (docker compose volumes usage)
PROS:

  • all requirements for e2e are stored within a single image and used from mounted container. So if image could from a hub, there is no longer dependency onto another repository
  • in case of refactoring from source image repository (aggregation, doc, comments, ...) the "public availability" for feature will be by contracts onto "/features" container's directory and could be curated during image creation

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The destination folder has to match the Cucumber configuration. If we say to Cucumber to read from the docker volume, that's fine too. As we don't have an artifact repositoty to rely on for the moment, cloning the docker-compose file and the features is the simplest solution that came into my mind in a spirit of sharing the mockserver expectations and cucumber features across the SDKs

- name: Run e2e tests
run: |
yarn install
yarn run build
yarn run e2e

sonarcloud:
runs-on: ubuntu-latest
steps:
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@
"lerna": "lerna",
"bootstrap": "npx lerna bootstrap",
"build": "lerna run build",
"test": "jest"
"test": "jest",
"e2e": "lerna run test:e2e"
},
"keywords": [],
"devDependencies": {
"@babel/core": "^7.23.3",
"@babel/preset-env": "^7.23.3",
"@babel/preset-typescript": "^7.23.3",
"@cucumber/cucumber": "^10.3.1",
"@types/jest": "^29.5.1",
"@typescript-eslint/eslint-plugin": "^6.9.0",
"@typescript-eslint/parser": "^6.9.0",
Expand Down
8 changes: 8 additions & 0 deletions packages/fax/cucumber.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module.exports = {
default: [
'tests/e2e/features/**/*.feature',
'--require-module ts-node/register',
'--require tests/rest/v3/**/*.steps.ts',
`--format-options '{"snippetInterface": "synchronous"}'`,
].join(' '),
};
3 changes: 2 additions & 1 deletion packages/fax/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
"scripts": {
"build": "yarn run clean && yarn run compile",
"clean": "rimraf dist tsconfig.tsbuildinfo",
"compile": "tsc -p tsconfig.build.json && tsc -p tsconfig.tests.json && rimraf dist/tests && rimraf tsconfig.build.tsbuildinfo"
"compile": "tsc -p tsconfig.build.json && tsc -p tsconfig.tests.json && rimraf dist/tests && rimraf tsconfig.build.tsbuildinfo",
"test:e2e": "cucumber-js"
},
"dependencies": {
"@sinch/sdk-client": "^1.1.0"
Expand Down
Binary file added packages/fax/tests/e2e/resources/sinch-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
207 changes: 207 additions & 0 deletions packages/fax/tests/rest/v3/faxes/faxes.steps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
import { Given, When, Then } from '@cucumber/cucumber';
import { FileBuffer, PageResult } from '@sinch/sdk-client';
import * as assert from 'assert';
import { FaxService, Fax } from '../../../../src';

let faxService: FaxService;
let listResponse: PageResult<Fax.Fax>;
const faxList: Fax.Fax[] = [];
let sendFaxResponse: Fax.Fax[];
let fax: Fax.Fax;
let fileBuffer: FileBuffer;
let deleteContentResponse: void;

Given('the Fax service is available', function () {
faxService = new FaxService({
projectId: 'tinyfrog-jump-high-over-lilypadbasin',
keyId: 'keyId',
keySecret: 'keySecret',
authHostname: 'http://localhost:3011',
});
faxService.setHostname('http://localhost:3012');
});

When('I send a fax with a contentUrl only to a single recipient', async function () {
sendFaxResponse = await faxService.faxes.send({
sendFaxRequestBody: {
to: '+12015555555',
contentUrl: 'https://developers.sinch.com/fax/fax.pdf',
},
});
});

// eslint-disable-next-line max-len
Then('the response contains a list of fax objects with a single element received from a multipart-form-data request with contentUrl only', function () {
assert.equal(sendFaxResponse.length, 1);
assert.equal('01W4FFL35P4NC4K35URLSINGLE1', sendFaxResponse[0].id);
});

When('I send a fax with a contentUrl only to multiple recipients', async function () {
sendFaxResponse = await faxService.faxes.send({
sendFaxRequestBody: {
to: ['+12015555555', '+12016666666'],
contentUrl: 'https://developers.sinch.com/fax/fax.pdf',
},
});
});

// eslint-disable-next-line max-len
Then('the response contains a list of fax objects with multiple elements received from a multipart-form-data request with contentUrl only', function () {
assert.equal(sendFaxResponse.length, 2);
assert.equal('01W4FFL35P4NC4K35URLMULTI01', sendFaxResponse[0].id);
assert.equal('01W4FFL35P4NC4K35URLMULTI02', sendFaxResponse[1].id);
});

When('I send a fax with a contentUrl and a binary file attachment to a single recipient', async function () {
sendFaxResponse = await faxService.faxes.send({
sendFaxRequestBody: {
to: '+12015555555',
contentUrl: 'https://developers.sinch.com/fax/fax.pdf',
filePaths: ['./tests/e2e/resources/sinch-logo.png'],
},
});
});

// eslint-disable-next-line max-len
Then('the response contains a list of fax objects with a single element received from a multipart-form-data request', function () {
assert.equal(sendFaxResponse.length, 1);
assert.equal('01W4FFL35P4NC4K35BINSINGLE1', sendFaxResponse[0].id);
});

When('I send a fax with a contentUrl and a binary file attachment to multiple recipients', async function () {
sendFaxResponse = await faxService.faxes.send({
sendFaxRequestBody: {
to: ['+12015555555', '+12016666666'],
contentUrl: 'https://developers.sinch.com/fax/fax.pdf',
filePaths: ['./tests/e2e/resources/sinch-logo.png'],
},
});
});

// eslint-disable-next-line max-len
Then('the response contains a list of fax objects with multiple elements received from a multipart-form-data request', function () {
assert.equal(sendFaxResponse.length, 2);
assert.equal('01W4FFL35P4NC4K35BINMULTI01', sendFaxResponse[0].id);
assert.equal('01W4FFL35P4NC4K35BINMULTI02', sendFaxResponse[1].id);
});

When('I send a fax with a contentUrl and a base64 file encoded to a single recipient', async function () {
sendFaxResponse = await faxService.faxes.send({
sendFaxRequestBody: {
to: '+12015555555',
contentUrl: 'https://developers.sinch.com/fax/fax.pdf',
files: [
{
file: 'WSdhIGRlcyBqb3VycywgZmF1dCBwYXMgbSdjaGVyY2hlciAhIEV0IHknYSBkZXMgam91cnMgdG91cyBsZXMgam91cnMgIQ==',
fileType: 'PDF',
},
{
file: 'UXVhbmQgbGUgdHJvbGwgcGFybGUsIGwnaG9tbWUgYXZpc8OpIGwnw6ljb3V0ZQ==',
fileType: 'PDF',
},
],
},
});
});

// eslint-disable-next-line max-len
Then('the response contains a list of fax objects with a single element received from an application-json request', function () {
assert.equal(sendFaxResponse.length, 1);
assert.equal('01W4FFL35P4NC4K35B64SINGLE1', sendFaxResponse[0].id);
});

When('I send a fax with a contentUrl and a base64 file encoded to multiple recipients', async function () {
sendFaxResponse = await faxService.faxes.send({
sendFaxRequestBody: {
to: ['+12015555555', '+12016666666'],
contentUrl: 'https://developers.sinch.com/fax/fax.pdf',
files: [
{
file: 'WSdhIGRlcyBqb3VycywgZmF1dCBwYXMgbSdjaGVyY2hlciAhIEV0IHknYSBkZXMgam91cnMgdG91cyBsZXMgam91cnMgIQ==',
fileType: 'PDF',
},
{
file: 'UXVhbmQgbGUgdHJvbGwgcGFybGUsIGwnaG9tbWUgYXZpc8OpIGwnw6ljb3V0ZQ==',
fileType: 'PDF',
},
],
},
});
});

// eslint-disable-next-line max-len
Then('the response contains a list of fax objects with multiple elements received from an application-json request', function () {
assert.equal(sendFaxResponse.length, 2);
assert.equal('01W4FFL35P4NC4K35B64MULTI01', sendFaxResponse[0].id);
assert.equal('01W4FFL35P4NC4K35B64MULTI02', sendFaxResponse[1].id);
});

When('I retrieve a fax', async function () {
fax = await faxService.faxes.get({
id: '01W4FFL35P4NC4K35CR3P35M1N1',
});
});

Then('the response contains a fax object', function () {
assert.equal('01W4FFL35P4NC4K35CR3P35M1N1', fax.id);
assert.equal('OUTBOUND', fax.direction);
assert.equal('+12014444444', fax.from);
assert.equal('+12015555555', fax.to);
assert.equal(1, fax.numberOfPages);
assert.equal('COMPLETED', fax.status);
assert.equal('America/New_York', fax.headerTimeZone);
assert.equal(60, fax.retryDelaySeconds);
assert.equal('multipart/form-data', fax.callbackUrlContentType);
assert.equal('0.07', (fax as any).pricePerPage);
assert.equal('123coffee-dada-beef-cafe-baadc0de5678', fax.projectId);
assert.equal('01K1TTENC4TSJ0LLYJ1GGLYJU1Y', fax.serviceId);
assert.equal('USD', fax.price?.currencyCode);
assert.equal('0.07', fax.price?.amount);
assert.equal(3, fax.maxRetries);
assert.equal(new Date('2024-06-06T14:42:42Z').getTime(), fax.createTime?.getTime());
assert.equal(new Date('2024-06-06T14:43:17Z').getTime(), fax.completedTime?.getTime());
assert.equal(true, fax.headerPageNumbers);
assert.equal('https://developers.sinch.com/fax/fax.pdf', fax.contentUrl?.[0]);
assert.equal('MONOCHROME', fax.imageConversionMethod);
assert.equal(true, fax.hasFile);
});

When('I send a request to list faxes', async function () {
listResponse = await faxService.faxes.list({});
});

Then('the response contains {string} faxes', function (expectedAnswer: string) {
const expectedFaxes = parseInt(expectedAnswer, 10);
assert.strictEqual(listResponse.data.length, expectedFaxes);
});

When('I send a request to list all the faxes', async function () {
for await (const fax of faxService.faxes.list({})) {
faxList.push(fax);
}
});

Then('the faxes list contains {string} faxes', function (expectedAnswer: string) {
const expectedFaxes = parseInt(expectedAnswer, 10);
assert.strictEqual(faxList.length, expectedFaxes);
});

When('I send a request to download a fax content as PDF', async function () {
fileBuffer = await faxService.faxes.downloadContent({
id: '01W4FFL35P4NC4K35CR3P35DWLD',
});
});

Then('the response contains a PDF document', function () {
assert.equal(fileBuffer.fileName, '01W4FFL35P4NC4K35CR3P35DWLD.pdf');
});

When('I send a request to delete a fax content on the server', async function () {
deleteContentResponse = await faxService.faxes.deleteContent({
id: '01W4FFL35P4NC4K35CR3P35DEL0',
});
});

Then('the response contains no data', function () {
assert.deepEqual(deleteContentResponse, {});
});
Loading
Loading