fix(test): cover pid file, signal helpers, improve coverage for others
This commit is contained in:
parent
f751652033
commit
76e2ba46dd
|
@ -0,0 +1,11 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [AsyncTracker](./js-utils.asynctracker.md) > [filter](./js-utils.asynctracker.filter.md)
|
||||
|
||||
## AsyncTracker.filter property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
filter: Optional<StackFilter>;
|
||||
```
|
|
@ -7,7 +7,7 @@
|
|||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
static getStack(): string;
|
||||
getStack(): string;
|
||||
```
|
||||
<b>Returns:</b>
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ export declare class AsyncTracker
|
|||
|
||||
| Property | Modifiers | Type | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| [filter](./js-utils.asynctracker.filter.md) | | <code>Optional<StackFilter></code> | |
|
||||
| [size](./js-utils.asynctracker.size.md) | | <code>number</code> | |
|
||||
|
||||
## Methods
|
||||
|
@ -34,5 +35,5 @@ export declare class AsyncTracker
|
|||
| [disable()](./js-utils.asynctracker.disable.md) | | |
|
||||
| [dump()](./js-utils.asynctracker.dump.md) | | |
|
||||
| [enable()](./js-utils.asynctracker.enable.md) | | |
|
||||
| [getStack()](./js-utils.asynctracker.getstack.md) | <code>static</code> | |
|
||||
| [getStack()](./js-utils.asynctracker.getstack.md) | | |
|
||||
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
## isNil() function
|
||||
|
||||
Check if a value is nil.
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
|
|
|
@ -44,13 +44,13 @@
|
|||
| [getOrDefault(map, key, defaultValue)](./js-utils.getordefault.md) | |
|
||||
| [getTestLogger(verbose)](./js-utils.gettestlogger.md) | |
|
||||
| [isDebug()](./js-utils.isdebug.md) | |
|
||||
| [isNil(val)](./js-utils.isnil.md) | |
|
||||
| [isNil(val)](./js-utils.isnil.md) | Check if a value is nil. |
|
||||
| [leftPad(val, min, fill)](./js-utils.leftpad.md) | |
|
||||
| [makeDict(map)](./js-utils.makedict.md) | Turns a map or dict into a dict |
|
||||
| [makeMap(val)](./js-utils.makemap.md) | Clone a map or map-like object into a new map. |
|
||||
| [mergeList(parts)](./js-utils.mergelist.md) | Merge arguments, which may or may not be arrays, into one return that is definitely an array. |
|
||||
| [mergeMap(target, source)](./js-utils.mergemap.md) | |
|
||||
| [mustCoalesce(values)](./js-utils.mustcoalesce.md) | Return the first value that is not nil. |
|
||||
| [mustCoalesce(values)](./js-utils.mustcoalesce.md) | Return the first value that is not nil.<!-- -->TODO: rename to mustDefault |
|
||||
| [mustExist(val)](./js-utils.mustexist.md) | Assert that a variable is not nil and return the value. |
|
||||
| [mustFind(list, predicate)](./js-utils.mustfind.md) | Find a value matching the given predicate or throw. |
|
||||
| [mustGet(map, key)](./js-utils.mustget.md) | Get an element from a Map and guard against nil values. |
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
Return the first value that is not nil.
|
||||
|
||||
TODO: rename to mustDefault
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
"@types/chai-as-promised": "7.1.2",
|
||||
"@types/lodash": "4.14.149",
|
||||
"@types/mocha": "7.0.2",
|
||||
"@types/mock-fs": "^4.10.0",
|
||||
"@types/node": "13.9.5",
|
||||
"@types/sinon-chai": "3.2.3",
|
||||
"@types/source-map-support": "0.5.1",
|
||||
|
@ -45,6 +46,7 @@
|
|||
"eslint-plugin-sonarjs": "0.5.0",
|
||||
"lodash": "4.17.15",
|
||||
"mocha": "7.1.1",
|
||||
"mock-fs": "^4.11.0",
|
||||
"noicejs": "3.0.1",
|
||||
"nyc": "15.0.0",
|
||||
"rollup": "2.3.1",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { AsyncHook, createHook } from 'async_hooks';
|
||||
|
||||
import { isNil } from './utils';
|
||||
import { isNil, Optional } from './utils';
|
||||
import { isDebug } from './utils/Env';
|
||||
|
||||
export interface TrackedResource {
|
||||
|
@ -9,6 +9,8 @@ export interface TrackedResource {
|
|||
type: string;
|
||||
}
|
||||
|
||||
export type StackFilter = (stack: string) => string;
|
||||
|
||||
/**
|
||||
* Async resource tracker using node's internal hooks.
|
||||
*
|
||||
|
@ -16,15 +18,7 @@ export interface TrackedResource {
|
|||
* Adapted from https://gist.github.com/boneskull/7fe75b63d613fa940db7ec990a5f5843#file-async-dump-js
|
||||
*/
|
||||
export class AsyncTracker {
|
||||
public static getStack(): string {
|
||||
const err = new Error();
|
||||
if (isNil(err.stack)) {
|
||||
return 'no stack trace available';
|
||||
} else {
|
||||
return err.stack; // TODO: filterStack(err.stack);
|
||||
}
|
||||
}
|
||||
|
||||
public filter: Optional<StackFilter>;
|
||||
private readonly hook: AsyncHook;
|
||||
private readonly resources: Map<number, TrackedResource>;
|
||||
|
||||
|
@ -35,7 +29,7 @@ export class AsyncTracker {
|
|||
this.resources.delete(id);
|
||||
},
|
||||
init: (id: number, type: string, triggerAsyncId: number) => {
|
||||
const source = AsyncTracker.getStack();
|
||||
const source = this.getStack();
|
||||
// @TODO: exclude async hooks, including this one
|
||||
this.resources.set(id, {
|
||||
source,
|
||||
|
@ -49,6 +43,19 @@ export class AsyncTracker {
|
|||
});
|
||||
}
|
||||
|
||||
public getStack(): string {
|
||||
const err = new Error();
|
||||
if (isNil(err.stack)) {
|
||||
return 'no stack trace available';
|
||||
}
|
||||
|
||||
if (isNil(this.filter)) {
|
||||
return err.stack;
|
||||
}
|
||||
|
||||
return this.filter(err.stack);
|
||||
}
|
||||
|
||||
public clear() {
|
||||
this.resources.clear();
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { TimeoutError } from '../error/TimeoutError';
|
||||
import { PredicateC0 } from '.';
|
||||
|
||||
/**
|
||||
* Resolve after a set amount of time.
|
||||
|
@ -24,7 +25,7 @@ export function timeout<T>(ms: number, oper: Promise<T>): Promise<T> {
|
|||
return Promise.race([limit, oper]);
|
||||
}
|
||||
|
||||
export async function waitFor(cb: () => boolean, step: number, count: number): Promise<void> {
|
||||
export async function waitFor(cb: PredicateC0, step: number, count: number): Promise<void> {
|
||||
let accum = 0;
|
||||
while (accum < count) {
|
||||
await defer(step);
|
||||
|
|
|
@ -6,26 +6,49 @@ import { NotFoundError } from '../error/NotFoundError';
|
|||
/* eslint-disable-next-line @typescript-eslint/ban-types */
|
||||
export type Nil = null | undefined;
|
||||
|
||||
export type SortAfter = 1;
|
||||
export type SortBefore = -1;
|
||||
export type SortEqual = 0;
|
||||
export type SortOrder = SortAfter | SortBefore | SortEqual;
|
||||
|
||||
/**
|
||||
* Value that may be nil.
|
||||
*/
|
||||
export type Optional<T> = T | Nil;
|
||||
|
||||
/**
|
||||
* Comparison (filter) predicate for a single value.
|
||||
* Comparison predicate for arity 0 - assert?
|
||||
*/
|
||||
export type PredicateC0 = () => boolean;
|
||||
|
||||
/**
|
||||
* Comparison predicate for arity 1 - filter.
|
||||
*/
|
||||
export type PredicateC1<TVal> = (val: TVal, idx: number, list: Array<TVal>) => boolean;
|
||||
|
||||
/**
|
||||
* Comparison (sort) predicate for two values.
|
||||
* Comparison predicate for arity 2 - sort.
|
||||
*/
|
||||
export type PredicateC2<TVal> = (pval: TVal, nval: TVal, idx: number, list: Array<TVal>) => number;
|
||||
export type PredicateC2<TVal> = (pval: TVal, nval: TVal, idx: number, list: Array<TVal>) => SortOrder;
|
||||
|
||||
/**
|
||||
* Reduction predicate for two values.
|
||||
* Transform predicate for arity 0 - constructor.
|
||||
*/
|
||||
export type PredicateR0<TVal> = () => TVal;
|
||||
|
||||
/**
|
||||
* Transform predicate for arity 1 - map.
|
||||
*/
|
||||
export type PredicateR1<TVal> = (val: TVal, idx: number, list: Array<TVal>) => TVal;
|
||||
|
||||
/**
|
||||
* Transform predicate for arity 2 - reduce.
|
||||
*/
|
||||
export type PredicateR2<TVal> = (pval: TVal, nval: TVal, idx: number, list: Array<TVal>) => TVal;
|
||||
|
||||
/**
|
||||
* Check if a value is nil.
|
||||
*/
|
||||
/* eslint-disable-next-line @typescript-eslint/ban-types */
|
||||
export function isNil<T>(val: Optional<T>): val is Nil {
|
||||
/* eslint-disable-next-line no-null/no-null */
|
||||
|
@ -113,6 +136,8 @@ export function mustExist<T>(val: Optional<T>): T {
|
|||
|
||||
/**
|
||||
* Return the first value that is not nil.
|
||||
*
|
||||
* TODO: rename to mustDefault
|
||||
*/
|
||||
export function mustCoalesce<T>(...values: Array<Optional<T>>): T {
|
||||
return mustFind(values, doesExist);
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
setOrPush,
|
||||
mergeMap,
|
||||
pushMergeMap,
|
||||
normalizeMap,
|
||||
} from '../../src/utils/Map';
|
||||
import { describeLeaks, itLeaks } from '../helpers/async';
|
||||
|
||||
|
@ -206,4 +207,28 @@ describeLeaks('map utils', async () => {
|
|||
})).to.deep.equal(singleItem);
|
||||
});
|
||||
});
|
||||
|
||||
describe('normalize map helper', () => {
|
||||
it('should convert values into arrays of strings', () => {
|
||||
const banVal = [Symbol()];
|
||||
const initial = new Map<string, unknown>([
|
||||
['bar', 'bin'],
|
||||
['ban', banVal],
|
||||
['toad', {
|
||||
toString() {
|
||||
return 'too';
|
||||
},
|
||||
}],
|
||||
]);
|
||||
|
||||
const normalized = normalizeMap(initial);
|
||||
|
||||
expect(normalized.bar).to.deep.equal(['bin']);
|
||||
expect(normalized.ban).to.equal(banVal);
|
||||
expect(normalized.toad).to.deep.equal(['too']);
|
||||
});
|
||||
|
||||
/* ['foo', 1] */
|
||||
xit('should convert numbers into string values');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
import { expect } from 'chai';
|
||||
import mockFS from 'mock-fs';
|
||||
|
||||
import { removePid, writePid } from '../../src';
|
||||
import { describeLeaks, itLeaks } from '../helpers/async';
|
||||
|
||||
const PID_PATH = 'foo';
|
||||
const PID_NAME = 'foo/test.pid';
|
||||
|
||||
describeLeaks('pid file utils', async () => {
|
||||
beforeEach(() => {
|
||||
mockFS({
|
||||
[PID_PATH]: mockFS.directory(),
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockFS.restore();
|
||||
});
|
||||
|
||||
itLeaks('should create a marker', async () => {
|
||||
await writePid(PID_NAME);
|
||||
|
||||
mockFS.restore();
|
||||
});
|
||||
|
||||
itLeaks('should not replace an existing marker', async () => {
|
||||
await writePid(PID_NAME);
|
||||
return expect(writePid(PID_PATH)).to.eventually.be.rejectedWith(Error);
|
||||
});
|
||||
|
||||
itLeaks('should remove an existing marker', async () => {
|
||||
await writePid(PID_NAME);
|
||||
await removePid(PID_NAME);
|
||||
|
||||
mockFS.restore();
|
||||
});
|
||||
|
||||
itLeaks('should fail to remove a missing marker', async () =>
|
||||
expect(removePid(PID_PATH)).to.eventually.be.rejectedWith(Error)
|
||||
);
|
||||
});
|
|
@ -1,18 +1,33 @@
|
|||
import { expect } from 'chai';
|
||||
import { getMethods } from '../../src/utils/Reflect';
|
||||
import { getMethods, getConstructor, constructorName } from '../../src/utils/Reflect';
|
||||
|
||||
class Test {
|
||||
public foo() { /* noop */ }
|
||||
public bar() { /* noop */ }
|
||||
}
|
||||
|
||||
describe('reflect utils', () => {
|
||||
describe('get methods helper', () => {
|
||||
it('should collect method functions', () => {
|
||||
class Test {
|
||||
public foo() { /* noop */ }
|
||||
public bar() { /* noop */ }
|
||||
}
|
||||
|
||||
const methods = getMethods(new Test()).values();
|
||||
|
||||
/* eslint-disable @typescript-eslint/unbound-method */
|
||||
expect(methods).to.include(Test.prototype.foo);
|
||||
expect(methods).to.include(Test.prototype.bar);
|
||||
});
|
||||
});
|
||||
|
||||
describe('get constructor helper', () => {
|
||||
it('should get the constructor from an instance', () => {
|
||||
const instance = new Test();
|
||||
expect(getConstructor(instance)).to.equal(Test);
|
||||
});
|
||||
});
|
||||
|
||||
describe('get constructor name helper', () => {
|
||||
it('should get the constructor name from an instance', () => {
|
||||
const instance = new Test();
|
||||
expect(constructorName(instance)).to.equal(Test.name);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
import { expect } from 'chai';
|
||||
|
||||
import { timeout } from '../../src/utils/Async';
|
||||
import { signal, SIGNAL_RESET } from '../../src/utils/Signal';
|
||||
import { describeLeaks, itLeaks } from '../helpers/async';
|
||||
|
||||
const MAX_SIGNAL_TIME = 500;
|
||||
|
||||
describeLeaks('signal utils', async () => {
|
||||
itLeaks('should wait for a signal', async () => {
|
||||
const signalPromise = signal(SIGNAL_RESET);
|
||||
|
||||
process.kill(process.pid, SIGNAL_RESET);
|
||||
await timeout(MAX_SIGNAL_TIME, signalPromise);
|
||||
|
||||
const signalValue = await signalPromise;
|
||||
|
||||
expect(signalValue).to.equal(SIGNAL_RESET);
|
||||
});
|
||||
});
|
|
@ -1,7 +1,7 @@
|
|||
import { expect } from 'chai';
|
||||
|
||||
import { NotFoundError } from '../../src/error/NotFoundError';
|
||||
import { countOf, filterNil, mustFind } from '../../src/utils';
|
||||
import { countOf, filterNil, mustFind, defaultWhen, mustCoalesce } from '../../src/utils';
|
||||
import { describeLeaks, itLeaks } from '../helpers/async';
|
||||
|
||||
describeLeaks('utils', async () => {
|
||||
|
@ -42,4 +42,24 @@ describeLeaks('utils', async () => {
|
|||
}).to.throw(NotFoundError);
|
||||
});
|
||||
});
|
||||
|
||||
describeLeaks('default when', async () => {
|
||||
itLeaks('should return the first item when the condition is true', async () => {
|
||||
expect(defaultWhen(true, 1, 2)).to.equal(1);
|
||||
});
|
||||
|
||||
itLeaks('should return the second item otherwise', async () => {
|
||||
expect(defaultWhen(false, 1, 2)).to.equal(2);
|
||||
});
|
||||
});
|
||||
|
||||
describeLeaks('must coalesce helper', async () => {
|
||||
/* eslint-disable no-null/no-null */
|
||||
itLeaks('should return the first existent value', async () => {
|
||||
expect(mustCoalesce(null, null, 3, null)).to.equal(3);
|
||||
expect(mustCoalesce(null, null, undefined, 'string')).to.equal('string');
|
||||
expect(mustCoalesce(null, undefined, [], null)).to.deep.equal([]);
|
||||
});
|
||||
/* eslint-enable no-null/no-null */
|
||||
});
|
||||
});
|
||||
|
|
12
yarn.lock
12
yarn.lock
|
@ -383,6 +383,13 @@
|
|||
resolved "https://artifacts.apextoaster.com/repository/group-npm/@types/mocha/-/mocha-7.0.2.tgz#b17f16cf933597e10d6d78eae3251e692ce8b0ce"
|
||||
integrity sha512-ZvO2tAcjmMi8V/5Z3JsyofMe3hasRcaw88cto5etSVMwVQfeivGAlEYmaQgceUSVYFofVjT+ioHsATjdWcFt1w==
|
||||
|
||||
"@types/mock-fs@^4.10.0":
|
||||
version "4.10.0"
|
||||
resolved "https://artifacts.apextoaster.com/repository/group-npm/@types/mock-fs/-/mock-fs-4.10.0.tgz#460061b186993d76856f669d5317cda8a007c24b"
|
||||
integrity sha512-FQ5alSzmHMmliqcL36JqIA4Yyn9jyJKvRSGV3mvPh108VFatX7naJDzSG4fnFQNZFq9dIx0Dzoe6ddflMB2Xkg==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/node@*":
|
||||
version "12.7.8"
|
||||
resolved "https://artifacts.apextoaster.com/repository/group-npm/@types/node/-/node-12.7.8.tgz#cb1bf6800238898bc2ff6ffa5702c3cadd350708"
|
||||
|
@ -2567,6 +2574,11 @@ mocha@7.1.1:
|
|||
yargs-parser "13.1.2"
|
||||
yargs-unparser "1.6.0"
|
||||
|
||||
mock-fs@^4.11.0:
|
||||
version "4.11.0"
|
||||
resolved "https://artifacts.apextoaster.com/repository/group-npm/mock-fs/-/mock-fs-4.11.0.tgz#0828107e4b843a6ba855ecebfe3c6e073b69db92"
|
||||
integrity sha512-Yp4o3/ZA15wsXqJTT+R+9w2AYIkD1i80Lds47wDbuUhOvQvm+O2EfjFZSz0pMgZZSPHRhGxgcd2+GL4+jZMtdw==
|
||||
|
||||
modify-values@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://artifacts.apextoaster.com/repository/group-npm/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022"
|
||||
|
|
Loading…
Reference in New Issue