diff --git a/.swcrc b/.swcrc index 4fd561667..b3bf03db1 100644 --- a/.swcrc +++ b/.swcrc @@ -8,14 +8,15 @@ "legacyDecorator": true, "decoratorMetadata": true }, + "target": "es2017", + "keepClassNames": true, "baseUrl": ".", "paths": { - "@ogma/*": [ - "packages/*/src/" - ] + "@ogma/*": ["packages/*/src/"] } }, "module": { - "type": "commonjs" + "type": "commonjs", + "strictMode": true } } diff --git a/packages/nestjs-module/test/app.service.spec.ts b/packages/nestjs-module/test/app.service.spec.ts index 859adbfd6..12bff3556 100644 --- a/packages/nestjs-module/test/app.service.spec.ts +++ b/packages/nestjs-module/test/app.service.spec.ts @@ -1,36 +1,40 @@ import { AppService } from './app.service'; import { Test } from '@nestjs/testing'; +import { spy, Stub } from 'hanbi'; +import { suite } from 'uvu'; +import { equal, ok } from 'uvu/assert'; -describe('AppService', () => { - let service: AppService; - let logger: { log: jest.Mock }; - - beforeEach(async () => { +const AppServiceSuite = suite<{ service: AppService; logSpy: Stub<(message: string) => void> }>( + 'AppService', + { + service: undefined, + logSpy: spy(), + }, +); +AppServiceSuite.before(async (context) => { + try { const modRef = await Test.createTestingModule({ providers: [ AppService, { provide: 'OGMA_SERVICE:AppService', useValue: { - log: jest.fn(), + log: context.logSpy.handler, }, }, ], }).compile(); - service = modRef.get(AppService); - logger = modRef.get<{ log: jest.Mock }>('OGMA_SERVICE:AppService'); - }); - - it('should be truthy', () => { - expect(service).toBeTruthy(); - }); - - it('should return { hello: world }', () => { - expect(service.getHello()).toEqual({ hello: 'world' }); - expect(logger.log).toHaveBeenCalledWith('Say Hello'); - }); + context.service = modRef.get(AppService); + } catch (err) { + console.error(err); + } }); - -process.on('unhandledRejection', (err) => { - throw new Error(err as any); +AppServiceSuite('it should create the service', ({ service }) => { + ok(service); +}); +AppServiceSuite('It should return { hello: world } and log "Say Hello"', ({ service, logSpy }) => { + equal(service.getHello(), { hello: 'world' }); + ok(logSpy.calledWith('Say Hello')); }); + +AppServiceSuite.run(); diff --git a/packages/nestjs-module/test/app.service.ts b/packages/nestjs-module/test/app.service.ts index 5371c6419..7c3406ea8 100644 --- a/packages/nestjs-module/test/app.service.ts +++ b/packages/nestjs-module/test/app.service.ts @@ -3,7 +3,7 @@ import { OgmaLogger, OgmaService } from '../src'; @Injectable() export class AppService { - constructor(@OgmaLogger(AppService) private readonly logger: OgmaService) {} + constructor(@OgmaLogger('AppService') private readonly logger: OgmaService) {} getHello() { this.logger.log('Say Hello'); diff --git a/packages/nestjs-module/test/delegator.service.spec.ts b/packages/nestjs-module/test/delegator.service.spec.ts index ab284540b..44b972f32 100644 --- a/packages/nestjs-module/test/delegator.service.spec.ts +++ b/packages/nestjs-module/test/delegator.service.spec.ts @@ -1,7 +1,8 @@ -import { createMock } from '@golevelup/ts-jest'; import { ExecutionContext } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import { OgmaInterceptorServiceOptions } from '../src'; +import { spy, stubMethod } from 'hanbi'; +import { suite } from 'uvu'; +import { equal, ok } from 'uvu/assert'; import { LogObject } from '../src/interceptor/interfaces/log.interface'; import { AbstractInterceptorService, @@ -16,12 +17,10 @@ const logProperly = (type: 'http' | 'gql' | 'ws' | 'rpc') => `should log properl const setRequestIdProperly = (type: 'http' | 'gql' | 'ws' | 'rpc') => `should set request id properly for ${type}`; -const abstractInterceptorServiceMock = () => createMock(); - -const spyFactory = ( - parser: AbstractInterceptorService, - method: 'getSuccessContext' | 'getErrorContext' | 'setRequestId', -): jest.SpyInstance => jest.spyOn(parser, method); +const abstractInterceptorServiceMock = () => ({ + getSuccessContext: spy().handler, + getErrorContext: spy().handler, +}); const parserReturn: LogObject = { callPoint: '/', @@ -33,226 +32,144 @@ const parserReturn: LogObject = { contentLength: 5, }; -const httpContext = { - getType: () => 'http', - getArgs: () => [1, 2, 3], -}; +const typeAndContexts = [ + { + type: 'http', + ctxMock: { + getType: () => 'http', + }, + }, + { + type: 'ws', + ctxMock: { + getType: () => 'ws', + }, + }, + { + type: 'gql', + ctxMock: { + getType: () => 'graphql', + }, + }, + { + type: 'rpc', + ctxMock: { + getType: () => 'rpc', + }, + }, +] as const; + +const executionMockFactory = (getType: { getType: () => string }): ExecutionContext => ({ + switchToHttp: spy().handler, + switchToRpc: spy().handler, + switchToWs: spy().handler, + getArgByIndex: spy().handler, + getArgs: spy().handler, + getClass: spy().handler, + getHandler: spy().handler, + getType: getType.getType as any, +}); const parsedString = { log: '127.0.0.1 - GET / HTTP/1.1 200 2ms - 5', meta: undefined }; -describe('DelegatorService', () => { - let delegate: DelegatorService; - let http: HttpInterceptorService; - let gql: GqlInterceptorService; - let ws: WebsocketInterceptorService; - let rpc: RpcInterceptorService; +const DelegatorServiceSuite = suite<{ + delegate: DelegatorService; + parsers: Record<'http' | 'gql' | 'ws' | 'rpc', AbstractInterceptorService>; +}>('DelegatorService Suite', { + delegate: undefined, + parsers: { + http: undefined, + gql: undefined, + ws: undefined, + rpc: undefined, + }, +}); +DelegatorServiceSuite.before(async (context) => { + const mod = await Test.createTestingModule({ + providers: [ + DelegatorService, + { + provide: HttpInterceptorService, + useFactory: abstractInterceptorServiceMock, + }, + { + provide: GqlInterceptorService, + useFactory: abstractInterceptorServiceMock, + }, + { + provide: WebsocketInterceptorService, + useFactory: abstractInterceptorServiceMock, + }, + { + provide: RpcInterceptorService, + useFactory: abstractInterceptorServiceMock, + }, + ], + }).compile(); + context.delegate = mod.get(DelegatorService); + context.parsers.http = mod.get(HttpInterceptorService); + context.parsers.gql = mod.get(GqlInterceptorService); + context.parsers.ws = mod.get(WebsocketInterceptorService); + context.parsers.rpc = mod.get(RpcInterceptorService); +}); +for (const method of ['getSuccessContext', 'getErrorContext'] as const) { + const data = method === 'getSuccessContext' ? 'some data' : new Error('Big oof'); const startTime = 0; - const options: OgmaInterceptorServiceOptions = { + const options = { color: false, json: false, }; - - beforeEach(async () => { - const mod = await Test.createTestingModule({ - providers: [ - DelegatorService, - { - provide: HttpInterceptorService, - useFactory: abstractInterceptorServiceMock, - }, - { - provide: GqlInterceptorService, - useFactory: abstractInterceptorServiceMock, - }, - { - provide: WebsocketInterceptorService, - useFactory: abstractInterceptorServiceMock, - }, - { - provide: RpcInterceptorService, - useFactory: abstractInterceptorServiceMock, - }, - ], - }).compile(); - delegate = mod.get(DelegatorService); - http = mod.get(HttpInterceptorService); - gql = mod.get(GqlInterceptorService); - ws = mod.get(WebsocketInterceptorService); - rpc = mod.get(RpcInterceptorService); - }); - describe('getContextSuccessString', () => { - const data = 'someData'; - it(logProperly('http'), () => { - const ctxMock = createMock(httpContext as Partial); - const spy = spyFactory(http, 'getSuccessContext').mockReturnValueOnce(parserReturn); - expect(delegate.getContextSuccessString(data, ctxMock, startTime, options)).toEqual( - parsedString, - ); - expect(spy).toBeCalledWith( - Buffer.from(JSON.stringify(data)).byteLength, - ctxMock, - startTime, - options, - ); - }); - it(logProperly('gql'), () => { - const ctxMock = createMock({ - getType: () => 'graphql', - }); - const spy = spyFactory(gql, 'getSuccessContext').mockReturnValueOnce(parserReturn); - expect(delegate.getContextSuccessString(data, ctxMock, startTime, options)).toEqual( - parsedString, - ); - expect(spy).toBeCalledWith( - Buffer.from(JSON.stringify(data)).byteLength, - ctxMock, - startTime, - options, - ); - }); - it(logProperly('ws'), () => { - const ctxMock = createMock({ - getType: () => 'ws', - }); - const spy = spyFactory(ws, 'getSuccessContext').mockReturnValueOnce(parserReturn); - expect(delegate.getContextSuccessString(data, ctxMock, startTime, options)).toEqual( - parsedString, - ); - expect(spy).toBeCalledWith( - Buffer.from(JSON.stringify(data)).byteLength, - ctxMock, - startTime, - options, - ); - }); - it(logProperly('rpc'), () => { - const ctxMock = createMock({ - getType: () => 'rpc', - }); - const spy = spyFactory(rpc, 'getSuccessContext').mockReturnValueOnce(parserReturn); - expect(delegate.getContextSuccessString(data, ctxMock, startTime, options)).toEqual( - parsedString, - ); - expect(spy).toBeCalledWith( - Buffer.from(JSON.stringify(data)).byteLength, - ctxMock, - startTime, - options, + for (const { type, ctxMock } of typeAndContexts) { + DelegatorServiceSuite(`${method} ${logProperly(type)}`, ({ delegate, parsers }) => { + const fullContextMock = executionMockFactory(ctxMock); + const parserSpy = stubMethod(parsers[type], method); + parserSpy.returns(parserReturn); + const methodName = + method === 'getSuccessContext' ? 'getContextSuccessString' : 'getContextErrorString'; + equal(delegate[methodName](data, fullContextMock, startTime, options), parsedString); + ok( + parserSpy.calledWith( + method === 'getSuccessContext' + ? Buffer.from(JSON.stringify(data)).byteLength + : (data as any), + fullContextMock, + startTime, + options, + ), ); }); + } +} +for (const { type, ctxMock } of typeAndContexts) { + const requestId = '1598961763272766'; + DelegatorServiceSuite(setRequestIdProperly(type), ({ delegate, parsers }) => { + const fullContextMock = executionMockFactory(ctxMock); + const setSpy = stubMethod(parsers[type], 'setRequestId'); + delegate.setRequestId(fullContextMock, requestId); + ok(setSpy.calledWith(fullContextMock, requestId)); }); - describe('getContextErrorString', () => { - const error = new Error('Big oof'); - it(logProperly('http'), () => { - const spy = spyFactory(http, 'getErrorContext').mockReturnValueOnce(parserReturn); - const ctxMock = createMock(httpContext as Partial); - expect(delegate.getContextErrorString(error, ctxMock, startTime, options)).toEqual( - parsedString, - ); - expect(spy).toBeCalledWith(error, ctxMock, startTime, options); - }); - it(logProperly('gql'), () => { - const spy = spyFactory(gql, 'getErrorContext').mockReturnValueOnce(parserReturn); - const ctxMock = createMock({ - getType: () => 'graphql', - }); - expect(delegate.getContextErrorString(error, ctxMock, startTime, options)).toEqual( - parsedString, - ); - expect(spy).toBeCalledWith(error, ctxMock, startTime, options); - }); - it(logProperly('ws'), () => { - const spy = spyFactory(ws, 'getErrorContext').mockReturnValueOnce(parserReturn); - const ctxMock = createMock({ - getType: () => 'ws', - }); - expect(delegate.getContextErrorString(error, ctxMock, startTime, options)).toEqual( - parsedString, - ); - expect(spy).toBeCalledWith(error, ctxMock, startTime, options); - }); - it(logProperly('rpc'), () => { - const spy = spyFactory(rpc, 'getErrorContext').mockReturnValueOnce(parserReturn); - const ctxMock = createMock({ - getType: () => 'rpc', - }); - expect(delegate.getContextErrorString(error, ctxMock, startTime, options)).toEqual( - parsedString, - ); - expect(spy).toBeCalledWith(error, ctxMock, startTime, options); - }); +} +DelegatorServiceSuite('It should use JSON format instead of string', ({ delegate, parsers }) => { + const fullCtxMock = executionMockFactory({ getType: () => 'http' }); + const parseSpy = stubMethod(parsers.http, 'getSuccessContext'); + parseSpy.returns(parserReturn); + const options = { json: true, color: false }; + equal(delegate.getContextSuccessString('data', fullCtxMock, 0, options), { + log: parserReturn, + meta: undefined, }); - describe('setRequestId', () => { - const requestId = '1598961763272766'; - it(setRequestIdProperly('rpc'), () => { - const spy = spyFactory(rpc, 'setRequestId'); - const ctxMock = createMock({ - getType: () => 'rpc', - }); - delegate.setRequestId(ctxMock, requestId); - expect(spy).toBeCalledWith(ctxMock, requestId); - }); - it(setRequestIdProperly('http'), () => { - const spy = spyFactory(http, 'setRequestId'); - const ctxMock = createMock(httpContext as Partial); - delegate.setRequestId(ctxMock, requestId); - expect(spy).toBeCalledWith(ctxMock, requestId); - }); - it(setRequestIdProperly('ws'), () => { - const spy = spyFactory(ws, 'setRequestId'); - const ctxMock = createMock({ - getType: () => 'ws', - }); - delegate.setRequestId(ctxMock, requestId); - expect(spy).toBeCalledWith(ctxMock, requestId); - }); - it(setRequestIdProperly('gql'), () => { - const spy = spyFactory(gql, 'setRequestId'); - const ctxMock = createMock({ - getType: () => 'graphql', - }); - delegate.setRequestId(ctxMock, requestId); - expect(spy).toBeCalledWith(ctxMock, requestId); - }); - }); - describe('useJsonFormat', () => { - it('should return as JSON instead of string', () => { - const spy = spyFactory(http, 'getSuccessContext').mockReturnValueOnce(parserReturn); - const ctxMock = createMock(httpContext as Partial); - expect( - delegate.getContextSuccessString('data', ctxMock, startTime, { - json: true, - color: false, - }), - ).toEqual({ log: parserReturn, meta: undefined }); - expect(spy).toBeCalledWith( - Buffer.from(JSON.stringify('data')).byteLength, - ctxMock, - startTime, - { - json: true, - color: false, - }, - ); - }); - }); - describe('no data', () => { - it('should replace no data with an empty string', () => { - const spy = spyFactory(http, 'getSuccessContext').mockReturnValueOnce({ - callerAddress: '127.0.0.1', - callPoint: '/', - contentLength: 0, - responseTime: 2, - method: 'GET', - protocol: 'HTTP/1.1', - status: '200', - }); - const ctxMock = createMock(httpContext as Partial); - expect(delegate.getContextSuccessString(null, ctxMock, startTime, options)).toEqual({ - log: '127.0.0.1 - GET / HTTP/1.1 200 2ms - 0', - meta: undefined, - }); - expect(spy).toBeCalledWith(0, ctxMock, startTime, options); - }); + ok(parseSpy.calledWith(Buffer.from(JSON.stringify('data')).byteLength, fullCtxMock, 0, options)); +}); +DelegatorServiceSuite('It should replace no data with an empty string', ({ delegate, parsers }) => { + const fullCtxMock = executionMockFactory({ getType: () => 'http' }); + const parseSpy = stubMethod(parsers.http, 'getSuccessContext'); + parseSpy.returns({ ...parserReturn, contentLength: 0 }); + const options = { json: false, color: false }; + equal(delegate.getContextSuccessString(null, fullCtxMock, 0, options), { + log: '127.0.0.1 - GET / HTTP/1.1 200 2ms - 0', + meta: undefined, }); + ok(parseSpy.calledWith(0, fullCtxMock, 0, options)); }); + +DelegatorServiceSuite.run();