Compare commits

..

48 Commits

Author SHA1 Message Date
Rainer Killinger
aac8e584a9 1.1.1 2022-11-08 15:06:51 +01:00
Rainer Killinger
8d6ea040c1 fix: removing transformations in production 2022-11-08 15:04:53 +01:00
Rainer Killinger
e0f4ce134d docs: update changelog 2022-11-01 19:32:43 +01:00
Rainer Killinger
276e6d3b40 1.1.0 2022-11-01 19:32:42 +01:00
Rainer Killinger
ae0612c6d5 refactor: update dependencies 2022-11-01 19:19:10 +01:00
Rainer Killinger
8aef5b8d5b fix: compatibility with log aggregators 2022-11-01 19:15:32 +01:00
openstappsbot
107d94d499 refactor: update all 2022-10-24 07:16:13 +00:00
Rainer Killinger
7eb727ea27 docs: update changelog 2022-10-12 12:16:22 +02:00
Rainer Killinger
134c2e4c84 1.0.1 2022-10-12 12:16:21 +02:00
openstappsbot
60bc460841 refactor: update typescript-eslint monorepo to v5.40.0 2022-10-11 15:43:41 +00:00
openstappsbot
c8be14f9d8 refactor: update all 2022-10-11 07:22:17 +00:00
Rainer Killinger
e14b1248d5 ci: add cobertura coverage report 2022-08-17 11:32:18 +02:00
Rainer Killinger
734c6ec262 docs: update changelog 2022-08-17 10:17:40 +02:00
Rainer Killinger
6c51f777dd 1.0.0 2022-08-17 10:17:39 +02:00
Rainer Killinger
82a3651fb3 test: move to @testdeck/mocha 2022-08-17 10:13:26 +02:00
Rainer Killinger
e6e75db3e8 refactor: update to typescript 4.4.4 2022-08-17 10:01:47 +02:00
openstappsbot
eeda6ef26c refactor: update all 2022-08-16 07:14:31 +00:00
Rainer Killinger
73e331cd94 docs: update changelog 2022-05-27 16:07:21 +02:00
Rainer Killinger
3ae31f0b2d 0.8.1 2022-05-27 16:07:20 +02:00
Rainer Killinger
15286dd717 refactor: update dependencies 2022-05-27 16:06:38 +02:00
Rainer Killinger
f75e532d44 docs: update changelog 2021-12-14 12:37:03 +01:00
Rainer Killinger
70ce8ea0d6 0.8.0 2021-12-14 12:37:02 +01:00
Rainer Killinger
3c3a571dca refactor: update dependencies 2021-12-14 12:26:04 +01:00
openstappsbot
a11a24225b refactor: update all 2021-08-02 07:09:50 +00:00
Rainer Killinger
3c582cebc6 docs: update changelog 2021-05-18 20:11:55 +02:00
Rainer Killinger
e486d25537 0.7.0 2021-05-18 20:11:50 +02:00
openstappsbot
308965a2fa refactor: update all 2021-05-13 07:10:59 +00:00
Rainer Killinger
78e7d7456b docs: update changelog 2021-04-06 16:30:31 +02:00
Rainer Killinger
822823ea1a 0.6.0 2021-04-06 16:30:30 +02:00
Rainer Killinger
2df53fcd95 refactor: bump version to match npmjs release 2021-04-06 16:30:10 +02:00
Rainer Killinger
e6bc84adf5 refactor: update @openstapps dependencies 2021-04-06 16:26:42 +02:00
Rainer Killinger
30d2108634 refactor: update @types/node for node version 14 2021-04-06 16:05:12 +02:00
Rainer Killinger
9de064756a refactor: update dependencies 2020-03-23 16:32:46 +01:00
Rainer Killinger
edc6e6fad5 feat: add EXIT log level 2020-03-23 16:32:41 +01:00
Rainer Killinger
3fb99ad896 test: add test for log level exclusiveness 2020-03-10 12:49:04 +01:00
Rainer Killinger
100b921488 fix: log level exclusiveness calculation 2020-03-10 12:49:04 +01:00
Karl-Philipp Wulfert
6a1499725c docs: update changelog 2019-07-12 10:55:45 +02:00
Karl-Philipp Wulfert
37e25800dc 0.4.0 2019-07-12 10:55:44 +02:00
Karl-Philipp Wulfert
ddbe00d2a5 feat: add transformations
Fixes #9
2019-07-11 12:53:40 +02:00
Karl-Philipp Wulfert
d05fd8a2a5 docs: update changelog 2019-06-05 11:45:02 +02:00
Karl-Philipp Wulfert
b2053c116e 0.3.1 2019-06-05 11:45:02 +02:00
Karl-Philipp Wulfert
d2cb99f70f fix: correct meta information for imports 2019-06-05 11:43:49 +02:00
Karl-Philipp Wulfert
1e6e951dea docs: update changelog 2019-06-04 15:31:04 +02:00
Karl-Philipp Wulfert
2485b68b4a 0.3.0 2019-06-04 15:31:03 +02:00
Karl-Philipp Wulfert
c30e83086a test: adjust tests 2019-06-03 13:20:44 +02:00
Karl-Philipp Wulfert
15a61f6932 refactor: adjust code to updated dependencies 2019-06-03 13:20:44 +02:00
Karl-Philipp Wulfert
c29420805e build: update dependencies 2019-06-03 13:20:43 +02:00
Karl-Philipp Wulfert
59af9607b6 docs: update changelog 2019-05-27 16:29:33 +02:00
26 changed files with 4492 additions and 3373 deletions

2
.eslintignore Normal file
View File

@@ -0,0 +1,2 @@
resources
openapi

3
.eslintrc.json Normal file
View File

@@ -0,0 +1,3 @@
{
"extends": "@openstapps"
}

View File

@@ -6,7 +6,7 @@ cache:
- node_modules
before_script:
- npm install
- npm ci
stages:
- build
@@ -22,12 +22,17 @@ build:
- lib
test:
variables:
FORCE_COLOR: '1' # see https://github.com/chalk/chalk/issues/203
stage: test
script:
- npm test
coverage: '/Statements[^:]*\:[^:]*\s+([\d\.]+)%/'
artifacts:
paths:
- coverage
reports:
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
audit:
stage: test

View File

@@ -1,9 +1,78 @@
# [1.1.0](https://gitlab.com/openstapps/logger/compare/v1.0.1...v1.1.0) (2022-11-01)
### Bug Fixes
* compatibility with log aggregators ([8aef5b8](https://gitlab.com/openstapps/logger/commit/8aef5b8d5b5fe4ed4ff7f17fd679ebdf83381001))
## [1.0.1](https://gitlab.com/openstapps/logger/compare/v1.0.0...v1.0.1) (2022-10-12)
# [1.0.0](https://gitlab.com/openstapps/logger/compare/v0.8.1...v1.0.0) (2022-08-17)
## [0.8.1](https://gitlab.com/openstapps/logger/compare/v0.8.0...v0.8.1) (2022-05-27)
# [0.8.0](https://gitlab.com/openstapps/logger/compare/v0.7.0...v0.8.0) (2021-12-14)
# [0.7.0](https://gitlab.com/openstapps/logger/compare/v0.6.0...v0.7.0) (2021-05-18)
# [0.6.0](https://gitlab.com/openstapps/logger/compare/v0.4.0...v0.6.0) (2021-04-06)
### Bug Fixes
* log level exclusiveness calculation ([100b921](https://gitlab.com/openstapps/logger/commit/100b921488c3b8846678bf72befb787ca51bd1c7))
### Features
* add EXIT log level ([edc6e6f](https://gitlab.com/openstapps/logger/commit/edc6e6fad5ece784059e406dcedd6d76fe07f74b))
# [0.4.0](https://gitlab.com/openstapps/logger/compare/v0.3.1...v0.4.0) (2019-07-12)
### Features
* add transformations ([ddbe00d](https://gitlab.com/openstapps/logger/commit/ddbe00d2a51430348fcf4e4e29807cc43a37cf49)), closes [#9](https://gitlab.com/openstapps/logger/issues/9)
## [0.3.1](https://gitlab.com/openstapps/logger/compare/v0.3.0...v0.3.1) (2019-06-05)
### Bug Fixes
* correct meta information for imports ([d2cb99f](https://gitlab.com/openstapps/logger/commit/d2cb99f70f713029550d8e535abfa65961486006))
# [0.3.0](https://gitlab.com/openstapps/logger/compare/v0.2.1...v0.3.0) (2019-06-04)
## [0.2.1](https://gitlab.com/openstapps/logger/compare/v0.2.0...v0.2.1) (2019-05-27)
# [0.2.0](https://gitlab.com/openstapps/logger/compare/v0.1.0...v0.2.0) (2019-05-27)
### Features
* add automatic publishing ([d9c4ce4](https://gitlab.com/openstapps/logger/commit/d9c4ce4)), closes [#6](https://gitlab.com/openstapps/logger/issues/6)
* add automatic publishing ([d9c4ce4](https://gitlab.com/openstapps/logger/commit/d9c4ce48dc980f85d43be47a23b5eda73f9f21b1)), closes [#6](https://gitlab.com/openstapps/logger/issues/6)
@@ -16,7 +85,7 @@
### Features
* allow env variables to overwrite smtp config ([3d82c94](https://gitlab.com/openstapps/logger/commit/3d82c94)), closes [#3](https://gitlab.com/openstapps/logger/issues/3)
* allow env variables to overwrite smtp config ([3d82c94](https://gitlab.com/openstapps/logger/commit/3d82c94577ad2013124fab64fc13ce24df579d21)), closes [#3](https://gitlab.com/openstapps/logger/issues/3)
@@ -32,12 +101,12 @@
## [0.0.1](https://gitlab.com/openstapps/logger/compare/911d71c...v0.0.1) (2018-11-28)
## [0.0.1](https://gitlab.com/openstapps/logger/compare/911d71cf3b453dfe302c33d275cb840086288e8e...v0.0.1) (2018-11-28)
### Features
* add logger ([911d71c](https://gitlab.com/openstapps/logger/commit/911d71c))
* add logger ([911d71c](https://gitlab.com/openstapps/logger/commit/911d71cf3b453dfe302c33d275cb840086288e8e))

View File

@@ -3,11 +3,12 @@
[![pipeline status](https://gitlab.com/openstapps/logger/badges/master/pipeline.svg)](https://gitlab.com/openstapps/logger/commits/master)
[![coverage report](https://gitlab.com/openstapps/logger/badges/master/coverage.svg)](https://gitlab.com/openstapps/logger/commits/master)
This is a simple logger for TypeScript projects with colors for console output.
This is a simple logger for TypeScript projects with transformations for the log output.
Logs are only printed if their log level is equal or higher than the defined log level.
## Log Levels
Available log levels are:
- 1 - INFO
- 2 - LOG
@@ -21,19 +22,23 @@ To select your desired log levels add the corresponding numbers and set the valu
For example `STAPPS_LOG_LEVEL=17` is 16 + 1 and would log everything that is `OK` or `INFO`.
If you want to use logger in production (`NODE_ENV=production`) and allow all transports to fail set
`ALLOW_NO_TRANSPORT` to `true`.
If you want to use logger in production (`NODE_ENV=production`) and allow all transports to fail set `ALLOW_NO_TRANSPORT` to `true`.
Additionally setting the environment variable `STAPPS_EXIT_LEVEL` which works in the same manner as `STAPPS_LOG_LEVEL` will terminate your process after logging at the selected level(s) (usefull for integration tests). It will be ignored in afore mentioned productive environments.
## SMTP
This class also provides a simple implementation of an SMTP transport which can be used as a
`TransportWithVerification` for the logger. You can use this to transport errors of the logger or to transport mails
of your own monitoring solution.
### Usage
You can instatiate it with a config or it will check for a config in the environment variables. Environment variables
can overwrite the actual config values.
Environment variables are:
* SMTP_AUTH_USER: SMTP username
* SMTP_AUTH_PASSWORD: SMTP password
* SMTP_HOST: SMTP host
@@ -43,3 +48,15 @@ Environment variables are:
* SMTP_SENDER_MAIL: sender of the mail
* SMTP_SENDER_NAME: name of the sender
* SMTP_SECURE: `true` to enable tls
## Transformations
By default the logger will only add the log level to the message. It will replace newlines with spaces and might skip some of your choosen Transformers when in production (`NODE_ENV=production`) for compatibility reasons with existing log aggregators and analyzers.
You can change this behavior by setting other Transformers via `Logger.setTransformations`. If you do so, mind the order of the transformers.
You may choose from the following:
* `AddLogLevel`, which prepends the output with the log level
* `Colorize`, which colorizes the output according to the log level
* `Timestamp`, which prepends a timestamp to the output

5734
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "@openstapps/logger",
"version": "0.2.1",
"version": "1.1.1",
"description": "A cli logger with colors, loglevels and the possibility to use a transport system for errors",
"repository": {
"type": "git",
@@ -8,28 +8,69 @@
},
"license": "GPL-3.0-only",
"scripts": {
"build": "npm run tslint && npm run compile",
"build": "npm run lint && npm run compile",
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0 && git add CHANGELOG.md && git commit -m 'docs: update changelog'",
"check-configuration": "openstapps-configuration",
"compile": "rimraf lib && tsc && prepend lib/cli.js '#!/usr/bin/env node\n'",
"documentation": "typedoc --includeDeclarations --mode modules --out docs --readme README.md --listInvalidSymbolLinks src",
"documentation": "typedoc --name \"@openstapps/logger\" --includeVersion --out docs --readme README.md --listInvalidSymbolLinks --entryPointStrategy expand src",
"postversion": "npm run changelog",
"prepublishOnly": "npm ci && npm run build",
"test": "nyc mocha --require ts-node/register --ui mocha-typescript 'test/*.ts'",
"tslint": "tslint 'src/**/*.ts'"
"preversion": "npm run prepublishOnly",
"push": "git push && git push origin \"v$npm_package_version\"",
"test": "nyc mocha 'test/**/*.spec.ts'",
"lint": "eslint --ext .ts src/"
},
"author": "Karl-Philipp Wulfert <krlwlfrt@gmail.com>",
"contributors": [
"Anselm Stordeur <anselmstordeur@gmail.com>",
"Jovan Krunic <jovan.krunic@gmail.com>"
"Jovan Krunic <jovan.krunic@gmail.com>",
"Rainer Killinger <mail-openstapps@killinger.co>"
],
"typings": "./lib/Logger.d.ts",
"main": "./lib/Logger.js",
"typings": "./lib/logger.d.ts",
"main": "./lib/logger.js",
"dependencies": {
"@types/node": "14.18.32",
"@types/nodemailer": "6.4.6",
"chalk": "4.1.2",
"flatted": "3.2.7",
"moment": "2.29.4",
"nodemailer": "6.8.0"
},
"devDependencies": {
"@openstapps/configuration": "0.33.0",
"@openstapps/eslint-config": "1.1.0",
"@testdeck/mocha": "0.3.0",
"@types/chai": "4.3.3",
"@types/chai-as-promised": "7.1.5",
"@types/chai-spies": "1.0.3",
"@types/mocha": "10.0.0",
"@typescript-eslint/eslint-plugin": "5.42.0",
"@typescript-eslint/parser": "5.42.0",
"chai": "4.3.6",
"chai-as-promised": "7.1.1",
"chai-spies": "1.0.0",
"conventional-changelog-cli": "2.2.2",
"eslint": "8.26.0",
"eslint-config-prettier": "8.5.0",
"eslint-plugin-jsdoc": "39.4.0",
"eslint-plugin-prettier": "4.2.1",
"eslint-plugin-unicorn": "43.0.2",
"mocha": "10.1.0",
"nyc": "15.1.0",
"prepend-file-cli": "1.0.6",
"prettier": "2.7.1",
"rimraf": "3.0.2",
"ts-node": "10.9.1",
"tslint": "6.1.3",
"typedoc": "0.22.18",
"typescript": "4.4.4"
},
"nyc": {
"all": true,
"branches": 95,
"check-coverage": true,
"exclude": [
"src/SMTP.ts",
"src/smtp.ts",
"src/cli.ts"
],
"extension": [
@@ -42,36 +83,13 @@
"lines": 95,
"per-file": true,
"reporter": [
"cobertura",
"html",
"text-summary"
],
"require": [
"ts-node/register"
],
"statements": 95
},
"devDependencies": {
"@openstapps/configuration": "0.16.1",
"@types/chai": "4.1.7",
"@types/chai-as-promised": "7.1.0",
"@types/mocha": "5.2.6",
"@types/sinon": "7.0.11",
"chai": "4.2.0",
"chai-as-promised": "7.1.1",
"conventional-changelog-cli": "2.0.17",
"mocha": "6.1.4",
"mocha-typescript": "1.1.17",
"nyc": "14.1.1",
"prepend-file-cli": "1.0.6",
"rimraf": "2.6.3",
"sinon": "7.3.2",
"ts-node": "8.1.0",
"tslint": "5.16.0",
"typedoc": "0.14.2",
"typescript": "3.4.5"
},
"dependencies": {
"@types/node": "10.14.7",
"@types/nodemailer": "6.1.0",
"chalk": "2.4.2",
"flatted": "2.0.0",
"nodemailer": "6.1.1"
}
}

View File

@@ -1,206 +0,0 @@
/*
* Copyright (C) 2018, 2019 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* 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 General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import chalk from 'chalk';
import {stringify} from 'flatted';
import {isNodeEnvironment, isProductiveNodeEnvironment} from './common';
import {Transport} from './Transport';
/**
* Logger with colors, loglevel and transport
*
* Log level can be defined by setting the environment variable STAPPS_LOG_LEVEL to a valid log level. Log levels are
* set in a binary way. For example STAPPS_LOG_LEVEL=12 does result in logs only for `Logger.warn` and `Logger.error`.
*
* Log levels in that order are:
* ```
* INFO: 1
* LOG: 2
* WARN: 4
* ERROR: 8
* OK: 16
* ```
*/
export class Logger {
/**
* Log levels
*/
private static logLevels = [
'INFO',
'LOG',
'WARN',
'ERROR',
'OK',
];
/**
* Transport for errors
*/
private static transport?: Transport;
/**
* Check if intended log level is allowed in environment log level
*
* @param logLevel
*/
private static checkLogLevel(logLevel: string): boolean {
const logLevelNumber = Math.pow(2, Logger.logLevels.indexOf(logLevel) + 1) - 1;
/* tslint:disable-next-line:no-bitwise */
return !!(Logger.getLogLevel() & logLevelNumber);
}
/**
* Return log level from environment
*/
private static getLogLevel(): number {
if (isNodeEnvironment() && typeof process.env.STAPPS_LOG_LEVEL !== 'undefined') {
// Node.js environment exists
return parseInt(process.env.STAPPS_LOG_LEVEL, 10);
} else if (typeof window !== 'undefined' && typeof (window as any).STAPPS_LOG_LEVEL === 'number') {
// browser environment exists
return (window as any).STAPPS_LOG_LEVEL;
}
// Log everything
return 31;
}
/**
* Log an error
*
* @param args Arguments to log
*/
public static async error(...args: any[]): Promise<string | void> {
if (!Logger.checkLogLevel('ERROR')) {
return;
}
/* tslint:disable-next-line:no-console */
console.error(chalk.bold.red(`[ERROR] ${Logger.stringifyArguments(...args)}`));
if (isProductiveNodeEnvironment()) {
if (typeof Logger.transport !== 'undefined') {
return Logger.transport.send('Error', Logger.stringifyArguments(...args));
} else if (!process.env.ALLOW_NO_TRANSPORT) {
throw new Error(`Error couldn't be transported! Please set a transport or set ALLOW_NO_TRANSPORT='true'.`);
}
}
}
/**
* Log an information
*
* @param args Arguments to log
*/
public static info(...args: any[]): void {
if (!Logger.checkLogLevel('INFO')) {
return;
}
/* tslint:disable-next-line:no-console */
console.info(chalk.cyan(`[INFO] ${Logger.stringifyArguments(args)}`));
}
/**
* Check if the logger is initialized correctly
*/
public static initialized(): void {
if (isProductiveNodeEnvironment() && typeof Logger.transport === 'undefined') {
if (!process.env.ALLOW_NO_TRANSPORT) {
throw new Error(`Productive environment doesn't set a transport for error notifications.`);
} else {
/* tslint:disable-next-line:no-console */
console.warn(chalk.yellow(`Productive environment doesn't set a transport for error notifications.`));
}
}
}
/**
* Log something
*
* @param args Arguments to log
*/
public static log(...args: any[]): void {
if (!this.checkLogLevel('LOG')) {
return;
}
/* tslint:disable-next-line:no-console */
console.log(chalk.white(`[LOG] ${Logger.stringifyArguments(args)}`));
}
/**
* Log something successful
*
* @param args Arguments to log
*/
public static ok(...args: any[]): void {
if (!this.checkLogLevel('OK')) {
return;
}
/* tslint:disable-next-line:no-console */
console.log(chalk.bold.green(`[OK] ${Logger.stringifyArguments(args)}`));
}
/**
* Set a transport
*
* @param transport Transport to set
*/
public static setTransport(transport?: Transport) {
Logger.transport = transport;
}
/**
* Stringify a list of arguments
*
* @param args Arguments to stringify
*/
public static stringifyArguments(...args: any[]): string {
const result: string[] = [];
args.forEach((argument) => {
const type = typeof argument;
if (['string', 'number'].indexOf(type) !== -1) {
result.push(argument);
} else if (argument instanceof Error) {
result.push(argument.message);
if (typeof argument.stack !== 'undefined') {
result.push(argument.stack);
}
} else {
result.push(stringify(argument, null, 2));
}
});
return result.join(', ');
}
/**
* Log a warning
*
* @param args Arguments to log
*/
public static warn(...args: any[]): void {
if (!this.checkLogLevel('WARN')) {
return;
}
/* tslint:disable-next-line:no-console */
console.warn(chalk.yellow(`[WARN] ${Logger.stringifyArguments(args)}`));
}
}

View File

@@ -12,42 +12,51 @@
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {Transport, VerifiableTransport} from './transport';
/**
* A recursive partial object
*
* Copied from https://stackoverflow.com/a/51365037
*/
import {Transport, VerifiableTransport} from './Transport';
export type RecursivePartial<T> = {
[P in keyof T]?: T[P] extends Array<infer U> ?
Array<RecursivePartial<U>> :
T[P] extends object ? RecursivePartial<T[P]> : T[P];
[P in keyof T]?: T[P] extends Array<infer U>
? Array<RecursivePartial<U>>
: T[P] extends object
? RecursivePartial<T[P]>
: T[P];
};
/**
* Deletes all properties that are undefined from an object
* @param obj
*
* @param object Object to delete undefined properties from
*/
export function deleteUndefinedProperties(obj: any) {
export function deleteUndefinedProperties(object: unknown): unknown {
// return atomic data types and arrays (recursion anchor)
if (typeof obj !== 'object' || Array.isArray(obj)) {
return obj;
if (typeof object !== 'object' || Array.isArray(object)) {
return object;
}
// check each key
Object.keys(obj).forEach((key) => {
if (typeof obj[key] === 'undefined') {
for (const key in object) {
/* istanbul ignore if */
if (!object.hasOwnProperty(key)) {
continue;
}
const indexedObject = object as {[k: string]: unknown};
if (typeof indexedObject[key] === 'undefined') {
// delete undefined keys
delete obj[key];
delete indexedObject[key];
} else {
// check recursive
obj[key] = deleteUndefinedProperties(obj[key]);
indexedObject[key] = deleteUndefinedProperties(indexedObject[key]);
}
});
}
return obj;
return object;
}
/**
@@ -61,9 +70,11 @@ export function isNodeEnvironment(): boolean {
* Checks if environment is productive
*/
export function isProductiveEnvironment(): boolean {
return typeof process.env === 'object'
&& typeof process.env.NODE_ENV !== 'undefined'
&& process.env.NODE_ENV === 'production';
return (
typeof process.env === 'object' &&
typeof process.env.NODE_ENV !== 'undefined' &&
process.env.NODE_ENV === 'production'
);
}
/**

348
src/logger.ts Normal file
View File

@@ -0,0 +1,348 @@
/*
* Copyright (C) 2019-2020 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* 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 General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {stringify} from 'flatted';
import {isNodeEnvironment, isProductiveEnvironment, isProductiveNodeEnvironment} from './common';
import {Transformation} from './transformation';
import {AddLogLevel} from './transformations/add-log-level';
import {Transport} from './transport';
/**
* Check if something has property STAPPS_LOG_LEVEL
*
* @param something Something to check
*/
// tslint:disable-next-line:completed-docs
function hasStAppsLogLevel(something: object): something is {STAPPS_LOG_LEVEL: number} {
return 'STAPPS_LOG_LEVEL' in something;
}
/**
* Check if something has property STAPPS_EXIT_LEVEL
*
* @param something Something to check
*/
// tslint:disable-next-line:completed-docs
function hasStAppsExitLevel(something: object): something is {STAPPS_EXIT_LEVEL: number} {
return 'STAPPS_EXIT_LEVEL' in something;
}
/**
* A level descriptor for either log or exit level
*/
export type Level = 'LOG' | 'EXIT';
/**
* A log level
*/
export type LogLevel = 'INFO' | 'LOG' | 'WARN' | 'ERROR' | 'OK';
/**
* A logger with transports and transformations
*
* Log level can be defined by setting the environment variable STAPPS_LOG_LEVEL to a valid log level. Log levels are
* set in a binary way. For example STAPPS_LOG_LEVEL=12 does result in logs only for `Logger.warn` and `Logger.error`.
*
* Log levels in that order are:
* ```
* INFO: 1
* LOG: 2
* WARN: 4
* ERROR: 8
* OK: 16
* ```
*/
export class Logger {
/**
* Base of binary system
*/
private static readonly binaryBase = 2;
/**
* Log levels
*/
private static readonly logLevels: LogLevel[] = ['INFO', 'LOG', 'WARN', 'ERROR', 'OK'];
/**
* Log level sum, equivalent to all log levels enabled
*/
private static readonly logLevelSum = Math.pow(Logger.binaryBase, Logger.logLevels.length) - 1;
/**
* Transformers for log output
*/
private static transformations?: Transformation[] = [new AddLogLevel()];
/**
* Transport for errors
*/
private static transport?: Transport;
/**
* Apply transformations to an output
* Will strip newlines in production environment
*
* @param logLevel Log level of the output
* @param output Output to apply transformations to
*/
private static applyTransformers(logLevel: LogLevel, output: string): string {
if (isProductiveEnvironment()) {
output = output.replace(/[\n\r]/g, ' ');
}
if (!Array.isArray(Logger.transformations) || Logger.transformations.length === 0) {
return output;
}
let transformedOutput = output;
for (const transformation of Logger.transformations) {
transformedOutput = transformation.transform(logLevel, transformedOutput);
}
return transformedOutput;
}
/**
* Check if intended exit level is allowed in environment exit level
*
* @param exitLevel Log level to check
*/
private static checkExitLevel(exitLevel: LogLevel): boolean {
if (Logger.getLevel('EXIT') === 0) {
return false;
}
// tslint:disable-next-line:no-bitwise
return (Logger.getLevel('EXIT') & Logger.logLevelNumber(exitLevel)) === Logger.logLevelNumber(exitLevel);
}
/**
* Check if intended log level is allowed in environment log level
*
* @param logLevel Log level to check
*/
private static checkLogLevel(logLevel: LogLevel): boolean {
// tslint:disable-next-line:no-bitwise
return (Logger.getLevel('LOG') & Logger.logLevelNumber(logLevel)) === Logger.logLevelNumber(logLevel);
}
/**
* Notify about exit and end process
*/
private static exit(): void {
if (isProductiveNodeEnvironment()) {
return;
}
// eslint-disable-next-line no-console
console.error(
Logger.applyTransformers('ERROR', `exiting as of used exit level ${Logger.getLevel('EXIT')} !`),
);
process.exit(-1);
}
/**
* Return log level from environment
*/
private static getLevel(level: Level): number {
if (typeof window !== 'undefined') {
// browser environment exists
if (hasStAppsLogLevel(window)) {
return window.STAPPS_LOG_LEVEL;
}
if (hasStAppsExitLevel(window)) {
return window.STAPPS_EXIT_LEVEL;
}
}
const environmentLevel = level === 'LOG' ? process.env.STAPPS_LOG_LEVEL : process.env.STAPPS_EXIT_LEVEL;
if (isNodeEnvironment() && typeof environmentLevel !== 'undefined') {
// Node.js environment exists
return Number.parseInt(environmentLevel, 10);
}
// Fallback to log everything, or not exiting
switch (level) {
case 'LOG':
return Logger.logLevelSum;
case 'EXIT':
return 0;
}
}
/**
* Get number of specific log level
*
* @param logLevel Log level to check
*/
private static logLevelNumber(logLevel: LogLevel): number {
return Math.pow(Logger.binaryBase, Logger.logLevels.indexOf(logLevel));
}
/**
* Log an error
*
* @param arguments_ Arguments to log
*/
public static async error(...arguments_: unknown[]): Promise<string | void> {
if (!Logger.checkLogLevel('ERROR')) {
return;
}
// eslint-disable-next-line no-console
console.error(Logger.applyTransformers('ERROR', Logger.stringifyArguments(...arguments_)));
if (isProductiveNodeEnvironment()) {
if (typeof Logger.transport !== 'undefined') {
return Logger.transport.send('Error', Logger.stringifyArguments(...arguments_));
}
if (process.env.ALLOW_NO_TRANSPORT !== 'true') {
throw new Error(
`Error couldn't be transported! Please set a transport or set ALLOW_NO_TRANSPORT='true'.`,
);
}
}
if (Logger.checkExitLevel('ERROR')) {
Logger.exit();
}
}
/**
* Log an information
*
* @param arguments_ Arguments to log
*/
public static info(...arguments_: unknown[]): void {
if (!Logger.checkLogLevel('INFO')) {
return;
}
// eslint-disable-next-line no-console
console.info(Logger.applyTransformers('INFO', Logger.stringifyArguments(...arguments_)));
if (Logger.checkExitLevel('INFO')) {
Logger.exit();
}
}
/**
* Check if the logger is initialized correctly
*/
public static initialized(): void {
if (isProductiveNodeEnvironment() && typeof Logger.transport === 'undefined') {
if (process.env.ALLOW_NO_TRANSPORT !== 'true') {
throw new Error(`Productive environment doesn't set a transport for error notifications.`);
}
Logger.warn(`Productive environment doesn't set a transport for error notifications.`);
}
}
/**
* Log something
*
* @param arguments_ Arguments to log
*/
public static log(...arguments_: unknown[]): void {
if (!Logger.checkLogLevel('LOG')) {
return;
}
// eslint-disable-next-line no-console
console.log(Logger.applyTransformers('LOG', Logger.stringifyArguments(...arguments_)));
if (Logger.checkExitLevel('LOG')) {
Logger.exit();
}
}
/**
* Log something successful
*
* @param arguments_ Arguments to log
*/
public static ok(...arguments_: unknown[]): void {
if (!Logger.checkLogLevel('OK')) {
return;
}
// eslint-disable-next-line no-console
console.log(Logger.applyTransformers('OK', Logger.stringifyArguments(...arguments_)));
if (Logger.checkExitLevel('OK')) {
Logger.exit();
}
}
/**
* Set transformations for log output
*
* @param transformations List of transformations
*/
public static setTransformations(transformations: Transformation[]) {
const transforms = transformations.filter(transform =>
isProductiveEnvironment() ? transform.useInProduction === true : true,
);
Logger.transformations = transforms;
}
/**
* Set a transport
*
* @param transport Transport to set
*/
public static setTransport(transport?: Transport) {
Logger.transport = transport;
}
/**
* Stringify a list of arguments
*
* @param arguments_ Arguments to stringify
*/
public static stringifyArguments(...arguments_: unknown[]): string {
const result: string[] = [];
for (const argument of arguments_) {
if (typeof argument === 'string' || typeof argument === 'number') {
result.push(argument.toString());
} else if (argument instanceof Error) {
result.push(argument.message);
if (typeof argument.stack !== 'undefined') {
result.push(argument.stack);
}
} else {
result.push(stringify(argument, undefined, 2));
}
}
return result.join(', ');
}
/**
* Log a warning
*
* @param arguments_ Arguments to log
*/
public static warn(...arguments_: unknown[]): void {
if (!Logger.checkLogLevel('WARN')) {
return;
}
// eslint-disable-next-line no-console
console.warn(Logger.applyTransformers('WARN', Logger.stringifyArguments(...arguments_)));
if (Logger.checkExitLevel('WARN')) {
Logger.exit();
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2018, 2019 StApps
* Copyright (C) 2019 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
@@ -15,23 +15,56 @@
import * as nodemailer from 'nodemailer';
import {MailOptions} from 'nodemailer/lib/sendmail-transport';
import {deleteUndefinedProperties, isProductiveEnvironment, RecursivePartial} from './common';
import {VerifiableTransport} from './Transport';
import {VerifiableTransport} from './transport';
/**
* A configuration of the transport used to send mails via SMTP
*/
export interface SMTPConfig {
/**
* Auth configuration
*/
auth: {
/**
* Password for login
*/
password: string;
/**
* User for login
*/
user: string;
};
/**
* List of "carbon copy" recipients
*/
cc?: string[];
/**
* SMTP host server
*/
host: string;
/**
* SMTP port
*/
port: number;
/**
* List of recipients
*/
recipients: string[];
/**
* Whether or not to establish a secure connection
*/
secure?: boolean;
/**
* Sender configuration
*/
sender: {
/**
* Mail of sender
*/
mail: string;
/**
* Name of sender
*/
name?: string;
};
}
@@ -40,7 +73,9 @@ export interface SMTPConfig {
* An implementation of mail transport via SMTP
*/
export class SMTP extends VerifiableTransport {
/**
* Singleton instance
*/
private static _instance: SMTP;
/**
@@ -51,8 +86,14 @@ export class SMTP extends VerifiableTransport {
/**
* Who is using this service
*/
private from: {
private readonly from: {
/**
* Mail of sender
*/
mail: string;
/**
* Name of sender
*/
name?: string;
};
@@ -64,7 +105,7 @@ export class SMTP extends VerifiableTransport {
/**
* Connection to SMTP server
*/
private transportAgent: nodemailer.Transporter;
private readonly transportAgent: nodemailer.Transporter;
/**
* Set to true if the transport was verified
@@ -84,30 +125,31 @@ export class SMTP extends VerifiableTransport {
* SMTP_SENDER_MAIL: sender of the mail
* SMTP_SENDER_NAME: name of the sender
* SMTP_SECURE: `true` to enable tls
* @param config {SMTPConfig}
* @return {Transport}
*
* @param config SMTP config for instance
*/
public static getInstance(config?: SMTPConfig): SMTP | undefined {
// if an instance of SMTP already exists
if (this._instance) {
return this._instance;
if (typeof SMTP._instance !== 'undefined') {
return SMTP._instance;
}
// monitoring is not required -> SMTP init can fail
if (!isProductiveEnvironment() || process.env.ALLOW_NO_TRANSPORT === 'true') {
try {
this._instance = new this(config);
} catch (err) {
/* tslint:disable-next-line:no-console */
SMTP._instance = new SMTP(config);
} catch {
// eslint-disable-next-line no-console
console.warn('SMTP config failed.');
return;
}
} else {
// monitoring is required -> SMTP will throw error if config is invalid
this._instance = new this(config);
SMTP._instance = new SMTP(config);
}
return this._instance;
return SMTP._instance;
}
/**
@@ -117,110 +159,112 @@ export class SMTP extends VerifiableTransport {
* For more information please consider reading
* https://stackoverflow.com/a/2071250
*
* @param {string} address
* @return {boolean}
* @param address Address to validate
*/
public static isValidEmailAddress(address: string): boolean {
// tslint:disable-next-line:max-line-length
return /^(([^<>()\[\].,;:\s@"]+(\.[^<>()\[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$/i.test(address);
return /^(([^<>()\[\].,;:\s@"]+(\.[^<>()\[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$/i.test(
address,
);
}
/**
* Checks a list of mail addresses for validity.
* @param {string[]} recipients
* @return {string[]}
* Checks a list of mail addresses for validity
*
* @param recipients List of recipients to check
*/
public static isValidRecipientsList(recipients: string[] | undefined): boolean {
return Array.isArray(recipients) && recipients.length > 0 && recipients.every(this.isValidEmailAddress);
return Array.isArray(recipients) && recipients.length > 0 && recipients.every(SMTP.isValidEmailAddress);
}
/**
* Creates an SMTP connection. WARNING: This class is supposed to be used as a singleton. You should never
* call `new SMTP()`
* @param {SMTPConfig} config
* Creates an SMTP connection.
*
* WARNING: This class is supposed to be used as a singleton. You should never call `new SMTP()`
*
* @param smtpConfig SMTP config
*/
private constructor(config?: SMTPConfig) {
private constructor(smtpConfig?: SMTPConfig) {
// create a partial config from environment variables that can overwrite the given config
const envConfig: RecursivePartial<SMTPConfig> = {
const environmentConfig: RecursivePartial<SMTPConfig> = {
auth: {
password: process.env.SMTP_AUTH_PASSWORD,
user: process.env.SMTP_AUTH_USER,
},
cc: ((typeof process.env.SMTP_CC !== 'undefined') ? (process.env.SMTP_CC as string).split(',') : []),
cc: typeof process.env.SMTP_CC !== 'undefined' ? (process.env.SMTP_CC as string).split(',') : [],
host: process.env.SMTP_HOST,
port: (typeof process.env.SMTP_PORT !== 'undefined') ? parseInt(process.env.SMTP_PORT, 10) : undefined,
recipients: (typeof process.env.SMTP_RECIPIENTS !== 'undefined') ?
(process.env.SMTP_RECIPIENTS).split(',') :
[],
secure: (typeof process.env.SMTP_SECURE !== 'undefined') ? (process.env.SMTP_SECURE === 'true') : false,
port:
typeof process.env.SMTP_PORT !== 'undefined' ? Number.parseInt(process.env.SMTP_PORT, 10) : undefined,
recipients:
typeof process.env.SMTP_RECIPIENTS !== 'undefined' ? process.env.SMTP_RECIPIENTS.split(',') : [],
secure: typeof process.env.SMTP_SECURE !== 'undefined' ? process.env.SMTP_SECURE === 'true' : false,
sender: {
mail: process.env.SMTP_SENDER_MAIL,
name: process.env.SMTP_SENDER_NAME,
},
};
// @ts-ignore
config = {
...config,
const config = {
...smtpConfig,
// deleting undefined properties so the actual config doesn't get overwritten by undefined values
...deleteUndefinedProperties(envConfig),
};
...(deleteUndefinedProperties(environmentConfig) as object),
} as SMTPConfig;
if (typeof config!.host === 'undefined') {
throw new Error(
if (typeof config.host === 'undefined') {
throw new TypeError(
'SMTP configuration needs a host. Add it to the config or use environment variables (SMTP_HOST).',
);
}
if (typeof config!.port === 'undefined' || isNaN(config!.port)) {
throw new Error(
if (typeof config.port === 'undefined' || Number.isNaN(config.port)) {
throw new TypeError(
'SMTP configuration needs a port. Add it to the config or use environment variables (SMTP_PORT).',
);
}
if (typeof config!.auth !== 'object') {
throw new Error(
if (typeof config.auth !== 'object') {
throw new TypeError(
'SMTP configuration needs an auth object.' +
'Add it to the config or use environment variables (SMTP_AUTH_USER, SMTP_AUTH_PASSWORD).',
'Add it to the config or use environment variables (SMTP_AUTH_USER, SMTP_AUTH_PASSWORD).',
);
}
if (typeof config!.auth.user === 'undefined') {
throw new Error(
if (typeof config.auth.user === 'undefined') {
throw new TypeError(
'SMTP auth configuration needs a user. Add it to the config or use environment variables (SMTP_AUTH_USER).',
);
}
if (typeof config!.auth.password === 'undefined') {
throw new Error(
if (typeof config.auth.password === 'undefined') {
throw new TypeError(
'SMTP auth configuration needs a password.' +
'Add it to the config or use environment variables (SMTP_AUTH_PASSWORD).',
'Add it to the config or use environment variables (SMTP_AUTH_PASSWORD).',
);
}
if (Array.isArray(config!.recipients) && config!.recipients.length < 1) {
if (Array.isArray(config.recipients) && config.recipients.length === 0) {
throw new Error(
'SMTP configuration needs recipients. Add it to the config or use environment variables (SMTP_RECIPIENTS).',
);
}
if (typeof config!.sender.mail === 'undefined') {
throw new Error(
if (typeof config.sender.mail === 'undefined') {
throw new TypeError(
'SMTP configuration needs a sender. Add it to the config or use environment variables (SMTP_SENDER_MAIL).',
);
}
super();
if (SMTP.isValidRecipientsList(config!.recipients)) {
this.recipients = config!.recipients;
if (SMTP.isValidRecipientsList(config.recipients)) {
this.recipients = config.recipients;
} else {
throw new Error('Invalid recipients found');
}
if (typeof config!.cc !== 'undefined') {
if (SMTP.isValidRecipientsList(config!.cc)) {
this.cc = config!.cc!;
if (typeof config.cc !== 'undefined') {
if (SMTP.isValidRecipientsList(config.cc)) {
this.cc = config.cc;
} else {
throw new Error('Invalid cc recipients found');
}
@@ -228,25 +272,24 @@ export class SMTP extends VerifiableTransport {
this.cc = [];
}
this.from = config!.sender;
this.from = config.sender;
this.verified = false;
// creating transport with configuration
this.transportAgent = nodemailer.createTransport({
auth: {
pass: config!.auth.password,
user: config!.auth.user,
pass: config.auth.password,
user: config.auth.user,
},
host: config!.host,
port: config!.port,
secure: typeof config!.secure !== 'undefined' ? config!.secure : false,
host: config.host,
port: config.port,
secure: typeof config.secure !== 'undefined' ? config.secure : false,
});
}
/**
* Check if instance was verified at least once
* @returns {boolean}
*/
public isVerified(): boolean {
return this.verified;
@@ -255,14 +298,15 @@ export class SMTP extends VerifiableTransport {
/**
* Sends a preconfigured mail with recipients and sender configured on
* creation of the class (set by environment or on creation of this class)
* @param {string} subject
* @param {string} message
*
* @param subject Subject of the mail
* @param message message of the mail
*/
public async send(subject: string, message: string): Promise<string> {
return await this.sendMail({
return this.sendMail({
cc: this.cc,
// use an address block if name is available, mail otherwise
from: (typeof this.from.name !== 'string') ? `${this.from.name} <${this.from.mail}>` : this.from.mail,
from: typeof this.from.name !== 'string' ? `${this.from.name} <${this.from.mail}>` : this.from.mail,
subject: subject,
text: message,
to: this.recipients,
@@ -271,7 +315,8 @@ export class SMTP extends VerifiableTransport {
/**
* Sends a mail object
* @param {MailOptions} mail
*
* @param mail Mail to send
*/
public async sendMail(mail: MailOptions): Promise<string> {
// info is the response of the smtp server
@@ -298,36 +343,36 @@ export class SMTP extends VerifiableTransport {
* @returns true if the transport is valid
*/
public async verify(): Promise<boolean> {
let verificationSuccessfull: boolean = false;
let verificationSuccessfull = false;
try {
verificationSuccessfull = await this.transportAgent.verify();
} catch (err) {
} catch (error) {
if (!isProductiveEnvironment() || process.env.ALLOW_NO_TRANSPORT !== 'true') {
throw err;
} else {
/* tslint:disable-next-line:no-console */
console.warn(
'SMTP verification error was ignored, because tranport failures are allowed: ',
err.message,
);
throw error;
}
// eslint-disable-next-line no-console
console.warn(
'SMTP verification error was ignored, because tranport failures are allowed:',
(error as Error).message,
);
}
if (!verificationSuccessfull) {
if (!isProductiveEnvironment() || process.env.ALLOW_NO_TRANSPORT !== 'true') {
throw new Error(
'Verification of SMTP transport failed.' +
'If you want to ignore this error set' +
'`NODE_ENV=dev` or `ALLOW_NO_TRANSPORT=true`',
'If you want to ignore this error set' +
'`NODE_ENV=dev` or `ALLOW_NO_TRANSPORT=true`',
);
} else {
/* tslint:disable-next-line:no-console */
console.warn('SMTP verification error was ignored, because tranport failures are allowed.');
}
// eslint-disable-next-line no-console
console.warn('SMTP verification error was ignored, because tranport failures are allowed.');
}
this.verified = verificationSuccessfull;
return verificationSuccessfull;
}
}

33
src/transformation.ts Normal file
View File

@@ -0,0 +1,33 @@
/*
* Copyright (C) 2019 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* 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 General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {LogLevel} from './logger';
/**
* A transformer for log output
*/
export interface Transformation {
/**
* Indicates if this transformation is stripped in production environments
*/
useInProduction: boolean;
/**
* Transform an output
*
* @param logLevel Log level to transform output for
* @param output Output to transform
*/
transform(logLevel: LogLevel, output: string): string;
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (C) 2019 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* 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 General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {LogLevel} from '../logger';
import {Transformation} from '../transformation';
/**
* Transformation that adds the log level to output
*/
export class AddLogLevel implements Transformation {
/**
* Keep this transformation in production environments
*/
useInProduction = true;
/**
* Add log level to output
*
* @param logLevel Log level to add to output
* @param output Output to colorize
*/
// tslint:disable-next-line:prefer-function-over-method
transform(logLevel: LogLevel, output: string): string {
return `[${logLevel}] ${output}`;
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright (C) 2019-2020 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* 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 General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import chalk from 'chalk';
import {LogLevel} from '../logger';
import {Transformation} from '../transformation';
/**
* Transformation that colorizes log output
*/
export class Colorize implements Transformation {
/**
* Skip this transformation in production environments
*/
useInProduction = false;
/**
* Instantiate a new colorize transformation
*
* @param logLevelToColor Map from log level to color transformation to apply
*/
constructor(
private readonly logLevelToColor: {[k in LogLevel]: chalk.Chalk} = {
ERROR: chalk.bold.red,
INFO: chalk.cyan,
LOG: chalk.white,
OK: chalk.bold.green,
WARN: chalk.yellow,
},
) {
// noop
}
/**
* Colorize log output
*
* @param logLevel Log level to choose color for
* @param output Output to colorize
*/
transform(logLevel: LogLevel, output: string): string {
return this.logLevelToColor[logLevel](output);
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright (C) 2019 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* 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 General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {LogLevel} from '../logger';
import {Transformation} from '../transformation';
/**
* Transformation that adds a timestamp to output
*/
export class Timestamp implements Transformation {
/**
* Keep this transformation in production environments
*/
useInProduction = true;
/**
* Add timestamp to output
*
* @param _logLevel Log level to add timestamp to output for
* @param output Output to add timestamp to
*/
transform(_logLevel: LogLevel, output: string): string {
return `[${new Date().toISOString()}] ${output}`;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2018, 2019 StApps
* Copyright (C) 2019 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.

View File

@@ -1,293 +0,0 @@
/*
* Copyright (C) 2018, 2019 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* 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 General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import * as chai from 'chai';
import {expect} from 'chai';
import * as chaiAsPromised from 'chai-as-promised';
import {slow, suite, test, timeout} from 'mocha-typescript';
import * as sinon from 'sinon';
import {Logger} from '../src/Logger';
import {DummyTransport} from './Transport.spec';
chai.should();
chai.use(chaiAsPromised);
@suite(timeout(2000), slow(1000))
export class LoggerSpec {
@test
async error() {
const stub = sinon.stub(console, 'error');
await Logger.error('Foobar');
stub.restore();
expect(stub.args[0][0]).contains('[ERROR]');
expect(stub.args[0][0]).contains('Foobar');
}
@test
async errorInProductiveEnvironment() {
const errorStub = sinon.stub(console, 'error');
const nodeEnv = process.env.NODE_ENV;
process.env.NODE_ENV = 'production';
await Logger.error('Foobar').should.be.rejectedWith(Error);
expect(errorStub.callCount).to.be.equal(1);
process.env.ALLOW_NO_TRANSPORT = 'true';
await Logger.error('Foobar').should.not.be.rejectedWith(Error);
expect(errorStub.callCount).to.be.equal(2);
delete process.env.ALLOW_NO_TRANSPORT;
Logger.setTransport(new DummyTransport());
await Logger.error('Foobar').should.not.be.rejectedWith(Error);
expect(errorStub.callCount).to.be.equal(3);
Logger.setTransport();
process.env.NODE_ENV = nodeEnv;
errorStub.restore();
}
@test
async errorNoOutput() {
const stub = sinon.stub(console, 'error');
process.env.STAPPS_LOG_LEVEL = '0';
await Logger.error('Foobar');
stub.restore();
delete process.env.STAPPS_LOG_LEVEL;
return expect(stub.called).to.be.false;
}
@test
async errorWithError() {
const e = new Error();
const stub = sinon.stub(console, 'error');
await Logger.error(e);
stub.restore();
expect(stub.args[0][0]).contains('Error').contains('at').contains(process.cwd());
}
@test
getLogLevel() {
const savedProcess = process;
// @ts-ignore
process = undefined;
(global as any).window = {
STAPPS_LOG_LEVEL: 0,
};
const stub = sinon.stub(console, 'info');
Logger.info('Foobar');
stub.restore();
process = savedProcess;
delete (global as any).window;
return expect(stub.called).to.be.false;
}
@test
info() {
const stub = sinon.stub(console, 'info');
Logger.info('Foobar');
stub.restore();
expect(stub.args[0][0]).contains('[INFO]');
expect(stub.args[0][0]).contains('Foobar');
}
@test
infoNoOutput() {
const stub = sinon.stub(console, 'info');
process.env.STAPPS_LOG_LEVEL = '0';
Logger.info('Foobar');
stub.restore();
delete process.env.STAPPS_LOG_LEVEL;
return expect(stub.called).to.be.false;
}
@test
initialized() {
Logger.setTransport(new DummyTransport());
expect(() => {
Logger.initialized();
}).not.to.throw();
Logger.setTransport();
}
@test
initializedInProductiveEnvironment() {
const nodeEnv = process.env.NODE_ENV;
process.env.NODE_ENV = 'production';
Logger.setTransport(new DummyTransport());
expect(() => {
Logger.initialized();
}).not.to.throw();
Logger.setTransport();
expect(() => {
Logger.initialized();
}).to.throw();
const stub = sinon.stub(console, 'warn');
process.env.ALLOW_NO_TRANSPORT = 'true';
expect(() => {
Logger.initialized();
}).not.to.throw();
delete process.env.ALLOW_NO_TRANSPORT;
expect(stub.called).to.be.equal(true);
stub.restore();
process.env.NODE_ENV = nodeEnv;
}
@test
log() {
const stub = sinon.stub(console, 'log');
Logger.log('Foobar');
stub.restore();
expect(stub.args[0][0]).contains('[LOG]');
expect(stub.args[0][0]).contains('Foobar');
}
@test
logNoOutput() {
const stub = sinon.stub(console, 'log');
process.env.STAPPS_LOG_LEVEL = '0';
Logger.log('Foobar');
stub.restore();
delete process.env.STAPPS_LOG_LEVEL;
return expect(stub.called).to.be.false;
}
@test
ok() {
const stub = sinon.stub(console, 'log');
Logger.ok('Foobar');
stub.restore();
expect(stub.args[0][0]).contains('[OK]');
expect(stub.args[0][0]).contains('Foobar');
}
@test
okNoOutput() {
const stub = sinon.stub(console, 'log');
process.env.STAPPS_LOG_LEVEL = '0';
Logger.ok('Foobar');
stub.restore();
delete process.env.STAPPS_LOG_LEVEL;
return expect(stub.called).to.be.false;
}
@test
setTransport() {
expect(() => {
Logger.setTransport(new DummyTransport());
Logger.setTransport();
}).not.to.throw();
}
@test
stringifyArguments() {
const stub = sinon.stub(console, 'log');
Logger.log('foo', 'bar');
stub.restore();
expect(stub.args[0][0]).contains('[LOG]');
expect(stub.args[0][0]).contains('foo');
expect(stub.args[0][0]).contains('bar');
}
@test
warn() {
const stub = sinon.stub(console, 'warn');
Logger.warn('Foobar');
stub.restore();
expect(stub.args[0][0]).contains('[WARN]');
expect(stub.args[0][0]).contains('Foobar');
}
@test
warnNoOutput() {
const stub = sinon.stub(console, 'warn');
process.env.STAPPS_LOG_LEVEL = '0';
Logger.warn('Foobar');
stub.restore();
delete process.env.STAPPS_LOG_LEVEL;
return expect(stub.called).to.be.false;
}
}

View File

@@ -13,7 +13,7 @@
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {expect} from 'chai';
import {slow, suite, test, timeout} from 'mocha-typescript';
import {suite, test} from '@testdeck/mocha';
import {
deleteUndefinedProperties,
isNodeEnvironment,
@@ -21,7 +21,7 @@ import {
isProductiveNodeEnvironment,
} from '../src/common';
@suite(timeout(2000), slow(1000))
@suite()
export class CommonSpec {
@test
deleteUndefinedProperties1() {

35
test/dummyTransport.ts Normal file
View File

@@ -0,0 +1,35 @@
import {Transport, VerifiableTransport} from '../src/transport';
export class DummyTransport extends Transport {
send(subject: string, message: string): Promise<string> {
return new Promise((resolve, reject) => {
if (0 === 0) {
resolve(subject);
}
reject(message);
});
}
}
export class VerifiableDummyTransport extends VerifiableTransport {
isVerified(): boolean {
return false;
}
send(subject: string, message: string): Promise<string> {
return new Promise((resolve, reject) => {
if (0 === 0) {
resolve(subject);
}
reject(message);
});
}
verify(): Promise<boolean> {
return new Promise((resolve) => {
resolve(true);
});
}
}

482
test/logger.spec.ts Normal file
View File

@@ -0,0 +1,482 @@
/*
* Copyright (C) 2018, 2020 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* 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 General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import chai from 'chai';
import {expect} from 'chai';
import chaiAsPromised from 'chai-as-promised';
import chaiSpies from 'chai-spies';
import {suite, test} from '@testdeck/mocha';
import {Logger} from '../src/logger';
import {AddLogLevel} from '../src/transformations/add-log-level';
import {Colorize} from '../src/transformations/colorize';
import {DummyTransport} from './dummyTransport';
chai.should();
chai.use(chaiSpies);
chai.use(chaiAsPromised);
@suite()
export class LoggerSpec {
static sandbox: ChaiSpies.Sandbox;
static before() {
LoggerSpec.sandbox = chai.spy.sandbox();
}
before() {
Logger.setTransformations([
new AddLogLevel(),
]);
}
after() {
LoggerSpec.sandbox.restore();
}
@test
async 'default log level'() {
expect((Logger as any).getLevel('LOG')).to.be.equal(31);
expect((Logger as any).getLevel('EXIT')).to.be.equal(0);
}
@test
async error() {
const spy = LoggerSpec.sandbox.on(console, 'error', () => {
// noop
});
await Logger.error('Foobar');
expect(spy).to.have.been.called();
expect(spy.__spy.calls[0][0]).to.contain('[ERROR]');
expect(spy.__spy.calls[0][0]).to.contain('Foobar');
}
@test
async 'error in productive environment'() {
const spy = LoggerSpec.sandbox.on(console, 'error', () => {
// noop
});
const nodeEnv = process.env.NODE_ENV;
process.env.NODE_ENV = 'production';
await Logger.error('Foobar').should.be.rejectedWith(Error);
expect(spy).to.have.been.called.exactly(1);
process.env.ALLOW_NO_TRANSPORT = 'true';
await Logger.error('Foobar').should.not.be.rejectedWith(Error);
expect(spy).to.have.been.called.exactly(2);
delete process.env.ALLOW_NO_TRANSPORT;
Logger.setTransport(new DummyTransport());
await Logger.error('Foobar').should.not.be.rejectedWith(Error);
expect(spy).to.have.been.called.exactly(3);
Logger.setTransport();
process.env.NODE_ENV = nodeEnv;
}
@test
async 'error without output'() {
const spy = LoggerSpec.sandbox.on(console, 'error', () => {
// noop
});
process.env.STAPPS_LOG_LEVEL = '0';
await Logger.error('Foobar');
delete process.env.STAPPS_LOG_LEVEL;
expect(spy).not.to.have.been.called();
}
@test
async 'error with Error'() {
const spy = LoggerSpec.sandbox.on(console, 'error', () => {
// noop
});
await Logger.error(new Error());
expect(spy).to.have.been.called();
expect(spy.__spy.calls[0][0]).to.contain('Error');
expect(spy.__spy.calls[0][0]).to.contain(process.cwd());
}
@test
info() {
const spy = LoggerSpec.sandbox.on(console, 'info', () => {
// noop
});
Logger.info('Foobar');
expect(spy).to.have.been.called();
expect(spy.__spy.calls[0][0]).to.contain('[INFO]');
expect(spy.__spy.calls[0][0]).to.contain('Foobar');
}
@test
async exits() {
const infoSpy = LoggerSpec.sandbox.on(console, 'info', () => {
// noop
});
const logSpy = LoggerSpec.sandbox.on(console, 'log', () => {
// noop
});
const warnSpy = LoggerSpec.sandbox.on(console, 'warn', () => {
// noop
});
const errorSpy = LoggerSpec.sandbox.on(console, 'error', () => {
// noop
});
const processSpy = LoggerSpec.sandbox.on(process, 'exit', () => {
// noop
});
const exitLevel = process.env.STAPPS_EXIT_LEVEL;
process.env.STAPPS_EXIT_LEVEL = '31';
Logger.info('Foobar');
Logger.log('Foobar');
Logger.warn('Foobar');
Logger.ok('Foobar');
await Logger.error('Foobar');
expect(infoSpy).to.have.been.called.once;
expect(infoSpy.__spy.calls[0][0]).to.contain('[INFO]');
expect(infoSpy.__spy.calls[0][0]).to.contain('Foobar');
expect(logSpy).to.have.been.called.twice;
expect(warnSpy).to.have.been.called.once;
expect(warnSpy.__spy.calls[0][0]).to.contain('[WARN]');
expect(warnSpy.__spy.calls[0][0]).to.contain('Foobar');
expect(errorSpy).to.have.been.called.exactly(6);
expect(processSpy).to.have.been.called.exactly(5);
process.env.STAPPS_EXIT_LEVEL = exitLevel;
}
@test
'info without output'() {
const spy = LoggerSpec.sandbox.on(console, 'info', () => {
// noop
});
process.env.STAPPS_LOG_LEVEL = '0';
Logger.info('Foobar');
delete process.env.STAPPS_LOG_LEVEL;
expect(spy).not.to.have.been.called;
}
@test
initialized() {
Logger.setTransport(new DummyTransport());
expect(() => {
Logger.initialized();
}).not.to.throw();
Logger.setTransport();
}
@test
'initialized in productive environment'() {
const nodeEnv = process.env.NODE_ENV;
process.env.NODE_ENV = 'production';
Logger.setTransport(new DummyTransport());
expect(() => {
Logger.initialized();
}).not.to.throw();
Logger.setTransport();
expect(() => {
Logger.initialized();
}).to.throw();
const spy = LoggerSpec.sandbox.on(console, 'warn', () => {
// noop
});
process.env.ALLOW_NO_TRANSPORT = 'true';
expect(() => {
Logger.initialized();
}).not.to.throw();
delete process.env.ALLOW_NO_TRANSPORT;
expect(spy).to.have.been.called();
process.env.NODE_ENV = nodeEnv;
}
@test
'is compatible with log aggregation in productive environment'() {
Logger.setTransformations([new AddLogLevel(), new Colorize()]);
let spy = LoggerSpec.sandbox.on(console, 'log', () => {
// noop
});
Logger.log('Foo\nbar');
expect(spy).to.have.been.called.once;
expect(spy.__spy.calls[0][0]).to.equal('\u001B[37m[LOG] Foo\u001B[39m\n\u001B[37mbar\u001B[39m');
const nodeEnv = process.env.NODE_ENV;
process.env.NODE_ENV = 'production';
process.env.ALLOW_NO_TRANSPORT = 'true';
Logger.setTransformations([new AddLogLevel(), new Colorize()]);
Logger.log('Foo\nbar');
expect(spy).to.have.been.called.twice;
expect(spy.__spy.calls[1][0]).to.equal('[LOG] Foo bar');
process.env.NODE_ENV = nodeEnv;
delete process.env.ALLOW_NO_TRANSPORT;
}
@test
log() {
const spy = LoggerSpec.sandbox.on(console, 'log', () => {
// noop
});
Logger.log('Foobar');
expect(spy).to.have.been.called();
expect(spy.__spy.calls[0][0]).to.contain('[LOG]');
expect(spy.__spy.calls[0][0]).to.contain('Foobar');
}
@test
'log without output'() {
const spy = LoggerSpec.sandbox.on(console, 'log', () => {
// noop
});
process.env.STAPPS_LOG_LEVEL = '0';
Logger.log('Foobar');
delete process.env.STAPPS_LOG_LEVEL;
expect(spy).to.not.have.been.called();
}
@test
ok() {
const spy = LoggerSpec.sandbox.on(console, 'log', () => {
// noop
});
Logger.ok('Foobar');
expect(spy).to.have.been.called();
expect(spy.__spy.calls[0][0]).to.contain('[OK]');
expect(spy.__spy.calls[0][0]).to.contain('Foobar');
}
@test
'ok without output'() {
const spy = LoggerSpec.sandbox.on(console, 'log', () => {
// noop
});
process.env.STAPPS_LOG_LEVEL = '0';
Logger.ok('Foobar');
delete process.env.STAPPS_LOG_LEVEL;
expect(spy).not.to.have.been.called();
}
@test
setTransport() {
expect(() => {
Logger.setTransport(new DummyTransport());
Logger.setTransport();
}).not.to.throw();
}
@test
'stringify'() {
const spy = LoggerSpec.sandbox.on(console, 'log', () => {
// noop
});
Logger.log('foo', 'bar');
expect(spy).to.have.been.called();
expect(spy.__spy.calls[0][0]).to.contain('foo');
expect(spy.__spy.calls[0][0]).to.contain('bar');
}
@test
'stringify object'() {
const spy = LoggerSpec.sandbox.on(console, 'log', () => {
// noop
});
Logger.log({
foo: 'bar',
});
expect(spy).to.have.been.called();
expect(spy.__spy.calls[0][0]).to.contain('foo');
expect(spy.__spy.calls[0][0]).to.contain('bar');
}
@test
warn() {
const spy = LoggerSpec.sandbox.on(console, 'warn', () => {
// noop
});
Logger.warn('Foobar');
expect(spy).to.have.been.called();
expect(spy.__spy.calls[0][0]).to.contain('[WARN]');
expect(spy.__spy.calls[0][0]).to.contain('Foobar');
}
@test
'warn without output'() {
const spy = LoggerSpec.sandbox.on(console, 'warn', () => {
// noop
});
process.env.STAPPS_LOG_LEVEL = '0';
Logger.warn('Foobar');
delete process.env.STAPPS_LOG_LEVEL;
expect(spy).not.to.have.been.called();
}
@test
'log level exclusiveness'() {
const warnSpy = LoggerSpec.sandbox.on(console, 'warn', () => {
// noop WARN
});
const infoSpy = LoggerSpec.sandbox.on(console, 'info', () => {
// noop INFO
});
const okSpy = LoggerSpec.sandbox.on(console, 'log', () => {
// noop OK
});
// only warn and info = warn + info = 4 + 1 = 5
process.env.STAPPS_LOG_LEVEL = '5';
Logger.warn('Foo');
Logger.info('Bar');
Logger.ok('Baz');
expect(warnSpy).to.have.been.called();
expect(warnSpy.__spy.calls[0][0]).to.contain('[WARN]');
expect(warnSpy.__spy.calls[0][0]).to.contain('Foo');
expect(infoSpy).to.have.been.called();
expect(infoSpy.__spy.calls[0][0]).to.contain('[INFO]');
expect(infoSpy.__spy.calls[0][0]).to.contain('Bar');
expect(okSpy).to.not.have.been.called();
delete process.env.STAPPS_LOG_LEVEL;
}
@test
getExitLevel() {
const savedProcess = process;
// @ts-ignore
process = undefined;
(global as any).window = {
STAPPS_EXIT_LEVEL: 0,
};
const stub = LoggerSpec.sandbox.on(console, 'info', () => {
// noop
});
Logger.info('Foobar');
process = savedProcess;
delete (global as any).window;
expect(stub).not.to.have.been.called();
}
@test
getLogLevel() {
const savedProcess = process;
// @ts-ignore
process = undefined;
(global as any).window = {
STAPPS_LOG_LEVEL: 0,
};
const stub = LoggerSpec.sandbox.on(console, 'info', () => {
// noop
});
Logger.info('Foobar');
process = savedProcess;
delete (global as any).window;
expect(stub).not.to.have.been.called();
}
@test
'output without transformations'() {
Logger.setTransformations([]);
const stub = LoggerSpec.sandbox.on(console, 'log', () => {
// noop
});
const applyTransformationsSpy = LoggerSpec.sandbox.on(new Logger(), 'applyTransformations');
Logger.log('Foobar');
expect(stub).to.have.been.called.with('Foobar');
expect(applyTransformationsSpy).not.to.have.been.called;
}
}

View File

@@ -13,12 +13,10 @@
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {expect} from 'chai';
import {slow, suite, test, timeout} from 'mocha-typescript';
import {SMTP} from '../src/SMTP';
import {suite, test} from '@testdeck/mocha';
import {SMTP} from '../src/smtp';
// tslint:disable:no-unused-expression
@suite(timeout(2000), slow(1000))
@suite()
export class SMTPSpec {
/* tslint:disable:member-ordering */
@test
@@ -105,5 +103,6 @@ export class SMTPSpec {
mailValidation17() {
expect(SMTP.isValidEmailAddress('stordeur@campus')).to.be.false;
}
/* tslint:enable:member-ordering */
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright (C) 2019 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* 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 General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {expect} from 'chai';
import {suite, test} from '@testdeck/mocha';
import {AddLogLevel} from '../../src/transformations/add-log-level';
@suite()
export class AddLogLevelSpec {
@test
'transform'() {
const transformation = new AddLogLevel();
expect(transformation.transform('ERROR', 'Foobar')).to.be.equal('[ERROR] Foobar');
}
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright (C) 2019 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* 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 General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {expect} from 'chai';
import {suite, test} from '@testdeck/mocha';
import {Colorize} from '../../src/transformations/colorize';
@suite()
export class ColorizeSpec {
@test
'transform'() {
const transformation = new Colorize();
expect(transformation.transform('ERROR', 'Foobar')).to.be.equal('\u001b[1m\u001b[31mFoobar\u001b[39m\u001b[22m');
expect(transformation.transform('LOG', 'Foobar')).to.be.equal('\u001b[37mFoobar\u001b[39m');
}
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright (C) 2019 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* 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 General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {expect} from 'chai';
import {suite, test} from '@testdeck/mocha';
import {Timestamp} from '../../src/transformations/timestamp';
@suite()
export class TimeStampSpec {
@test
'default'() {
const transformation = new Timestamp();
expect(transformation.transform('ERROR', 'Foobar')).to.be.contain(`Z`);
}
}

View File

@@ -13,45 +13,11 @@
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {expect} from 'chai';
import {slow, suite, test, timeout} from 'mocha-typescript';
import {suite, test} from '@testdeck/mocha';
import {isTransportWithVerification} from '../src/common';
import {Transport, VerifiableTransport} from '../src/Transport';
import {DummyTransport, VerifiableDummyTransport} from './dummyTransport';
export class DummyTransport extends Transport {
send(subject: string, message: string): Promise<string> {
return new Promise((resolve, reject) => {
if (0 === 0) {
resolve(subject);
}
reject(message);
});
}
}
export class VerifiableDummyTransport extends VerifiableTransport {
isVerified(): boolean {
return false;
}
send(subject: string, message: string): Promise<string> {
return new Promise((resolve, reject) => {
if (0 === 0) {
resolve(subject);
}
reject(message);
});
}
verify(): Promise<boolean> {
return new Promise((resolve) => {
resolve(true);
});
}
}
@suite(timeout(2000), slow(1000))
@suite()
export class TransportSpec {
@test
isNotTransportWithVerification() {

View File

@@ -1,3 +0,0 @@
{
"extends": "./node_modules/@openstapps/configuration/tslint.json"
}