Skip to content

Commit

Permalink
ci: add some tests to run on Node.js 18 and later
Browse files Browse the repository at this point in the history
  • Loading branch information
striezel committed Dec 1, 2023
1 parent de69d91 commit 7259947
Show file tree
Hide file tree
Showing 4 changed files with 374 additions and 2 deletions.
8 changes: 6 additions & 2 deletions .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-22.04
strategy:
matrix:
node-version: [12.x, 14.x, 16.x, 18.x, 20.x]
node-version: [12, 14, 16, 18, 20]
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
Expand All @@ -18,4 +18,8 @@ jobs:
run: |
sudo apt-get update
sudo apt-get install -y build-essential libcairo2-dev libgif-dev libpango1.0-dev
- run: cd "$GITHUB_WORKSPACE"/export-server && npm install
- name: Install NPM packages
run: cd "$GITHUB_WORKSPACE"/export-server && npm install
- name: Tests
run: cd "$GITHUB_WORKSPACE"/export-server && ./test.sh
if: matrix.node-version >= 18
2 changes: 2 additions & 0 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ node_18:
script:
- cd ./export-server
- npm install
- ./test.sh

node_20:
image: node:20-slim
Expand All @@ -47,3 +48,4 @@ node_20:
script:
- cd ./export-server
- npm install
- ./test.sh
30 changes: 30 additions & 0 deletions export-server/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/bin/sh

# Start the server.
npm run start &
# Give server some time to start completely.
sleep 3
SERVER_PID=$(pgrep node)
echo "Server process id is $SERVER_PID."

# Run actual tests.
node --test
EXIT_CODE=$?

# Stop / kill server process.
kill $SERVER_PID
if [ $? -ne 0 ]
then
kill -9 $SERVER_PID
fi

sleep 1

if [ $EXIT_CODE -eq 0 ]
then
echo Tests were successful.
exit 0
else
echo Tests have failed.
exit 1
fi
336 changes: 336 additions & 0 deletions export-server/tests/server.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,336 @@
const assert = require('node:assert');
const { spawn, spawnSync } = require('node:child_process');
const http = require('node:http');
const { after, before, describe, it } = require('node:test');

describe('server', () => {
describe('request to non-root URL is not acceptable', () => {
it('HTTP status code 404 when another URL is requested', () => {
const options = {
port: 3000,
host: 'localhost',
method: 'POST',
path: '/foo.svg'
};

const req = http.request(options);
req.end();

req.on('response', (response) => {
assert.strictEqual(404, response.statusCode);
});
});
});

describe('only POST method is allowed', () => {
it('HTTP status code 405 when DELETE is used', () => {
const options = {
port: 3000,
host: 'localhost',
method: 'DELETE',
};

const req = http.request(options);
req.end();

req.on('response', (response) => {
assert.strictEqual(405, response.statusCode);
assert.ok(response.headers['allow']);
assert.strictEqual(response.headers['allow'], 'POST');
});
});

it('HTTP status code 405 when GET is used', () => {
const options = {
port: 3000,
host: 'localhost',
method: 'GET',
};

const req = http.request(options);
req.end();

req.on('response', (response) => {
assert.strictEqual(405, response.statusCode);
assert.ok(response.headers['allow']);
assert.strictEqual(response.headers['allow'], 'POST');
});
});

it('HTTP status code 405 when HEAD is used', () => {
const options = {
port: 3000,
host: 'localhost',
method: 'HEAD',
};

const req = http.request(options);
req.end();

req.on('response', (response) => {
assert.strictEqual(405, response.statusCode);
assert.ok(response.headers['allow']);
assert.strictEqual(response.headers['allow'], 'POST');
});
});

it('HTTP status code 405 when PATCH is used', () => {
const options = {
port: 3000,
host: 'localhost',
method: 'PATCH',
};

const req = http.request(options);
req.end();

req.on('response', (response) => {
assert.strictEqual(405, response.statusCode);
assert.ok(response.headers['allow']);
assert.strictEqual(response.headers['allow'], 'POST');
});
});

it('HTTP status code 405 when PUT is used', () => {
const options = {
port: 3000,
host: 'localhost',
method: 'PUT',
};

const req = http.request(options);
req.end();

req.on('response', (response) => {
assert.strictEqual(405, response.statusCode);
assert.ok(response.headers['allow']);
assert.strictEqual(response.headers['allow'], 'POST');
});
});

it('HTTP status code 405 when TRACE is used', () => {
const options = {
port: 3000,
host: 'localhost',
method: 'TRACE',
};

const req = http.request(options);
req.end();

req.on('response', (response) => {
assert.strictEqual(405, response.statusCode);
assert.ok(response.headers['allow']);
assert.strictEqual(response.headers['allow'], 'POST');
});
});
});

describe('query available methods via OPTIONS', () => {
it('HTTP status code 204 and Allow header for OPTIONS', () => {
const options = {
port: 3000,
host: 'localhost',
method: 'OPTIONS',
};

const req = http.request(options);
req.end();

req.on('response', (response) => {
// 204: no content
assert.strictEqual(204, response.statusCode);
// Response shall contain header "Allow: POST".
assert.ok(response.headers['allow']);
assert.strictEqual(response.headers['allow'], 'POST');
});
});
});

describe('large payload is rejected', () => {
it('HTTP status code 413 when request is too large', () => {
const options = {
port: 3000,
host: 'localhost',
method: 'POST',
};

const req = http.request(options);
const payload = { long: 'abcdefghij'.repeat(500000) };
req.write(JSON.stringify(payload));
req.end();

req.on('response', (response) => {
assert.strictEqual(413, response.statusCode);
});
});
});

describe('malformed JSON is rejected', () => {
it('HTTP status code 400 when request contains invalid JSON', () => {
const options = {
port: 3000,
host: 'localhost',
method: 'POST',
};

const req = http.request(options);
const payload = '{ "foo": "bar", "baz": ';
req.write(payload);
req.end();

req.on('response', (response) => {
assert.strictEqual(400, response.statusCode);
});
});
});

describe('image generation requests', () => {
const example_data = {
title: {
text: "ECharts entry example"
},
tooltip: {},
legend: {
data: ["Sales"]
},
backgroundColor: "#ffffff",
xAxis: {
data: ["shirt","cardigan","chiffon shirt","pants","heels","socks"]
},
yAxis: {},
series: [{
name: "Sales",
type: "bar",
data: [5,20,36,10,10,20]
}]
};
const payload = JSON.stringify(example_data);

it('request with example data is successful', () => {
const options = {
port: 3000,
host: 'localhost',
method: 'POST',
headers: {
'X-Image-Format': 'svg'
}
};

const req = http.request(options);
req.write(payload);
req.end();

req.on('response', (response) => {
assert.strictEqual(200, response.statusCode);
let body = '';
response.on('data', (chunk) => {
body += chunk;
});
response.on('end', () => {
assert.ok(body.startsWith('<svg'));
assert.ok(body.endsWith('</svg>'));
assert.ok(body.indexOf('width="700"') > 0);
assert.ok(body.indexOf('height="400"') > 0);
assert.ok(body.indexOf('ECharts entry example') > 0);
assert.ok(body.indexOf('cardigan') > 0);
assert.ok(body.indexOf('chiffon shirt') > 0);
assert.ok(body.indexOf('pants') > 0);
assert.ok(body.indexOf('heels') > 0);
assert.ok(body.indexOf('socks') > 0);
});
});
});

it('request with different image width', () => {
const options = {
port: 3000,
host: 'localhost',
method: 'POST',
headers: {
'X-Image-Format': 'svg',
'X-Image-Width': '751'
}
};

const req = http.request(options);
req.write(payload);
req.end();

req.on('response', (response) => {
assert.strictEqual(200, response.statusCode);
let body = '';
response.on('data', (chunk) => {
body += chunk;
});
response.on('end', () => {
assert.ok(body.startsWith('<svg'));
assert.ok(body.endsWith('</svg>'));
assert.ok(body.indexOf('width="751"') > 0);
assert.ok(body.indexOf('height="400"') > 0);
});
});
});

it('request with different image height', () => {
const options = {
port: 3000,
host: 'localhost',
method: 'POST',
headers: {
'X-Image-Format': 'svg',
'X-Image-Height': '432'
}
};

const req = http.request(options);
req.write(payload);
req.end();

req.on('response', (response) => {
assert.strictEqual(200, response.statusCode);
let body = '';
response.on('data', (chunk) => {
body += chunk;
});
response.on('end', () => {
assert.ok(body.startsWith('<svg'));
assert.ok(body.endsWith('</svg>'));
assert.ok(body.indexOf('width="700"') > 0);
assert.ok(body.indexOf('height="432"') > 0);
});
});
});

it('request with different image width and height', () => {
const options = {
port: 3000,
host: 'localhost',
method: 'POST',
headers: {
'X-Image-Format': 'svg',
'X-Image-Width': '765',
'X-Image-Height': '456'
}
};

const req = http.request(options);
req.write(payload);
req.end();

req.on('response', (response) => {
assert.strictEqual(200, response.statusCode);
let body = '';
response.on('data', (chunk) => {
body += chunk;
});
response.on('end', () => {
assert.ok(body.startsWith('<svg'));
assert.ok(body.endsWith('</svg>'));
assert.ok(body.indexOf('width="765"') > 0);
assert.ok(body.indexOf('height="456"') > 0);
});
});
});
});
});

0 comments on commit 7259947

Please sign in to comment.