const { test } = require('tap');
const cluster = require('cluster');
const log4js = require('../../lib/log4js');
const recorder = require('../../lib/appenders/recording');

log4js.configure({
  appenders: {
    vcr: { type: 'recording' },
  },
  categories: { default: { appenders: ['vcr'], level: 'debug' } },
});

if (cluster.isMaster) {
  cluster.fork();

  const masterLogger = log4js.getLogger('master');
  const masterPid = process.pid;
  masterLogger.info('this is master');

  let workerLevel;
  cluster.on('message', (worker, message) => {
    if (worker.type || worker.topic) {
      message = worker;
    }
    if (message.type && message.type === '::testing') {
      workerLevel = message.level;
    }
  });

  cluster.on('exit', (worker) => {
    const workerPid = worker.process.pid;
    const logEvents = recorder.replay();

    test('cluster master', (batch) => {
      batch.test('events should be logged', (t) => {
        t.equal(logEvents.length, 3);

        t.equal(logEvents[0].categoryName, 'master');
        t.equal(logEvents[0].pid, masterPid);

        t.equal(logEvents[1].categoryName, 'worker');
        t.equal(logEvents[1].pid, workerPid);
        // serialising errors with stacks intact
        t.type(logEvents[1].data[1], 'Error');
        t.match(logEvents[1].data[1].stack, 'Error: oh dear');
        // serialising circular references in objects
        t.type(logEvents[1].data[2], 'object');
        t.type(logEvents[1].data[2].me, 'object');
        // serialising errors with custom properties
        t.type(logEvents[1].data[3], 'Error');
        t.match(logEvents[1].data[3].stack, 'Error: wtf');
        t.equal(logEvents[1].data[3].alert, 'chartreuse');
        // serialising things that are not errors, but look a bit like them
        t.type(logEvents[1].data[4], 'object');
        t.equal(logEvents[1].data[4].stack, 'this is not a stack trace');

        t.equal(logEvents[2].categoryName, 'log4js');
        t.equal(logEvents[2].level.toString(), 'ERROR');
        t.equal(logEvents[2].data[0], 'Unable to parse log:');

        t.end();
      });

      batch.end();
    });

    test('cluster worker', (batch) => {
      batch.test('logger should get correct config', (t) => {
        t.equal(workerLevel, 'DEBUG');
        t.end();
      });
      batch.end();
    });
  });
} else {
  const workerLogger = log4js.getLogger('worker');
  // test for serialising circular references
  const circle = {};
  circle.me = circle;
  // test for serialising errors with their own properties
  const someError = new Error('wtf');
  someError.alert = 'chartreuse';
  // test for serialising things that look like errors but aren't.
  const notAnError = { stack: 'this is not a stack trace' };
  workerLogger.info(
    'this is worker',
    new Error('oh dear'),
    circle,
    someError,
    notAnError
  );
  // can't run the test in the worker, things get weird
  process.send({
    type: '::testing',
    level: workerLogger.level.toString(),
  });
  // test sending a badly-formed log message
  process.send({ topic: 'log4js:message', data: { cheese: 'gouda' } });
  cluster.worker.disconnect();
}
