text: const { test } = require("tap");
  const sandbox = require("@log4js-node/sandboxed-module");
  const util = require("util");
  const recording = require("../../lib/appenders/recording");
  
  test("log4js", batch => {
    batch.test("shutdown should return appenders and categories back to initial state", t => {
      const stringifyMap = (map) => JSON.stringify(Array.from(map));
      const deepCopyMap = (map) => new Map(JSON.parse(stringifyMap(map)));
  
      const log4js = require("../../lib/log4js");
  
      const appenders = require("../../lib/appenders");
      const categories = require("../../lib/categories");
      const initialAppenders = deepCopyMap(appenders);
      const initialCategories = deepCopyMap(categories);
  
      log4js.configure({
        appenders: { recorder: { type: "recording" } },
        categories: { default: { appenders: ["recorder"], level: "DEBUG" } }
      });
  
      const configuredAppenders = deepCopyMap(appenders);
      const configuredCategories = deepCopyMap(categories);
      t.notEqual(
        stringifyMap(configuredAppenders), 
        stringifyMap(initialAppenders), 
        "appenders should be different from initial state"
      );
      t.notEqual(
        stringifyMap(configuredCategories), 
        stringifyMap(initialCategories), 
        "categories should be different from initial state"
      );
  
      log4js.shutdown(() => {
        const finalAppenders = deepCopyMap(appenders);
        const finalCategories = deepCopyMap(categories);
        t.equal(
          stringifyMap(finalAppenders),
          stringifyMap(initialAppenders), 
          "appenders should revert back to initial state"
        );
        t.equal(
          stringifyMap(finalCategories), 
          stringifyMap(initialCategories), 
          "categories should revert back to initial state"
        );
        t.end();
      });
    });
  
    batch.test("getLogger", t => {
      const log4js = require("../../lib/log4js");
      log4js.configure({
        appenders: { recorder: { type: "recording" } },
        categories: { default: { appenders: ["recorder"], level: "DEBUG" } }
      });
      const logger = log4js.getLogger("tests");
  
      t.test("should take a category and return a logger", assert => {
        assert.equal(logger.category, "tests");
        assert.equal(logger.level.toString(), "DEBUG");
        assert.type(logger.debug, "function");
        assert.type(logger.info, "function");
        assert.type(logger.warn, "function");
        assert.type(logger.error, "function");
        assert.type(logger.fatal, "function");
        assert.end();
      });
  
      t.test("log events", assert => {
        recording.reset();
  
        logger.debug("Debug event");
        logger.trace("Trace event 1");
        logger.trace("Trace event 2");
        logger.warn("Warning event");
        logger.error("Aargh!", new Error("Pants are on fire!"));
        logger.error("Simulated CouchDB problem", {
          err: 127,
          cause: "incendiary underwear"
        });
  
        const events = recording.replay();
  
        assert.equal(events[0].level.toString(), "DEBUG");
        assert.equal(events[0].data[0], "Debug event");
        assert.type(events[0].startTime, "Date");
  
        assert.equal(events.length, 4, "should not emit events of a lower level");
        assert.equal(events[1].level.toString(), "WARN");
  
        assert.type(
          events[2].data[1],
          "Error",
          "should include the error if passed in"
        );
        assert.equal(events[2].data[1].message, "Pants are on fire!");
        assert.end();
      });
  
      t.end();
    });
  
    batch.test("when shutdown is called", t => {
      const events = {
        shutdownCalled: []
      };
  
      const log4js = sandbox.require("../../lib/log4js", {
        requires: {
          "./appenders/file": {
            name: "file",
            configure() {
              function thing(evt) {
                events.event = evt;
                return null;
              }
  
              thing.shutdown = function(cb) {
                events.shutdownCalled.push(true);
                cb();
              };
              return thing;
            }
          }
        }
      });
  
      const config = {
        appenders: {
          file: {
            type: "file",
            filename: "cheesy-wotsits.log",
            maxLogSize: 1024,
            backups: 3
          },
          alsoFile: {
            type: "file"
          }
        },
        categories: {
          default: { appenders: ["file", "alsoFile"], level: "DEBUG" }
        }
      };
  
      log4js.configure(config);
      const logger = log4js.getLogger();
      log4js.shutdown(() => {
        t.equal(
          events.shutdownCalled.length,
          2,
          "should invoke appender shutdowns"
        );
        logger.info("this should not go to the appenders");
        t.notOk(events.event);
        t.end();
      });
    });
  
    batch.test("configuration when passed as filename", t => {
      let appenderConfig;
      let configFilename;
  
      const log4js = sandbox.require("../../lib/log4js", {
        ignoreMissing: true,
        requires: {
          fs: {
            statSync() {
              return { mtime: Date.now() };
            },
            readFileSync(filename) {
              configFilename = filename;
              return JSON.stringify({
                appenders: {
                  file: {
                    type: "file",
                    filename: "whatever.log"
                  }
                },
                categories: { default: { appenders: ["file"], level: "DEBUG" } }
              });
            },
            readdirSync() {
              return ["file"];
            }
          },
          "./file": {
            configure(configuration) {
              appenderConfig = configuration;
              return function() {};
            }
          }
        }
      });
  
      log4js.configure("/path/to/cheese.json");
      t.equal(
        configFilename,
        "/path/to/cheese.json",
        "should read the config from a file"
      );
      t.equal(
        appenderConfig.filename,
        "whatever.log",
        "should pass config to appender"
      );
      t.end();
    });
  
    batch.test("with configure not called", t => {
      const fakeStdoutAppender = {
        configure() {
          this.required = true;
          return function(evt) {
            fakeStdoutAppender.evt = evt;
          };
        }
      };
  
      const log4js = sandbox.require("../../lib/log4js", {
        requires: {
          "./appenders/stdout": fakeStdoutAppender
        }
      });
  
      const logger = log4js.getLogger("some-logger");
      logger.debug("This is a test");
      t.ok(fakeStdoutAppender.required, "stdout should be required");
      t.notOk(fakeStdoutAppender.evt, "should not log anything");
      t.end();
    });
  
    batch.test("with configure called with empty values", t => {
      [null, undefined, "", " ", []].forEach(config => {
        const log4js = require("../../lib/log4js");
        const expectedError = `Problem reading config from file "${util.inspect(
          config
        )}". Error was ENOENT: no such file or directory`;
        t.throws(() => log4js.configure(config), expectedError);
      });
  
      t.end();
    });
  
    batch.test("configuration persistence", t => {
      const firstLog4js = require("../../lib/log4js");
      firstLog4js.configure({
        appenders: { recorder: { type: "recording" } },
        categories: { default: { appenders: ["recorder"], level: "DEBUG" } }
      });
      recording.reset();
  
      const secondLog4js = require("../../lib/log4js");
      secondLog4js
        .getLogger()
        .info("This should go to the appender defined in firstLog4js");
  
      t.equal(
        recording.replay()[0].data[0],
        "This should go to the appender defined in firstLog4js"
      );
      t.end();
    });
  
    batch.end();
  });
  