Jest - макет и тестирование многопотоков pino на основе уровней логов - PullRequest
0 голосов
/ 17 апреля 2020

Я изо всех сил пытаюсь найти правильный способ насмешки и использования pino в сервисе регистрации тестов, так что вот моя реализация регистратора pino. Это запись в разные файловые потоки на основе уровней журнала.

 getChildLoggerService(fileNameString): pino.Logger {
    const streams: Streams = [
      { level: 'fatal', stream: fs.createWriteStream(path.join(process.cwd(), './logs/database-connect-fatal.log'))},
      { level: 'error', stream: fs.createWriteStream(path.join(process.cwd(), './logs/database-connect-error.log'))},
      { level: 'debug', stream: fs.createWriteStream(path.join(process.cwd(), './logs/database-connect-debug.log'))},
      { level: 'info', stream: fs.createWriteStream(path.join(process.cwd(), './logs/database-connect-info.log'))},
    ];

    return  pino({useLevelLabels: true,
      base: {
        hostName: os.hostname(),
        platform: os.platform(),
        processId: process.pid,
        timestamp: this.appUtilService.getCurrentLocaleTimeZone(),
        // tslint:disable-next-line: object-literal-sort-keys
        fileName: this.appUtilService.getFileName(fileNameString),
      } ,
      level: this.appUtilService.getLogLevel(),
      messageKey: LOGGER_MSG_KEY,
      prettyPrint: this.appUtilService.checkForDevEnv(process.env.NODE_ENV),
      timestamp: () => {
        return this.appUtilService.getCurrentLocaleTimeZone()
      },

    }, multistream(streams)).child({
      connectorReqId: (process.env.REQ_APP_NAME === null ? 'local': process.env.REQ_APP_NAME)
        +uuid.v4().toString()
    });
  }

Самая важная часть, которую я хотел проверить, - это многопотоковые операции, в которых мне нужно записывать в разные файлы журнала на основе уровней журнала, и пока я не мог не могу придумать, как это сделать

import pino, { DestinationStream } from 'pino';
const sinon = require('sinon');
import pinoms from 'pino-multi-stream';
const fs = require('fs');
const path = require('path');
const stream = require('stream');
const { PassThrough } = require('stream');
class EchoStream extends stream.Writable {
    _write(chunk, enc, next) {
        console.log('ssdsdsd',chunk.toString());
        next();
    }
}
import * as _ from 'lodash';
import { Writable } from 'stream';
import { mocked } from 'ts-jest/utils';
import { LogServiceInstance } from './log.service';
// jest.mock('pino', () => jest.fn().mockImplementation(() => { ====> Tried this inline mock, doesnt work
//  return {
//      child: jest.fn().mockReturnValue(jest.requireActual('pino').Logger)
//  }
// }));
// jest.mock('pino', () => {
//  return jest.fn().mockImplementation(() => {
//      return {
//          child: jest.fn().mockReturnValue(jest.requireActual('pino').Logger),
//          stream: jest.fn().mockImplementation(() => {
//              return [
//                  {
//                      level: 'info',
//                      stream: fs.createWriteStream(
//                          path.resolve(process.cwd(), '/test/database-connector-logs/info.log')
//                      ),
//                  },
//                  {
//                      level: 'warn',
//                      stream: fs.createWriteStream(
//                          path.resolve(process.cwd(), '/test/database-connector-logs/warn.log')
//                      ),
//                  },
//              ];
//          }),
//      };
//  });
// });



describe('Test suite for Log service', () => {
    //const mockedPino = mocked(pino, true);
    test('Test case for getLoggerInstance', () => {
        const mockedPinoMsStream = [

        const mockedPinoStream = (pino.prototype.stream = jest.fn(() => mockedPinoMsStream));

        console.dir(pino);
        const prop = Reflect.ownKeys(pino).find((s) => {
            return s === 'symbols';
        });
    // Tried this but it did not work as the actual files are written with the values
        pino[prop]['streamSym'] = jest.fn().mockImplementation(() => {
            return fs.createWriteStream(path.resolve(process.cwd(), './test/database-connector-logs/info.log'))
        });
        console.dir(pino);
        const log = LogServiceInstance.getChildLoggerService(__filename);

        console.dir(Object.getPrototypeOf(log));
        log.info('test logging');
        expect(2).toEqual(2);

});

Может кто-нибудь сообщить мне, где издевательства не так и как правильно их издеваться

ОБНОВЛЕНИЕ:

Я понял, что насмешливый pino-multi-stream может помочь, поэтому попробовал это так. Это было добавлено в самом верху, и все остальные макеты были удалены (даже внутри набора тестов)

const mockedPinoMultiStream  = {
    stream: jest.fn().mockImplementation(() => {
        return {write: jest.fn().mockReturnValue(new PassThrough())}
    })
}

jest.mock('pino-multi-stream', () => {
    return {
        multistream: jest.fn().mockReturnValue(mockedPinoMultiStream)
    }
});

хотел протестировать, если на основе уровня используются соответствующие именованные файлы , но это также приводит к исключению

TypeError: stream.write не является функцией

at Pino.write (/XXX/node_modules/pino/lib/proto.js:161:15)
at Pino.LOG (/XXXX/node_modules/pino/lib/tools.js:39:26)

ПОСЛЕДНИЕ ОБНОВЛЕНИЯ:

Поэтому я разрешил исключение, изменив способ pino многопотоковый макет

const { PassThrough } = require('stream');
...
...
const mockedPinoMultiStream  = {
  write: jest.fn().mockImplementation((data) => {
   return new Passthrough();
  })
};

Теперь исключений больше нет, а write (метод) корректно корректируется, когда я печатаю "pino". Но я не понимаю, как проверить разные файлы на основе разных уровней журнала. Может ли кто-нибудь дать мне знать, как это сделать? Примечание: я попытался установить возвращаемое значение fs.createWriteStream вместо Passthrough, но это не сработало

1 Ответ

0 голосов
/ 18 апреля 2020

Atlast, я нашел ответ на использование потоков pino, основанных на разных уровнях журнала. Я пошел дальше и создал тестовый каталог для размещения файлов тестового журнала. В действительности, мы не хотим, чтобы pino фальсифицировал реальные файлы журналов. Поэтому я решила насмехаться над потоками пино во время начала шутливого теста. Этот файл запускается в первую очередь до запуска любого набора тестов. Поэтому я изменил конфигурацию jest в пакете. json как

"setupFiles": [
      "<rootDir>/jest-setup/stream.logger.js"
    ],

в файле stream.logger. js, я добавил

const pinoms = require('pino-multi-stream');
const fs = require('fs');
const path = require('path');
const stream = require('stream');
const Writable = require('stream').Writable;
const { PassThrough } = require('stream');
const pino = require('pino');
class MyWritable extends Writable {
  constructor(options) {
    super(options);
  }

  _write(chunk, encoding, callback) {
    const writeStream =fs.createWriteStream(path.resolve(process.cwd(), './test/logs/info.log'));
    writeStream.write(chunk,'utf-8');
    writeStream.emit('close');
    writeStream.end();
  }
}
const mockedPinoMultiStream  = {
  write: jest.fn().mockImplementation((data) => {
    const writeStream = new MyWritable();
    return writeStream._write(data);
  })
};
jest.mock('pino-multi-stream', () => {
    return {
        multistream: jest.fn().mockReturnValue(mockedPinoMultiStream)
    }
});

Теперь я продолжил и создал файл теста - log.service.spe c .ts

import * as pino from 'pino';
const sinon = require('sinon');
import pinoms from 'pino-multi-stream';
const fs = require('fs');
const path = require('path');
const stream = require('stream');


import * as _ from 'lodash';
import { Writable } from 'stream';
import { mocked } from 'ts-jest/utils';
import { LogServiceInstance } from './log.service';

describe('Test suite for Log service', () => {
    //const mockedPino = mocked(pino, true);
    afterEach(() => {
        // delete the contents of the log files after each test suite
        fs.truncate((path.resolve(process.cwd(), './test/logs/info.log')), 0, () => {
            console.dir('Info log file deleted');
        });

        fs.truncate((path.resolve(process.cwd(), './test/logs/warn.log')), 0, () => {
            console.dir('Warn log file deleted');
        });
        fs.truncate((path.resolve(process.cwd(), './test/logs/debug.log')), 0, () => {
            console.dir('Debug log file deleted');
        });
    });
    test('Test case fir getLoggerInstance', () => {
        const pinoLoggerInstance = LogServiceInstance.getChildLoggerService(__filename);
        pinoLoggerInstance.info('test logging');

        _.map(Object.getOwnPropertySymbols(pinoLoggerInstance), (mapItems:any) => {
            if(mapItems.toString().includes('Symbol')) {
                if(mapItems.toString().includes('pino.level')) {
                    expect(pinoLoggerInstance[mapItems]).toEqual(20);
                }
            }
            if(mapItems.toString().includes('pino.chindings')) {
                const childInstance = pinoLoggerInstance[mapItems].toString().substr(1);
                const jsonString  = '{'+ childInstance+ '}';
                const expectedObj = Object.create(JSON.parse(jsonString));
                expect(expectedObj.fileName).toEqual('log.service.spec');
                expect(expectedObj.appName).toEqual('AppJestTesting');
                expect(expectedObj.connectorReqId).toEqual(expect.objectContaining(new String('AppJestTesting')));
            }
        });
        // make sure the info.log file is written in this case
        const infoBuffRead = fs.createReadStream(path.resolve(process.cwd(), './test/logs/info.log')).read(1024);
        expect(infoBuffRead).toBeDefined();

        // now write a warn log
        pinoLoggerInstance.warn('test warning log');
        const warnBuffRead = fs.createReadStream(path.resolve(process.cwd(), './test/logs/warn.log')).read(1024);
        expect(warnBuffRead).toBeDefined();


        // now write a debug log
        pinoLoggerInstance.debug('test warning log');
        const debugBuffRead = fs.createReadStream(path.resolve(process.cwd(), './test/logs/warn.log')).read(1024);
        expect(debugBuffRead).toBeDefined();


    });
});

Я также позаботился о том, чтобы файлы журнала теста со временем не перегружались данными, удаляя их содержимое после каждого выполнения. помогает людям, пытающимся протестировать мультипоток pino

...