Files
openstapps/test/main.spec.ts
2021-04-27 15:53:08 +02:00

323 lines
9.3 KiB
TypeScript

/*
* Copyright (C) 2019 StApps
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
// tslint:disable:no-implicit-dependencies
// tslint:disable:no-magic-numbers
// tslint:disable:completed-docs
// tslint:disable:prefer-function-over-method
// tslint:disable:newline-per-chained-call
import {Logger} from '@openstapps/logger';
import chai from 'chai';
import {expect} from 'chai';
import chaiSpies from 'chai-spies';
import {ContainerInfo} from 'dockerode';
import {slow, suite, test, timeout} from '@testdeck/mocha';
import {sslHardeningParameters, protocolHardeningParameters, SSLFilePaths } from './../src/common';
import {containerMatchesRegex, generateUpstreamMap, getGatewayOfStAppsBackend, getTemplateView, generateListener, getContainers} from '../src/main';
import { resolve } from 'path';
import { mkdirSync, writeFileSync, unlinkSync, rmdirSync } from 'fs';
process.on('unhandledRejection', async (error) => {
await Logger.error(error);
process.exit(1);
});
chai.should();
chai.use(chaiSpies);
@suite(timeout(1000), slow(500))
export class MainSpec {
static anyContainerWithExposedPorts: ContainerInfo = {
Command: 'sh',
Created: 1524669882,
HostConfig: {
NetworkMode: 'default',
},
Id: 'e3d3f4d18aceac2780bdb95523845d066ed25c04fc65168a5ddbd37a85671bb7',
Image: 'ubuntu:4',
ImageID: 'sha256:ef9f0c8c4b6f99dd208948c7aae1d042590aa18e05ebeae4f586e4b4beebeac9',
Labels: {},
Mounts: [],
Names: [
'/container_name_1',
],
NetworkSettings: {
Networks: {
bridge: {
Aliases: null,
EndpointID: 'da17549a086ff2c9f622e80de833e6f334afda52c8f07080428640c1716dcd14',
Gateway: '172.18.0.1',
GlobalIPv6Address: '',
GlobalIPv6PrefixLen: 0,
IPAMConfig: null,
IPAddress: '172.18.0.3',
IPPrefixLen: 16,
IPv6Gateway: '',
Links: null,
MacAddress: '03:41:ac:11:00:23',
NetworkID: '947ea5247cc7429e1fdebd5404fa4d15f7c05e6765f2b93ddb3bdb6aaffd1193',
},
},
},
Ports: [
{
IP: '0.0.0.0',
PrivatePort: 80,
PublicPort: 80,
Type: 'tcp',
},
],
State: 'running',
Status: 'Up 3 minutes',
};
static backendContainerWithExposedPorts: ContainerInfo = {
Command: 'node ./bin/www',
Created: 1524669882,
HostConfig: {
NetworkMode: 'deployment_default',
},
Id: 'e3d3f4d18aceac2780bdb95523845d066ed25c04fc65168a5ddbd37a85671bb7',
Image: 'registry.gitlab.com/openstapps/backend/b-tu-typescript-refactor-for-new-tslint-config',
ImageID: 'sha256:ef9f0c8c4b6f99dd208948c7aae1d042590aa18e05ebeae4f586e4b4beebeac9',
Labels: {
'com.docker.compose.config-hash': '91c6e0cebad15951824162c93392b6880b69599692f07798ae8de659c1616a03',
'com.docker.compose.container-number': '1',
'com.docker.compose.oneoff': 'False',
'com.docker.compose.project': 'deployment',
'com.docker.compose.service': 'backend',
'com.docker.compose.version': '1.21.0',
'stapps.version': '1.0.0',
},
Mounts: [],
Names: [
'/deployment_backend_1',
],
NetworkSettings: {
Networks: {
deployment_default: {
Aliases: null,
EndpointID: 'da17549a086ff2c9f622e80de833e6f334afda52c8f07080428640c1716dcd14',
Gateway: '172.18.0.1',
GlobalIPv6Address: '',
GlobalIPv6PrefixLen: 0,
IPAMConfig: null,
IPAddress: '172.18.0.3',
IPPrefixLen: 16,
IPv6Gateway: '',
Links: null,
MacAddress: '03:41:ac:11:00:23',
NetworkID: '947ea5247cc7429e1fdebd5404fa4d15f7c05e6765f2b93ddb3bdb6aaffd1193',
},
},
},
Ports: [
{
IP: '127.0.0.1',
PrivatePort: 3000,
PublicPort: 3000,
Type: 'tcp',
},
],
State: 'running',
Status: 'Up 3 minutes',
};
static sandbox = chai.spy.sandbox();
before() {
MainSpec.sandbox.restore();
}
@test
checkIfContainerDoesNotMatchAnyContainer() {
expect(containerMatchesRegex(
'anyName',
new RegExp('d+'),
MainSpec.anyContainerWithExposedPorts),
).to.be.equal(false);
}
@test
checkIfContainerDoesNotMatchIfVersionIsIncorrect() {
expect(containerMatchesRegex(
'backend',
new RegExp('1\\.4\\.\\d+'),
MainSpec.backendContainerWithExposedPorts),
).to.be.equal(false);
}
@test
checkIfContainerMatches() {
expect(containerMatchesRegex(
'backend',
new RegExp('1\\.0\\.\\d+'),
MainSpec.backendContainerWithExposedPorts),
).to.be.equal(true);
}
@test
async getGatewayOfAnyContainerWithExposedPorts() {
expect(await getGatewayOfStAppsBackend(MainSpec.anyContainerWithExposedPorts)).to.be.equal('0.0.0.0:80');
}
@test
async 'get gateway of backend container'() {
const spy = MainSpec.sandbox.on(console, 'error', () => {
// noop
});
const containerWithoutPorts: Partial<ContainerInfo> = {
Id: 'Foo',
Ports: [],
};
expect(await getGatewayOfStAppsBackend(containerWithoutPorts as ContainerInfo)).to.be.equal('');
expect(spy.__spy.calls[0][0]).to.contain('Container Foo does not advertise any port.');
}
@test
async 'get gateway of backend container without ports'() {
expect(await getGatewayOfStAppsBackend(MainSpec.backendContainerWithExposedPorts)).to.be.equal('127.0.0.1:3000');
}
@test
async upstreamMapCallsLoggerErrorWhenNoMatchingContainerIsFound() {
const spy = MainSpec.sandbox.on(console, 'error', () => {
});
expect(await generateUpstreamMap(
['0\\.8\\.\\d+'],
['1\\.1\\.\\d+'],
[MainSpec.backendContainerWithExposedPorts],
)).to.be.equal(`map $http_x_stapps_version $proxyurl {
default unsupported;
"~0\\.8\\.\\d+" unavailable;
"~1\\.1\\.\\d+" outdated;
}
`);
expect(spy.__spy.calls[0][0]).to.contain('No backend for version');
}
@test
async upstreamMapWithOneActiveVersionAndNoOutdatedOnes() {
expect(await generateUpstreamMap(
['1\\.0\\.\\d+'],
['0\\.8\\.\\d+'],
[MainSpec.backendContainerWithExposedPorts],
)).to.be.equal(`map $http_x_stapps_version $proxyurl {
default unsupported;
"~1\\.0\\.\\d+" 1__0___d_;
"~0\\.8\\.\\d+" outdated;
}
upstream 1__0___d_ {
server 127.0.0.1:3000;
}
`);
}
@test
async testGetContainers() {
try {
await getContainers();
return false;
} catch (e) {
if (e.message.startsWith('No')) {
// Result, if docker is installed
expect(e.message).to.equal(`No running docker containers found.
Please check if docker is running and Node.js can access the docker socket (/var/run/docker.sock)`);
} else {
// Result, if docker is not installed
expect(e.message).to.equal(`connect ENOENT /var/run/docker.sock`);
}
}
return true;
}
@test
async testGetTemplateView() {
try {
let containersWithSameVersion = [MainSpec.backendContainerWithExposedPorts, MainSpec.backendContainerWithExposedPorts];
await getTemplateView(containersWithSameVersion);
return false;
} catch (e) {
expect(e.message).to.equal(
`Multiple backends for one version found.`);
}
return true;
}
@test
createListenerFaultyConfig() {
expect(generateListener({
certificate: 'faultyTest',
certificateChain: 'faultyTest',
certificateKey: 'faultyTest',
dhparam: 'faultyTest',
})).to
.equal(`listen 80 default_server;
${protocolHardeningParameters}
`);
}
@test
createListenerCorrectConfig() {
const testCertDir = resolve(__dirname, 'certs');
mkdirSync(testCertDir);
const certificateFile = resolve(testCertDir, 'ssl.crt');
const certificateKeyFile = resolve(testCertDir, 'ssl.key');
const certificateChainFile = resolve(testCertDir, 'chain.crt');
const dhparamFile = resolve(testCertDir, 'dhparam.pem');
writeFileSync(certificateFile,'Test');
writeFileSync(certificateKeyFile,'Test');
writeFileSync(certificateChainFile,'Test');
writeFileSync(dhparamFile,'Test');
const sslFilePaths: SSLFilePaths = {
certificate: certificateFile,
certificateKey: certificateKeyFile,
certificateChain: certificateChainFile,
dhparam: dhparamFile,
};
expect(generateListener(sslFilePaths)).to.equal(`listen 443 ssl default_server;
ssl_certificate ${sslFilePaths.certificate};
ssl_certificate_key ${sslFilePaths.certificateKey};
ssl_trusted_certificate ${sslFilePaths.certificateChain};
ssl_dhparam ${sslFilePaths.dhparam};
${sslHardeningParameters}
${protocolHardeningParameters}
`);
unlinkSync(certificateFile);
unlinkSync(certificateKeyFile);
unlinkSync(certificateChainFile);
unlinkSync(dhparamFile);
rmdirSync(testCertDir);
}
}