diff --git a/tests/event/test00001.test.ts b/tests/event/test00001.test.ts new file mode 100644 index 0000000..9127ac2 --- /dev/null +++ b/tests/event/test00001.test.ts @@ -0,0 +1,89 @@ +/* + * Copyright (c) + * Weyoss + * https://github.com/weyoss + * + * This source code is licensed under the MIT license found in the LICENSE file + * in the root directory of this source tree. + */ + +import { promisify, promisifyAll } from 'bluebird'; +import { EventBusConnectionError } from '../../src/event/errors'; +import { EventBus } from '../../src/event'; + +type TEvent = { + e1: (arg: string) => void; + 'eventBus.disconnect': () => void; +}; + +test('EventBus: case 1', async () => { + const getInstanceAsync = await promisify(EventBus.getInstance); + const eventBusAsync0 = promisifyAll(await getInstanceAsync()); + const eventBusAsync = promisifyAll(await getInstanceAsync()); + expect(eventBusAsync).toBe(eventBusAsync0); + + // on + const callback = jest.fn(); + eventBusAsync.on('e1', callback); + eventBusAsync.emit('e1', 'hello'); + expect(callback).toHaveBeenCalledTimes(1); + expect(callback).toHaveBeenNthCalledWith(1, 'hello'); + + // once + const callback2 = jest.fn(); + eventBusAsync.once('e1', callback2); + eventBusAsync.emit('e1', 'hello1'); + eventBusAsync.emit('e1', 'hello2'); + expect(callback2).toHaveBeenCalledTimes(1); + expect(callback2).toHaveBeenNthCalledWith(1, 'hello1'); + + // removeListener + const callback3 = jest.fn(); + eventBusAsync.on('e1', callback3); + eventBusAsync.emit('e1', 'hello3'); + expect(callback3).toHaveBeenCalledTimes(1); + expect(callback3).toHaveBeenNthCalledWith(1, 'hello3'); + eventBusAsync.removeListener('e1', callback3); + eventBusAsync.emit('e1', 'hello4'); + expect(callback3).toHaveBeenCalledTimes(1); + + // removeAllListeners of an event + const callback4 = jest.fn(); + eventBusAsync.on('e1', callback4); + eventBusAsync.emit('e1', 'hello5'); + expect(callback4).toHaveBeenCalledTimes(1); + expect(callback4).toHaveBeenNthCalledWith(1, 'hello5'); + eventBusAsync.removeAllListeners('e1'); + eventBusAsync.emit('e1', 'hello6'); + expect(callback4).toHaveBeenCalledTimes(1); + + // removeAllListeners + const callback5 = jest.fn(); + eventBusAsync.on('e1', callback5); + eventBusAsync.emit('e1', 'hello6'); + expect(callback5).toHaveBeenCalledTimes(1); + expect(callback5).toHaveBeenNthCalledWith(1, 'hello6'); + eventBusAsync.removeAllListeners(); + eventBusAsync.emit('e1', 'hello7'); + expect(callback5).toHaveBeenCalledTimes(1); + + await eventBusAsync.disconnectAsync(); + expect(() => eventBusAsync.on('e1', () => void 0)).toThrow( + EventBusConnectionError, + ); + expect(() => eventBusAsync.once('e1', () => void 0)).toThrow( + EventBusConnectionError, + ); + expect(() => eventBusAsync.removeListener('e1', () => void 0)).toThrow( + EventBusConnectionError, + ); + expect(() => eventBusAsync.removeAllListeners('e1')).toThrow( + EventBusConnectionError, + ); + expect(() => eventBusAsync.removeAllListeners()).toThrow( + EventBusConnectionError, + ); + expect(() => eventBusAsync.emit('e1', 'hello8')).toThrow( + EventBusConnectionError, + ); +}); diff --git a/tests/event/test00002.test.ts b/tests/event/test00002.test.ts new file mode 100644 index 0000000..70d53ce --- /dev/null +++ b/tests/event/test00002.test.ts @@ -0,0 +1,105 @@ +/* + * Copyright (c) + * Weyoss + * https://github.com/weyoss + * + * This source code is licensed under the MIT license found in the LICENSE file + * in the root directory of this source tree. + */ + +import { delay, promisify, promisifyAll } from 'bluebird'; +import { EventBusConnectionError } from '../../src/event/errors'; +import { redisConfig } from '../config'; +import { EventBusRedis } from '../../src/event'; + +type TEvent = { + e1: (arg: string) => void; +}; + +test('EventBusRedis: case 1', async () => { + const getInstanceAsync = await promisify(EventBusRedis.getInstance); + const eventBusAsync0 = promisifyAll( + await getInstanceAsync(redisConfig), + ); + const eventBusAsync = promisifyAll( + await getInstanceAsync(redisConfig), + ); + expect(eventBusAsync).toBe(eventBusAsync0); + + // on + const callback = jest.fn(); + eventBusAsync.on('e1', callback); + eventBusAsync.emit('e1', 'hello'); + await delay(2000); + expect(callback).toHaveBeenCalledTimes(1); + expect(callback).toHaveBeenNthCalledWith(1, 'hello'); + + // once + const callback2 = jest.fn(); + eventBusAsync.once('e1', callback2); + eventBusAsync.emit('e1', 'hello1'); + eventBusAsync.emit('e1', 'hello2'); + await delay(2000); + expect(callback2).toHaveBeenCalledTimes(1); + expect(callback2).toHaveBeenNthCalledWith(1, 'hello1'); + + // removeListener + const callback3 = jest.fn(); + eventBusAsync.on('e1', callback3); + eventBusAsync.emit('e1', 'hello3'); + await delay(2000); + expect(callback3).toHaveBeenCalledTimes(1); + expect(callback3).toHaveBeenNthCalledWith(1, 'hello3'); + eventBusAsync.removeListener('e1', callback3); + eventBusAsync.emit('e1', 'hello4'); + await delay(2000); + expect(callback3).toHaveBeenCalledTimes(1); + + // removeAllListeners of an event + const callback4 = jest.fn(); + eventBusAsync.on('e1', callback4); + eventBusAsync.emit('e1', 'hello5'); + await delay(2000); + expect(callback4).toHaveBeenCalledTimes(1); + expect(callback4).toHaveBeenNthCalledWith(1, 'hello5'); + eventBusAsync.removeAllListeners('e1'); + eventBusAsync.emit('e1', 'hello6'); + await delay(2000); + expect(callback4).toHaveBeenCalledTimes(1); + + // removeAllListeners + const callback5 = jest.fn(); + eventBusAsync.on('e1', callback5); + eventBusAsync.emit('e1', 'hello6'); + await delay(2000); + expect(callback5).toHaveBeenCalledTimes(1); + expect(callback5).toHaveBeenNthCalledWith(1, 'hello6'); + eventBusAsync.removeAllListeners(); + await delay(2000); + eventBusAsync.emit('e1', 'hello7'); + expect(callback5).toHaveBeenCalledTimes(1); + + await eventBusAsync.disconnectAsync(); + + // second time + await eventBusAsync.disconnectAsync(); + + expect(() => eventBusAsync.on('e1', () => void 0)).toThrow( + EventBusConnectionError, + ); + expect(() => eventBusAsync.once('e1', () => void 0)).toThrow( + EventBusConnectionError, + ); + expect(() => eventBusAsync.removeListener('e1', () => void 0)).toThrow( + EventBusConnectionError, + ); + expect(() => eventBusAsync.removeAllListeners('e1')).toThrow( + EventBusConnectionError, + ); + expect(() => eventBusAsync.removeAllListeners()).toThrow( + EventBusConnectionError, + ); + expect(() => eventBusAsync.emit('e1', 'hello8')).toThrow( + EventBusConnectionError, + ); +}); diff --git a/tests/event/test00003.test.ts b/tests/event/test00003.test.ts new file mode 100644 index 0000000..d6c8b5f --- /dev/null +++ b/tests/event/test00003.test.ts @@ -0,0 +1,63 @@ +/* + * Copyright (c) + * Weyoss + * https://github.com/weyoss + * + * This source code is licensed under the MIT license found in the LICENSE file + * in the root directory of this source tree. + */ + +import { EventEmitter } from '../../src/event'; + +type TEvent = { + e1: (arg: string) => void; +}; + +test('EventEmitter', async () => { + const eventEmitter = new EventEmitter(); + + // on + const callback = jest.fn(); + eventEmitter.on('e1', callback); + eventEmitter.emit('e1', 'hello'); + expect(callback).toHaveBeenCalledTimes(1); + expect(callback).toHaveBeenNthCalledWith(1, 'hello'); + + // once + const callback2 = jest.fn(); + eventEmitter.once('e1', callback2); + eventEmitter.emit('e1', 'hello1'); + eventEmitter.emit('e1', 'hello2'); + expect(callback2).toHaveBeenCalledTimes(1); + expect(callback2).toHaveBeenNthCalledWith(1, 'hello1'); + + // removeListener + const callback3 = jest.fn(); + eventEmitter.on('e1', callback3); + eventEmitter.emit('e1', 'hello3'); + expect(callback3).toHaveBeenCalledTimes(1); + expect(callback3).toHaveBeenNthCalledWith(1, 'hello3'); + eventEmitter.removeListener('e1', callback3); + eventEmitter.emit('e1', 'hello4'); + expect(callback3).toHaveBeenCalledTimes(1); + + // removeAllListeners of an event + const callback4 = jest.fn(); + eventEmitter.on('e1', callback4); + eventEmitter.emit('e1', 'hello5'); + expect(callback4).toHaveBeenCalledTimes(1); + expect(callback4).toHaveBeenNthCalledWith(1, 'hello5'); + eventEmitter.removeAllListeners('e1'); + eventEmitter.emit('e1', 'hello6'); + expect(callback4).toHaveBeenCalledTimes(1); + + // removeAllListeners + const callback5 = jest.fn(); + eventEmitter.on('e1', callback5); + eventEmitter.emit('e1', 'hello6'); + expect(callback5).toHaveBeenCalledTimes(1); + expect(callback5).toHaveBeenNthCalledWith(1, 'hello6'); + eventEmitter.removeAllListeners(); + eventEmitter.emit('e1', 'hello7'); + expect(callback5).toHaveBeenCalledTimes(1); +}); diff --git a/tests/jest.setup.ts b/tests/jest.setup.ts index 1b967d6..354ea06 100644 --- a/tests/jest.setup.ts +++ b/tests/jest.setup.ts @@ -17,6 +17,7 @@ afterAll(noop); beforeEach(async () => { await startUp(); + jest.resetModules(); }); afterEach(async () => { diff --git a/tests/lock/test00001.test.ts b/tests/lock/test00001.test.ts deleted file mode 100644 index 7b477de..0000000 --- a/tests/lock/test00001.test.ts +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) - * Weyoss - * https://github.com/weyoss - * - * This source code is licensed under the MIT license found in the LICENSE file - * in the root directory of this source tree. - */ - -import { delay, promisifyAll } from 'bluebird'; -import { Lock } from '../../src/lock/lock'; -import { getRedisInstance } from '../common'; -import { - LockExtendError, - LockNotAcquiredError, - LockNotReleasedError, -} from '../../src/lock/errors'; - -test('Lock: acquireLock(), extendLock(), releaseLock()', async () => { - const redisClient = await getRedisInstance(); - const lock = promisifyAll(new Lock(redisClient, 'key1', 5000, false)); - - await lock.acquireLockAsync(); - - await expect(lock.acquireLockAsync()).rejects.toThrow(LockNotReleasedError); - - await delay(10000); - await expect(lock.extendLockAsync()).rejects.toThrow(LockExtendError); - - await lock.acquireLockAsync(); - - await lock.extendLockAsync(); - - await expect( - Promise.all([lock.releaseLockAsync(), lock.releaseLockAsync()]), - ).rejects.toThrow(LockNotAcquiredError); - - await delay(5000); - await lock.releaseLockAsync(); - await lock.releaseLockAsync(); - - await expect(lock.extendLockAsync()).rejects.toThrow(LockNotAcquiredError); - - await lock.acquireLockAsync(); -}); diff --git a/tests/lock/test00002.test.ts b/tests/lock/test00002.test.ts deleted file mode 100644 index 09d4f69..0000000 --- a/tests/lock/test00002.test.ts +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) - * Weyoss - * https://github.com/weyoss - * - * This source code is licensed under the MIT license found in the LICENSE file - * in the root directory of this source tree. - */ - -import { promisifyAll } from 'bluebird'; -import { Lock } from '../../src/lock/lock'; -import { getRedisInstance } from '../common'; -import { LockAcquireError } from '../../src/lock/errors'; - -test('Lock: retryOnFail', async () => { - const redisClient = await getRedisInstance(); - const lock = promisifyAll(new Lock(redisClient, 'key1', 20000, false)); - - await lock.acquireLockAsync(); - - const lock2 = promisifyAll(new Lock(redisClient, 'key1', 10000, false)); - - await expect(lock2.acquireLockAsync()).rejects.toThrow(LockAcquireError); - - const lock3 = promisifyAll(new Lock(redisClient, 'key1', 10000, true)); - - await lock3.acquireLockAsync(); - - await lock3.extendLockAsync(); -}); diff --git a/tests/lock/test00004.test.ts b/tests/lock/test00004.test.ts deleted file mode 100644 index ffc5f20..0000000 --- a/tests/lock/test00004.test.ts +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) - * Weyoss - * https://github.com/weyoss - * - * This source code is licensed under the MIT license found in the LICENSE file - * in the root directory of this source tree. - */ - -import { delay, promisifyAll } from 'bluebird'; -import { ELockStatus, Lock } from '../../src/lock/lock'; -import { getRedisInstance } from '../common'; -import { LockMethodNotAllowedError } from '../../src/lock/errors'; - -test('Lock: acquireOrExtend', async () => { - const redisClient = await getRedisInstance(); - const lm1 = promisifyAll(new Lock(redisClient, 'key1', 10000, false, true)); - await expect(lm1.acquireOrExtendAsync()).rejects.toThrowError( - LockMethodNotAllowedError, - ); - const lm2 = promisifyAll(new Lock(redisClient, 'key1', 10000)); - const op1 = await lm2.acquireOrExtendAsync(); - expect(op1).toEqual(ELockStatus.locked); - await delay(5000); - const op2 = await lm2.acquireOrExtendAsync(); - expect(op2).toEqual(ELockStatus.extended); - await delay(15000); - const op3 = await lm2.acquireOrExtendAsync(); - expect(op3).toEqual(ELockStatus.locked); -}); diff --git a/tests/locker/test00001.test.ts b/tests/locker/test00001.test.ts new file mode 100644 index 0000000..a05be6f --- /dev/null +++ b/tests/locker/test00001.test.ts @@ -0,0 +1,39 @@ +/* + * Copyright (c) + * Weyoss + * https://github.com/weyoss + * + * This source code is licensed under the MIT license found in the LICENSE file + * in the root directory of this source tree. + */ + +import { delay, promisifyAll } from 'bluebird'; +import { Locker } from '../../src/locker/locker'; +import { getRedisInstance } from '../common'; +import { LockExtendError, LockNotAcquiredError } from '../../src/locker/errors'; + +test('Locker: locker(), extend(), releaseLock()', async () => { + const redisClient = await getRedisInstance(); + const lock = promisifyAll( + new Locker(redisClient, console, 'key1', 5000, false), + ); + expect(lock.getId()).toBeDefined(); + await expect(lock.acquireLockAsync()).resolves.toBe(true); + expect(lock.isLocked()).toBe(true); + await expect(lock.acquireLockAsync()).resolves.toBe(false); + expect(lock.isLocked()).toBe(true); + + await delay(10000); + + await expect(lock.extendLockAsync()).rejects.toThrow(LockExtendError); + await expect(lock.acquireLockAsync()).resolves.toBe(true); + await expect(lock.extendLockAsync()).resolves.toBeUndefined(); + await expect(lock.releaseLockAsync()).resolves.toBe(true); + expect(lock.isLocked()).toBe(false); + expect(lock.isReleased()).toBe(true); + await expect(lock.releaseLockAsync()).resolves.toBe(false); + expect(lock.isReleased()).toBe(true); + await expect(lock.extendLockAsync()).rejects.toThrow(LockNotAcquiredError); + await expect(lock.acquireLockAsync()).resolves.toBe(true); + await expect(lock.releaseLockAsync()).resolves.toBe(true); +}); diff --git a/tests/locker/test00002.test.ts b/tests/locker/test00002.test.ts new file mode 100644 index 0000000..17960c4 --- /dev/null +++ b/tests/locker/test00002.test.ts @@ -0,0 +1,31 @@ +/* + * Copyright (c) + * Weyoss + * https://github.com/weyoss + * + * This source code is licensed under the MIT license found in the LICENSE file + * in the root directory of this source tree. + */ + +import { promisifyAll } from 'bluebird'; +import { Locker } from '../../src/locker/locker'; +import { getRedisInstance } from '../common'; + +test('Locker: retryOnFail', async () => { + const redisClient = await getRedisInstance(); + const lock = promisifyAll( + new Locker(redisClient, console, 'key1', 20000, false), + ); + await expect(lock.acquireLockAsync()).resolves.toBe(true); + + const lock2 = promisifyAll( + new Locker(redisClient, console, 'key1', 10000, false), + ); + await expect(lock2.acquireLockAsync()).resolves.toBe(false); + + const lock3 = promisifyAll( + new Locker(redisClient, console, 'key1', 10000, true), + ); + await expect(lock3.acquireLockAsync()).resolves.toBe(true); + await expect(lock3.extendLockAsync()).resolves.toBeUndefined(); +}); diff --git a/tests/lock/test00003.test.ts b/tests/locker/test00003.test.ts similarity index 53% rename from tests/lock/test00003.test.ts rename to tests/locker/test00003.test.ts index 48f3a7f..54c87f3 100644 --- a/tests/lock/test00003.test.ts +++ b/tests/locker/test00003.test.ts @@ -8,28 +8,26 @@ */ import { delay, promisifyAll } from 'bluebird'; -import { Lock } from '../../src/lock/lock'; +import { Locker } from '../../src/locker/locker'; import { getRedisInstance } from '../common'; -import { - LockAcquireError, - LockMethodNotAllowedError, -} from '../../src/lock/errors'; +import { LockMethodNotAllowedError } from '../../src/locker/errors'; -test('Lock: autoExtend', async () => { +test('Locker: autoExtend', async () => { const redisClient = await getRedisInstance(); - const lock = promisifyAll(new Lock(redisClient, 'key1', 10000, false, true)); - - await lock.acquireLockAsync(); - + const lock = promisifyAll( + new Locker(redisClient, console, 'key1', 10000, false, 3000), + ); + await expect(lock.acquireLockAsync()).resolves.toBe(true); await expect(lock.extendLockAsync()).rejects.toThrowError( LockMethodNotAllowedError, ); await delay(20000); - const lock2 = promisifyAll(new Lock(redisClient, 'key1', 10000, false)); - - await expect(lock2.acquireLockAsync()).rejects.toThrow(LockAcquireError); + const lock2 = promisifyAll( + new Locker(redisClient, console, 'key1', 10000, false), + ); + await expect(lock2.acquireLockAsync()).resolves.toBe(false); await lock.releaseLockAsync(); await lock2.releaseLockAsync(); diff --git a/tests/locker/test00004.test.ts b/tests/locker/test00004.test.ts new file mode 100644 index 0000000..42a28fa --- /dev/null +++ b/tests/locker/test00004.test.ts @@ -0,0 +1,86 @@ +/* + * Copyright (c) + * Weyoss + * https://github.com/weyoss + * + * This source code is licensed under the MIT license found in the LICENSE file + * in the root directory of this source tree. + */ + +// type-coverage:ignore-next-line +import { LockAbortError } from '../../src/locker/errors'; + +// type-coverage:ignore-next-line +const mockRunScript = jest + .fn() + .mockImplementationOnce( + ( + scriptName: string, + keys: (string | number)[], + args: (string | number)[], + cb: ICallback, + ) => { + setTimeout(() => cb(null, 1), 10000); + }, + ) + .mockImplementationOnce( + ( + scriptName: string, + keys: (string | number)[], + args: (string | number)[], + cb: ICallback, + ) => { + setTimeout(() => cb(null, 1), 1000); + }, + ); + +// type-coverage:ignore-next-line +const mockHalt = jest.fn().mockImplementationOnce((cb: ICallback) => { + setTimeout(() => cb(), 5000); +}); + +// type-coverage:ignore-next-line +const mockSet = jest.fn().mockImplementationOnce( + ( + key: string, + value: string, + options: { + expire?: { mode: 'EX' | 'PX'; value: number }; + exists?: 'NX' | 'XX'; + }, + cb: ICallback, + ) => { + setTimeout(() => cb(null, '1'), 5000); + }, +); + +jest.mock('../common', () => { + return { + getRedisInstance() { + return Promise.resolve({ + // type-coverage:ignore-next-line + runScript: mockRunScript, + // type-coverage:ignore-next-line + halt: mockHalt, + // type-coverage:ignore-next-line + set: mockSet, + }); + }, + }; +}); + +import { promisifyAll } from 'bluebird'; +import { Locker } from '../../src/locker/locker'; +import { getRedisInstance } from '../common'; +import { ICallback } from '../../types'; + +test('Locker: extendLock() -> LockAbortError', async () => { + const redisClient = await getRedisInstance(); + const lock = promisifyAll( + new Locker(redisClient, console, 'key1', 10000, false), + ); + await expect(lock.acquireLockAsync()).resolves.toBe(true); + await expect( + Promise.all([lock.extendLockAsync(), lock.shutdownAsync()]), + ).rejects.toThrow(LockAbortError); +}); diff --git a/tests/locker/test00005.test.ts b/tests/locker/test00005.test.ts new file mode 100644 index 0000000..a627bba --- /dev/null +++ b/tests/locker/test00005.test.ts @@ -0,0 +1,93 @@ +/* + * Copyright (c) + * Weyoss + * https://github.com/weyoss + * + * This source code is licensed under the MIT license found in the LICENSE file + * in the root directory of this source tree. + */ + +// type-coverage:ignore-next-line +const mockRunScript = jest + .fn() + .mockImplementationOnce( + ( + scriptName: string, + keys: (string | number)[], + args: (string | number)[], + cb: ICallback, + ) => { + setTimeout(() => cb(null, 1), 1000); + }, + ) + .mockImplementationOnce( + ( + scriptName: string, + keys: (string | number)[], + args: (string | number)[], + cb: ICallback, + ) => { + console.log('second', scriptName); + setTimeout(() => cb(null, 1), 1000); + }, + ); + +// type-coverage:ignore-next-line +const mockHalt = jest.fn().mockImplementationOnce((cb: ICallback) => { + setTimeout(() => cb(), 5000); +}); + +// type-coverage:ignore-next-line +const mockSet = jest.fn().mockImplementationOnce( + ( + key: string, + value: string, + options: { + expire?: { mode: 'EX' | 'PX'; value: number }; + exists?: 'NX' | 'XX'; + }, + cb: ICallback, + ) => { + setTimeout(() => cb(null, '1'), 10000); + }, +); + +// type-coverage:ignore-next-line +jest.mock('../common', () => { + return { + getRedisInstance() { + return Promise.resolve({ + // type-coverage:ignore-next-line + runScript: mockRunScript, + // type-coverage:ignore-next-line + halt: mockHalt, + // type-coverage:ignore-next-line + set: mockSet, + }); + }, + }; +}); + +import { promisifyAll } from 'bluebird'; +import { Locker } from '../../src/locker/locker'; +import { getRedisInstance } from '../common'; +import { ICallback } from '../../types'; +import { LockAbortError } from '../../src/locker/errors'; + +test('Locker: acquireLock() -> LockAbortError', async () => { + const redisClient = await getRedisInstance(); + const lock = promisifyAll( + new Locker(redisClient, console, 'key1', 10000, false), + ); + await expect( + Promise.all([ + lock.acquireLockAsync(), + new Promise((resolve) => { + setTimeout(() => { + lock.emit('locker.error', new Error()); + resolve(); + }, 3000); + }), + ]), + ).rejects.toThrow(LockAbortError); +}); diff --git a/tests/ticker/test00001.test.ts b/tests/ticker/test00001.test.ts deleted file mode 100644 index a3125d9..0000000 --- a/tests/ticker/test00001.test.ts +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (c) - * Weyoss - * https://github.com/weyoss - * - * This source code is licensed under the MIT license found in the LICENSE file - * in the root directory of this source tree. - */ - -import { delay } from 'bluebird'; -import { Ticker } from '../../src/ticker/ticker'; - -describe('Ticker', () => { - test('Case 1', async () => { - let changedFromTicker = 0; - const ticker = new Ticker(() => (changedFromTicker += 1), 1000); - ticker.nextTick(); - - let down = 0; - ticker.once('down', () => (down += 1)); - - await delay(2000); - expect(down).toBe(0); - expect(changedFromTicker).toBe(1); - }); - - test('Case 2', async () => { - let changedFromTicker = 0; - const ticker = new Ticker(() => (changedFromTicker += 1), 0); - - let down = 0; - ticker.once('down', () => (down += 1)); - ticker.quit(); - - await delay(1000); - expect(down).toBe(1); - expect(changedFromTicker).toBe(0); - }); - - test('Case 3', async () => { - let changedFromTicker = 0; - const ticker = new Ticker(() => (changedFromTicker += 1), 10000); - ticker.nextTick(); - - let down = 0; - ticker.once('down', () => (down += 1)); - ticker.quit(); - - await delay(1000); - expect(down).toBe(1); - expect(changedFromTicker).toBe(0); - }); - - test('Case 4', async () => { - let changedFromTicker = 0; - const ticker = new Ticker(() => (changedFromTicker += 1), 3000); - ticker.nextTick(); - - let down = 0; - ticker.once('down', () => (down += 1)); - ticker.quit(); - - await delay(5000); - expect(down).toBe(1); - expect(changedFromTicker).toBe(0); - }); - - test('Case 5', async () => { - let changedFromTicker = 0; - const ticker = new Ticker(() => (changedFromTicker += 1), 3000); - ticker.runTimer(); - - let down = 0; - ticker.once('down', () => (down += 1)); - ticker.quit(); - - await delay(1000); - expect(down).toBe(1); - expect(changedFromTicker).toBe(0); - }); - - test('Case 6', async () => { - let changedFromTicker = 0; - const ticker = new Ticker(() => (changedFromTicker += 1), 3000); - ticker.runTimer(); - - let down = 0; - ticker.once('down', () => (down += 1)); - ticker.quit(); - - await delay(5000); - expect(down).toBe(1); - expect(changedFromTicker).toBe(0); - }); - - test('Case 7', async () => { - let changedFromTicker = 0; - const ticker = new Ticker(() => (changedFromTicker += 1), 3000); - ticker.runTimer(); - - expect(() => ticker.runTimer()).toThrow('A timer is already running'); - expect(() => ticker.nextTick()).toThrow('A timer is already running'); - ticker.quit(); - }); -}); diff --git a/tests/timer/test00001.test.ts b/tests/timer/test00001.test.ts new file mode 100644 index 0000000..5dac718 --- /dev/null +++ b/tests/timer/test00001.test.ts @@ -0,0 +1,45 @@ +/* + * Copyright (c) + * Weyoss + * https://github.com/weyoss + * + * This source code is licensed under the MIT license found in the LICENSE file + * in the root directory of this source tree. + */ + +import { delay } from 'bluebird'; +import { Timer } from '../../src/timer/timer'; + +test('Timer.setTimeout()', async () => { + let count = 0; + const ticker = new Timer(); + const r = ticker.setTimeout(() => (count += 1), 5000); + expect(r).toBe(true); + + const r2 = ticker.setTimeout(() => (count += 1), 1000); + expect(r2).toBe(false); + + await delay(10000); + + expect(count).toBe(1); + + const r3 = ticker.setTimeout(() => (count += 1), 5000); + expect(r3).toBe(true); + ticker.reset(); + + await delay(10000); + + expect(count).toBe(1); + const r4 = ticker.setTimeout(() => (count += 1), 5000); + expect(r4).toBe(true); + + const r5 = ticker.setTimeout(() => (count += 1), 60000); + expect(r5).toBe(false); + + ticker.reset(); + + const r6 = ticker.setTimeout(() => (count += 1), 5000); + expect(r6).toBe(true); + + ticker.reset(); +}); diff --git a/tests/timer/test00002.test.ts b/tests/timer/test00002.test.ts new file mode 100644 index 0000000..3ad868f --- /dev/null +++ b/tests/timer/test00002.test.ts @@ -0,0 +1,38 @@ +/* + * Copyright (c) + * Weyoss + * https://github.com/weyoss + * + * This source code is licensed under the MIT license found in the LICENSE file + * in the root directory of this source tree. + */ + +import { delay } from 'bluebird'; +import { Timer } from '../../src/timer/timer'; + +test('Timer.setInterval()', async () => { + let count = 0; + const ticker = new Timer(); + const r = ticker.setInterval(() => (count += 1), 5000); + expect(r).toBe(true); + + const r2 = ticker.setInterval(() => (count += 1), 1000); + expect(r2).toBe(false); + + await delay(5000); + + expect(count).toBe(1); + + const r3 = ticker.setInterval(() => (count += 1), 5000); + expect(r3).toBe(false); + + await delay(15000); + + expect(count).toBeGreaterThan(2); + ticker.reset(); + + const r4 = ticker.setInterval(() => (count += 1), 5000); + expect(r4).toBe(true); + + ticker.reset(); +}); diff --git a/tests/timer/test00003.test.ts b/tests/timer/test00003.test.ts new file mode 100644 index 0000000..45461b7 --- /dev/null +++ b/tests/timer/test00003.test.ts @@ -0,0 +1,21 @@ +/* + * Copyright (c) + * Weyoss + * https://github.com/weyoss + * + * This source code is licensed under the MIT license found in the LICENSE file + * in the root directory of this source tree. + */ + +import { Timer } from '../../src/timer/timer'; + +test('Timer.setInterval() always blocks Timer.setTimeout()', async () => { + const ticker = new Timer(); + const r = ticker.setInterval(() => void 0, 5000); + expect(r).toBe(true); + + const r2 = ticker.setTimeout(() => void 0, 1000); + expect(r2).toBe(false); + + ticker.reset(); +}); diff --git a/tests/timer/test00004.test.ts b/tests/timer/test00004.test.ts new file mode 100644 index 0000000..751989e --- /dev/null +++ b/tests/timer/test00004.test.ts @@ -0,0 +1,26 @@ +/* + * Copyright (c) + * Weyoss + * https://github.com/weyoss + * + * This source code is licensed under the MIT license found in the LICENSE file + * in the root directory of this source tree. + */ + +import { delay } from 'bluebird'; +import { Timer } from '../../src/timer/timer'; + +test('Timer.setTimeout() should block Timer.setInterval() when not fired', async () => { + const ticker = new Timer(); + const r = ticker.setTimeout(() => void 0, 5000); + expect(r).toBe(true); + + const r2 = ticker.setInterval(() => void 0, 1000); + expect(r2).toBe(false); + + await delay(10000); + + const r3 = ticker.setInterval(() => void 0, 1000); + expect(r3).toBe(true); + ticker.reset(); +}); diff --git a/tests/worker/test00001.test.ts b/tests/worker/test00001.test.ts index a6355aa..0291264 100644 --- a/tests/worker/test00001.test.ts +++ b/tests/worker/test00001.test.ts @@ -7,64 +7,25 @@ * in the root directory of this source tree. */ -import { Worker } from '../../src/worker/worker'; -import { delay, promisifyAll } from 'bluebird'; -import { WorkerRunner } from '../../src/worker/worker-runner/worker-runner'; -import { getRedisInstance } from '../common'; -import { WorkerPool } from '../../src/worker/worker-runner/worker-pool'; -import { ICallback } from '../../types'; - -class UnmanagedCounterWorker extends Worker { - count = 0; - - constructor() { - super(false); - } - - override work(cb: ICallback) { - if (this.count >= 3) this.quit(() => void 0); - else this.count += 1; - cb(); - } -} - -class ManagedCounterWorker extends Worker { - count = 0; - - constructor() { - super(true); - } - - override work(cb: ICallback) { - if (this.count < 3) this.count += 1; - cb(); - } -} - -describe('Worker & WorkerRunner', () => { - test('Case 1', async () => { - const worker = promisifyAll(new UnmanagedCounterWorker()); - await worker.runAsync(); - await delay(5000); - expect(worker.count).toBe(3); - }); - - test('Case 2', async () => { - const worker1 = promisifyAll(new ManagedCounterWorker()); - const worker2 = promisifyAll(new ManagedCounterWorker()); - await expect(worker1.runAsync()).rejects.toThrow( - 'You can not run a managed worker', - ); - const client = await getRedisInstance(); - const workerRunner = promisifyAll( - new WorkerRunner(client, 'my-runner', new WorkerPool(), console), - ); - workerRunner.addWorker(worker1); - workerRunner.addWorker(worker2); - await workerRunner.runAsync(); - await delay(10000); - expect(worker1.count).toBe(3); - expect(worker2.count).toBe(3); - await workerRunner.quitAsync(); - }); +import { promisifyAll } from 'bluebird'; +import path from 'path'; +import { WorkerCallable } from '../../src/worker/worker-callable'; +import { WorkerPayloadRequiredError } from '../../src/worker/errors'; + +it('WorkerCallable: case 1', async () => { + const filename = path.resolve(__dirname, './workers/worker-ok.worker.js'); + const worker = promisifyAll( + new WorkerCallable(filename), + ); + const reply = await worker.callAsync('Hello world!'); + expect(reply).toEqual('Hello world!'); + + await expect(async () => worker.callAsync(null)).rejects.toThrow( + WorkerPayloadRequiredError, + ); + + await worker.quitAsync(); + + // second timer is OK + await worker.quitAsync(); }); diff --git a/tests/worker/test00002.test.ts b/tests/worker/test00002.test.ts new file mode 100644 index 0000000..8fb03a1 --- /dev/null +++ b/tests/worker/test00002.test.ts @@ -0,0 +1,72 @@ +/* + * Copyright (c) + * Weyoss + * https://github.com/weyoss + * + * This source code is licensed under the MIT license found in the LICENSE file + * in the root directory of this source tree. + */ + +import { promisifyAll } from 'bluebird'; +import path from 'path'; +import { WorkerCallable } from '../../src/worker/worker-callable'; + +it('WorkerCallable: case 2', async () => { + const filename = path.resolve( + __dirname, + './workers/worker-non-existent.worker.js', + ); + const worker = promisifyAll(new WorkerCallable(filename)); + + await expect(worker.callAsync('Hello world!')).rejects.toThrow( + 'Error code: FILE_READ_ERROR', + ); + + await expect(worker.callAsync('Hello world!')).rejects.toThrow( + 'Error code: FILE_READ_ERROR', + ); + + const filename2 = path.resolve( + __dirname, + './workers/worker-non-existent.worker.jsc', + ); + const worker2 = promisifyAll(new WorkerCallable(filename2)); + await expect(worker2.callAsync('Hello world!')).rejects.toThrow( + 'Error code: FILE_EXTENSION_ERROR', + ); + + const filename3 = path.resolve(__dirname, './workers/worker-error.worker.js'); + const worker3 = promisifyAll(new WorkerCallable(filename3)); + await expect(worker3.callAsync('Hello world!')).rejects.toThrow( + 'Error code: PROCESSING_ERROR', + ); + await worker3.quitAsync(); + + const filename4 = path.resolve( + __dirname, + './workers/worker-exception.worker.js', + ); + const worker4 = promisifyAll(new WorkerCallable(filename4)); + await expect(worker4.callAsync('Hello world!')).rejects.toThrow( + 'Error code: PROCESSING_CAUGHT_ERROR', + ); + await worker4.quitAsync(); + + const filename5 = path.resolve( + __dirname, + './workers/worker-faulty.worker.js', + ); + const worker5 = promisifyAll(new WorkerCallable(filename5)); + await expect(worker5.callAsync('Hello world!')).rejects.toThrow( + 'Error code: INVALID_WORKER_TYPE', + ); + + const filename6 = path.resolve( + __dirname, + './workers/worker-faulty-exit.worker.js', + ); + const worker6 = promisifyAll(new WorkerCallable(filename6)); + await expect(worker6.callAsync('Hello world!')).rejects.toThrow( + 'Error code: TERMINATED', + ); +}); diff --git a/tests/worker/test00003.test.ts b/tests/worker/test00003.test.ts new file mode 100644 index 0000000..22e8b5a --- /dev/null +++ b/tests/worker/test00003.test.ts @@ -0,0 +1,50 @@ +/* + * Copyright (c) + * Weyoss + * https://github.com/weyoss + * + * This source code is licensed under the MIT license found in the LICENSE file + * in the root directory of this source tree. + */ + +import { EventEmitter } from 'events'; +import { delay } from 'bluebird'; +import { EWorkerThreadExecutionCode, EWorkerType } from '../../types/worker'; +import path from 'path'; + +it('WorkerCallable: case 3', async () => { + const mockParentPort: EventEmitter & { postMessage?: () => void } = + new EventEmitter(); + mockParentPort.postMessage = jest.fn(); + + const mockFilename = path.resolve(__dirname, './workers/worker-ok.worker.js'); + const mockType = EWorkerType.CALLABLE; + + jest.mock('worker_threads', () => { + return { + isMainThread: false, + parentPort: mockParentPort, + workerData: { filename: mockFilename, type: mockType }, + }; + }); + + // eslint-disable-next-line + // @ts-ignore + const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {}); // type-coverage:ignore-line + + await import('../../src/worker/worker-thread'); + + await delay(5000); + + mockParentPort.emit('message', '123456789'); + + await delay(5000); + + expect(mockParentPort.postMessage).toHaveBeenCalledTimes(1); + expect(mockParentPort.postMessage).toHaveBeenCalledWith({ + code: EWorkerThreadExecutionCode.OK, + data: '123456789', + }); + + expect(mockExit).toHaveBeenCalledTimes(0); // type-coverage:ignore-line +}); diff --git a/tests/worker/test00004.test.ts b/tests/worker/test00004.test.ts new file mode 100644 index 0000000..47a0226 --- /dev/null +++ b/tests/worker/test00004.test.ts @@ -0,0 +1,53 @@ +/* + * Copyright (c) + * Weyoss + * https://github.com/weyoss + * + * This source code is licensed under the MIT license found in the LICENSE file + * in the root directory of this source tree. + */ + +import { EventEmitter } from 'events'; +import { delay } from 'bluebird'; +import { EWorkerThreadExecutionCode, EWorkerType } from '../../types/worker'; +import path from 'path'; + +it('WorkerCallable: case 4', async () => { + const mockParentPort: EventEmitter & { postMessage?: () => void } = + new EventEmitter(); + mockParentPort.postMessage = jest.fn(); + + const mockFilename = path.resolve( + __dirname, + './workers/worker-error.worker.js', + ); + const mockType = EWorkerType.CALLABLE; + + jest.mock('worker_threads', () => { + return { + isMainThread: false, + parentPort: mockParentPort, + workerData: { filename: mockFilename, type: mockType }, + }; + }); + + // eslint-disable-next-line + // @ts-ignore + const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {}); // type-coverage:ignore-line + + await import('../../src/worker/worker-thread'); + + await delay(5000); + + mockParentPort.emit('message', '123456789'); + + await delay(5000); + + expect(mockParentPort.postMessage).toHaveBeenCalledTimes(1); + expect(mockParentPort.postMessage).toHaveBeenCalledWith({ + code: EWorkerThreadExecutionCode.PROCESSING_ERROR, + error: { name: 'Error', message: 'MY_ERROR' }, + }); + + expect(mockExit).toHaveBeenCalledTimes(0); // type-coverage:ignore-line +}); diff --git a/tests/worker/test00005.test.ts b/tests/worker/test00005.test.ts new file mode 100644 index 0000000..1eff2ea --- /dev/null +++ b/tests/worker/test00005.test.ts @@ -0,0 +1,53 @@ +/* + * Copyright (c) + * Weyoss + * https://github.com/weyoss + * + * This source code is licensed under the MIT license found in the LICENSE file + * in the root directory of this source tree. + */ + +import { EventEmitter } from 'events'; +import { delay } from 'bluebird'; +import { EWorkerThreadExecutionCode, EWorkerType } from '../../types/worker'; +import path from 'path'; + +it('WorkerCallable: case 5', async () => { + const mockParentPort: EventEmitter & { postMessage?: () => void } = + new EventEmitter(); + mockParentPort.postMessage = jest.fn(); + + const mockFilename = path.resolve( + __dirname, + './workers/worker-exception.worker.js', + ); + const mockType = EWorkerType.CALLABLE; + + jest.mock('worker_threads', () => { + return { + isMainThread: false, + parentPort: mockParentPort, + workerData: { filename: mockFilename, type: mockType }, + }; + }); + + // eslint-disable-next-line + // @ts-ignore + const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {}); // type-coverage:ignore-line + + await import('../../src/worker/worker-thread'); + + await delay(5000); + + mockParentPort.emit('message', '123456789'); + + await delay(5000); + + expect(mockParentPort.postMessage).toHaveBeenCalledTimes(1); + expect(mockParentPort.postMessage).toHaveBeenCalledWith({ + code: EWorkerThreadExecutionCode.PROCESSING_CAUGHT_ERROR, + error: { name: 'Error', message: 'THROW_ERROR' }, + }); + + expect(mockExit).toHaveBeenCalledTimes(0); // type-coverage:ignore-line +}); diff --git a/tests/worker/test00006.test.ts b/tests/worker/test00006.test.ts new file mode 100644 index 0000000..a366f4d --- /dev/null +++ b/tests/worker/test00006.test.ts @@ -0,0 +1,59 @@ +/* + * Copyright (c) + * Weyoss + * https://github.com/weyoss + * + * This source code is licensed under the MIT license found in the LICENSE file + * in the root directory of this source tree. + */ + +import { EventEmitter } from 'events'; +import { delay } from 'bluebird'; +import { EWorkerThreadExitCode, EWorkerType } from '../../types/worker'; +import path from 'path'; + +it('WorkerCallable: case 6', async () => { + const mockParentPort: EventEmitter & { postMessage?: () => void } = + new EventEmitter(); + mockParentPort.postMessage = jest.fn(); + + const mockFilename = path.resolve( + __dirname, + './workers/worker-faulty.worker.js', + ); + const mockType = EWorkerType.CALLABLE; + + jest.mock('worker_threads', () => { + return { + isMainThread: false, + parentPort: mockParentPort, + workerData: { filename: mockFilename, type: mockType }, + }; + }); + + // eslint-disable-next-line + // @ts-ignore + const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {}); // type-coverage:ignore-line + + await import('../../src/worker/worker-thread'); + + await delay(5000); + + mockParentPort.emit('message', '123456789'); + + await delay(5000); + + expect(mockParentPort.postMessage).toHaveBeenCalledTimes(1); + expect(mockParentPort.postMessage).toHaveBeenCalledWith({ + code: EWorkerThreadExitCode.INVALID_WORKER_TYPE, + error: null, + }); + + // type-coverage:ignore-next-line + expect(mockExit).toHaveBeenCalledTimes(1); + + // type-coverage:ignore-next-line + expect(mockExit).toHaveBeenCalledWith( + EWorkerThreadExitCode.INVALID_WORKER_TYPE, + ); +}); diff --git a/tests/worker/test00007.test.ts b/tests/worker/test00007.test.ts new file mode 100644 index 0000000..628f294 --- /dev/null +++ b/tests/worker/test00007.test.ts @@ -0,0 +1,61 @@ +/* + * Copyright (c) + * Weyoss + * https://github.com/weyoss + * + * This source code is licensed under the MIT license found in the LICENSE file + * in the root directory of this source tree. + */ + +import { EventEmitter } from 'events'; +import { delay } from 'bluebird'; +import { EWorkerThreadExitCode, EWorkerType } from '../../types/worker'; +import path from 'path'; + +it('WorkerCallable: case 7', async () => { + const mockParentPort: EventEmitter & { postMessage?: () => void } = + new EventEmitter(); + mockParentPort.postMessage = jest.fn(); + + const mockFilename = path.resolve( + __dirname, + './workers/worker-faulty-exit.worker.js', + ); + const mockType = EWorkerType.CALLABLE; + + jest.mock('worker_threads', () => { + return { + isMainThread: false, + parentPort: mockParentPort, + workerData: { filename: mockFilename, type: mockType }, + }; + }); + + // eslint-disable-next-line + // @ts-ignore + const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {}); // type-coverage:ignore-line + + await import('../../src/worker/worker-thread'); + + await delay(5000); + + mockParentPort.emit('message', '123456789'); + + await delay(5000); + + // In real world the thread would exit when the file has been imported + // But process.exit is mocked so getHandlerFn() continues and trigger a second + // exit call + + // type-coverage:ignore-next-line + expect(mockExit).toHaveBeenCalledTimes(2); + + // type-coverage:ignore-next-line + expect(mockExit).toHaveBeenNthCalledWith(1, 333); + + // type-coverage:ignore-next-line + expect(mockExit).toHaveBeenNthCalledWith( + 2, + EWorkerThreadExitCode.INVALID_WORKER_TYPE, + ); +}); diff --git a/tests/worker/test00008.test.ts b/tests/worker/test00008.test.ts new file mode 100644 index 0000000..57a7485 --- /dev/null +++ b/tests/worker/test00008.test.ts @@ -0,0 +1,58 @@ +/* + * Copyright (c) + * Weyoss + * https://github.com/weyoss + * + * This source code is licensed under the MIT license found in the LICENSE file + * in the root directory of this source tree. + */ + +import { EventEmitter } from 'events'; +import { delay } from 'bluebird'; +import { EWorkerThreadExitCode } from '../../types/worker'; + +it('WorkerCallable: case 8', async () => { + const mockParentPort: EventEmitter & { postMessage?: () => void } = + new EventEmitter(); + mockParentPort.postMessage = jest.fn(); + + jest.mock('worker_threads', () => { + return { + isMainThread: false, + parentPort: mockParentPort, + workerData: '', + }; + }); + + // eslint-disable-next-line + // @ts-ignore + const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {}); // type-coverage:ignore-line + + await import('../../src/worker/worker-thread'); + + await delay(5000); + + mockParentPort.emit('message', '123456789'); + + await delay(5000); + + // In real world the thread would exit when workerData is empty + // But process.exit is mocked so getHandlerFn() is called with empty workerData + expect(mockParentPort.postMessage).toHaveBeenCalledTimes(1); + expect(mockParentPort.postMessage).toHaveBeenNthCalledWith(1, { + code: EWorkerThreadExitCode.WORKER_DATA_REQUIRED, + error: null, + }); + + // In real world the thread would exit when workerData is empty + // But process.exit is mocked so getHandlerFn() is called with empty workerData + + // type-coverage:ignore-next-line + expect(mockExit).toHaveBeenCalledTimes(1); + + // type-coverage:ignore-next-line + expect(mockExit).toHaveBeenNthCalledWith( + 1, + EWorkerThreadExitCode.WORKER_DATA_REQUIRED, + ); +}); diff --git a/tests/worker/test00009.test.ts b/tests/worker/test00009.test.ts new file mode 100644 index 0000000..8cd796a --- /dev/null +++ b/tests/worker/test00009.test.ts @@ -0,0 +1,63 @@ +/* + * Copyright (c) + * Weyoss + * https://github.com/weyoss + * + * This source code is licensed under the MIT license found in the LICENSE file + * in the root directory of this source tree. + */ + +import { EventEmitter } from 'events'; +import { delay } from 'bluebird'; +import { EWorkerThreadExitCode, EWorkerType } from '../../types/worker'; +import path from 'path'; + +it('WorkerCallable: case 9', async () => { + const mockParentPort: EventEmitter & { postMessage?: () => void } = + new EventEmitter(); + mockParentPort.postMessage = jest.fn(); + + const mockFilename = path.resolve( + __dirname, + './workers/worker-non-existent.js', + ); + const mockType = EWorkerType.CALLABLE; + + jest.mock('worker_threads', () => { + return { + isMainThread: false, + parentPort: mockParentPort, + workerData: { filename: mockFilename, type: mockType }, + }; + }); + + // eslint-disable-next-line + // @ts-ignore + const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {}); // type-coverage:ignore-line + + await import('../../src/worker/worker-thread'); + + await delay(5000); + + mockParentPort.emit('message', '123456789'); + + await delay(5000); + + // In real world the thread would exit when the file has been imported + // But process.exit is mocked so the code continues execution + + // type-coverage:ignore-next-line + expect(mockExit).toHaveBeenCalledTimes(2); + + // type-coverage:ignore-next-line + expect(mockExit).toHaveBeenNthCalledWith( + 1, + EWorkerThreadExitCode.FILE_READ_ERROR, + ); + + // type-coverage:ignore-next-line + expect(mockExit).toHaveBeenNthCalledWith( + 2, + EWorkerThreadExitCode.FILE_IMPORT_ERROR, + ); +}); diff --git a/tests/worker/test00010.test.ts b/tests/worker/test00010.test.ts new file mode 100644 index 0000000..cb9d996 --- /dev/null +++ b/tests/worker/test00010.test.ts @@ -0,0 +1,69 @@ +/* + * Copyright (c) + * Weyoss + * https://github.com/weyoss + * + * This source code is licensed under the MIT license found in the LICENSE file + * in the root directory of this source tree. + */ + +import { EventEmitter } from 'events'; +import { delay } from 'bluebird'; +import { EWorkerThreadExitCode, EWorkerType } from '../../types/worker'; +import path from 'path'; + +it('WorkerCallable: case 10', async () => { + const mockParentPort: EventEmitter & { postMessage?: () => void } = + new EventEmitter(); + mockParentPort.postMessage = jest.fn(); + + const mockFilename = path.resolve( + __dirname, + './workers/worker-non-existent.cjsx', + ); + const mockType = EWorkerType.CALLABLE; + + jest.mock('worker_threads', () => { + return { + isMainThread: false, + parentPort: mockParentPort, + workerData: { filename: mockFilename, type: mockType }, + }; + }); + + // eslint-disable-next-line + // @ts-ignore + const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {}); // type-coverage:ignore-line + + await import('../../src/worker/worker-thread'); + + await delay(5000); + + mockParentPort.emit('message', '123456789'); + + await delay(5000); + + // In real world the thread would exit when the file has been imported + // But process.exit is mocked so the code continues execution + + // type-coverage:ignore-next-line + expect(mockExit).toHaveBeenCalledTimes(3); + + // type-coverage:ignore-next-line + expect(mockExit).toHaveBeenNthCalledWith( + 1, + EWorkerThreadExitCode.FILE_EXTENSION_ERROR, + ); + + // type-coverage:ignore-next-line + expect(mockExit).toHaveBeenNthCalledWith( + 2, + EWorkerThreadExitCode.FILE_READ_ERROR, + ); + + // type-coverage:ignore-next-line + expect(mockExit).toHaveBeenNthCalledWith( + 3, + EWorkerThreadExitCode.FILE_IMPORT_ERROR, + ); +}); diff --git a/tests/worker/test00011.test.ts b/tests/worker/test00011.test.ts new file mode 100644 index 0000000..cb024ef --- /dev/null +++ b/tests/worker/test00011.test.ts @@ -0,0 +1,39 @@ +/* + * Copyright (c) + * Weyoss + * https://github.com/weyoss + * + * This source code is licensed under the MIT license found in the LICENSE file + * in the root directory of this source tree. + */ + +import { promisifyAll } from 'bluebird'; +import path from 'path'; +import { + WorkerAlreadyDownError, + WorkerAlreadyRunningError, +} from '../../src/worker/errors'; +import { WorkerRunnable } from '../../src/worker/worker-runnable'; + +it('WorkerRunnable', async () => { + const filename = path.resolve( + __dirname, + './workers/worker-runnable-ok.worker.js', + ); + const worker = promisifyAll(new WorkerRunnable(filename)); + // will emit an error upon shutdown + worker.on('worker.error', (err) => { + console.error(err); + }); + await worker.runAsync('Hello world!'); + + await expect(async () => worker.runAsync('Hello world!')).rejects.toThrow( + WorkerAlreadyRunningError, + ); + + await worker.quitAsync(); + + await expect(async () => worker.quitAsync()).rejects.toThrow( + WorkerAlreadyDownError, + ); +}); diff --git a/tests/worker/test00012.test.ts b/tests/worker/test00012.test.ts new file mode 100644 index 0000000..7e3e18d --- /dev/null +++ b/tests/worker/test00012.test.ts @@ -0,0 +1,31 @@ +/* + * Copyright (c) + * Weyoss + * https://github.com/weyoss + * + * This source code is licensed under the MIT license found in the LICENSE file + * in the root directory of this source tree. + */ + +import { delay, promisifyAll } from 'bluebird'; +import path from 'path'; +import { WorkerResourceGroup } from '../../src/worker/worker-resource-group'; +import { getRedisInstance } from '../common'; + +it('WorkerResourceGroup: addWorker()', async () => { + const redisClient = await getRedisInstance(); + const workerRunnableResourceGroup = promisifyAll( + new WorkerResourceGroup(redisClient, console, 'mygroupid'), + ); + + const filename = path.resolve( + __dirname, + './workers/worker-runnable-ok.worker.js', + ); + workerRunnableResourceGroup.addWorker(filename, 'hello world'); + await workerRunnableResourceGroup.runAsync(); + + await delay(10000); + + await workerRunnableResourceGroup.shutdownAsync(); +}); diff --git a/tests/worker/test00013.test.ts b/tests/worker/test00013.test.ts new file mode 100644 index 0000000..b769570 --- /dev/null +++ b/tests/worker/test00013.test.ts @@ -0,0 +1,28 @@ +/* + * Copyright (c) + * Weyoss + * https://github.com/weyoss + * + * This source code is licensed under the MIT license found in the LICENSE file + * in the root directory of this source tree. + */ + +import { delay, promisifyAll } from 'bluebird'; +import path from 'path'; +import { WorkerResourceGroup } from '../../src/worker/worker-resource-group'; +import { getRedisInstance } from '../common'; + +it('WorkerResourceGroup: loadFromDir()', async () => { + const redisClient = await getRedisInstance(); + const workerRunnableResourceGroup = promisifyAll( + new WorkerResourceGroup(redisClient, console, 'mygroupid'), + ); + + const dir = path.resolve(__dirname, './workers'); + await workerRunnableResourceGroup.loadFromDirAsync(dir, 'hello world'); + await workerRunnableResourceGroup.runAsync(); + + await delay(10000); + + await workerRunnableResourceGroup.shutdownAsync(); +}); diff --git a/tests/worker/workers/worker-error.worker.ts b/tests/worker/workers/worker-error.worker.ts new file mode 100644 index 0000000..0a51aa8 --- /dev/null +++ b/tests/worker/workers/worker-error.worker.ts @@ -0,0 +1,14 @@ +/* + * Copyright (c) + * Weyoss + * https://github.com/weyoss + * + * This source code is licensed under the MIT license found in the LICENSE file + * in the root directory of this source tree. + */ + +import { ICallback } from '../../../types'; + +export default function myWorkerCallable(msg: string, cb: ICallback) { + cb(new Error('MY_ERROR')); +} diff --git a/tests/worker/workers/worker-exception.worker.ts b/tests/worker/workers/worker-exception.worker.ts new file mode 100644 index 0000000..70a598b --- /dev/null +++ b/tests/worker/workers/worker-exception.worker.ts @@ -0,0 +1,12 @@ +/* + * Copyright (c) + * Weyoss + * https://github.com/weyoss + * + * This source code is licensed under the MIT license found in the LICENSE file + * in the root directory of this source tree. + */ + +export default function myWorkerCallable() { + throw new Error('THROW_ERROR'); +} diff --git a/tests/worker/workers/worker-faulty-exit.worker.ts b/tests/worker/workers/worker-faulty-exit.worker.ts new file mode 100644 index 0000000..5577ab8 --- /dev/null +++ b/tests/worker/workers/worker-faulty-exit.worker.ts @@ -0,0 +1,12 @@ +/* + * Copyright (c) + * Weyoss + * https://github.com/weyoss + * + * This source code is licensed under the MIT license found in the LICENSE file + * in the root directory of this source tree. + */ + +export const myWorkerCallable = 6; + +process.exit(333); diff --git a/tests/worker/workers/worker-faulty.worker.ts b/tests/worker/workers/worker-faulty.worker.ts new file mode 100644 index 0000000..61d28db --- /dev/null +++ b/tests/worker/workers/worker-faulty.worker.ts @@ -0,0 +1,10 @@ +/* + * Copyright (c) + * Weyoss + * https://github.com/weyoss + * + * This source code is licensed under the MIT license found in the LICENSE file + * in the root directory of this source tree. + */ + +export const myWorkerCallable = 6; diff --git a/tests/worker/workers/worker-ok.worker.ts b/tests/worker/workers/worker-ok.worker.ts new file mode 100644 index 0000000..2172cad --- /dev/null +++ b/tests/worker/workers/worker-ok.worker.ts @@ -0,0 +1,17 @@ +/* + * Copyright (c) + * Weyoss + * https://github.com/weyoss + * + * This source code is licensed under the MIT license found in the LICENSE file + * in the root directory of this source tree. + */ + +import { ICallback } from '../../../types'; + +export default function myWorkerCallable( + payload: string, + cb: ICallback, +) { + setTimeout(() => cb(null, payload), 5000); +} diff --git a/tests/worker/workers/worker-runnable-ok.worker.ts b/tests/worker/workers/worker-runnable-ok.worker.ts new file mode 100644 index 0000000..4ca6766 --- /dev/null +++ b/tests/worker/workers/worker-runnable-ok.worker.ts @@ -0,0 +1,14 @@ +/* + * Copyright (c) + * Weyoss + * https://github.com/weyoss + * + * This source code is licensed under the MIT license found in the LICENSE file + * in the root directory of this source tree. + */ + +import { ICallback } from '../../../types'; + +export default function myWorkerRunnable(msg: string, cb: ICallback) { + setTimeout(cb, 5000); +}