const { PacketType, Decoder, Encoder, isPacketValid } = require("..");
const expect = require("expect.js");
const helpers = require("./helpers.js");

describe("socket.io-parser", () => {
  it("exposes types", () => {
    expect(PacketType.CONNECT).to.be.a("number");
    expect(PacketType.DISCONNECT).to.be.a("number");
    expect(PacketType.EVENT).to.be.a("number");
    expect(PacketType.ACK).to.be.a("number");
    expect(PacketType.CONNECT_ERROR).to.be.a("number");
    expect(PacketType.BINARY_EVENT).to.be.a("number");
    expect(PacketType.BINARY_ACK).to.be.a("number");
  });

  it("encodes connection", () => {
    return helpers.test({
      type: PacketType.CONNECT,
      nsp: "/woot",
      data: {
        token: "123",
      },
    });
  });

  it("encodes disconnection", () => {
    return helpers.test({
      type: PacketType.DISCONNECT,
      nsp: "/woot",
    });
  });

  it("encodes an event", () => {
    return helpers.test({
      type: PacketType.EVENT,
      data: ["a", 1, {}],
      nsp: "/",
    });
  });

  it("encodes an event (with an integer as event name)", () => {
    return helpers.test({
      type: PacketType.EVENT,
      data: [1, "a", {}],
      nsp: "/",
    });
  });

  it("encodes an event (with ack)", () => {
    return helpers.test({
      type: PacketType.EVENT,
      data: ["a", 1, {}],
      id: 1,
      nsp: "/test",
    });
  });

  it("encodes an ack", () => {
    return helpers.test({
      type: PacketType.ACK,
      data: ["a", 1, {}],
      id: 123,
      nsp: "/",
    });
  });

  it("encodes an connect error", () => {
    return helpers.test({
      type: PacketType.CONNECT_ERROR,
      data: "Unauthorized",
      nsp: "/",
    });
  });

  it("encodes an connect error (with object)", () => {
    return helpers.test({
      type: PacketType.CONNECT_ERROR,
      data: {
        message: "Unauthorized",
      },
      nsp: "/",
    });
  });

  it("throws an error when encoding circular objects", () => {
    const a = {};
    a.b = a;

    const data = {
      type: PacketType.EVENT,
      data: a,
      id: 1,
      nsp: "/",
    };

    const encoder = new Encoder();

    expect(() => encoder.encode(data)).to.throwException();
  });

  it("decodes a bad binary packet", () => {
    try {
      const decoder = new Decoder();
      decoder.add("5");
    } catch (e) {
      expect(e.message).to.match(/Illegal/);
    }
  });

  it("throw an error upon parsing error", () => {
    const isInvalidPayload = (str) =>
      expect(() => new Decoder().add(str)).to.throwException(
        /^invalid payload$/,
      );

    isInvalidPayload('442["some","data"');
    isInvalidPayload('0/admin,"invalid"');
    isInvalidPayload("0[]");
    isInvalidPayload("1/admin,{}");
    isInvalidPayload('2/admin,"invalid');
    isInvalidPayload("2/admin,{}");
    isInvalidPayload('2[{"toString":"foo"}]');
    isInvalidPayload('2[true,"foo"]');
    isInvalidPayload('2[null,"bar"]');
    isInvalidPayload('2["connect"]');
    isInvalidPayload('2["disconnect","123"]');

    expect(() => new Decoder().add("999")).to.throwException(
      /^unknown packet type 9$/,
    );

    expect(() => new Decoder().add(999)).to.throwException(
      /^Unknown type: 999$/,
    );
  });

  it("should resume decoding after calling destroy()", () => {
    return new Promise((resolve) => {
      const decoder = new Decoder();

      decoder.on("decoded", (packet) => {
        expect(packet.data).to.eql(["hello"]);
        resolve();
      });

      decoder.add('51-["hello"]');
      decoder.destroy();
      decoder.add('2["hello"]');
    });
  });

  it("should ensure that a packet is valid", () => {
    expect(
      isPacketValid({
        type: 0,
        nsp: "/",
      }),
    ).to.eql(true);

    expect(
      isPacketValid({
        type: 0,
        nsp: "/admin",
        data: "invalid",
      }),
    ).to.eql(false);

    expect(
      isPacketValid({
        type: 0,
        nsp: "/",
        data: [],
      }),
    ).to.eql(false);

    expect(
      isPacketValid({
        type: 1,
        nsp: "/admin",
        data: {},
      }),
    ).to.eql(false);

    expect(
      isPacketValid({
        type: 2,
        nsp: "/admin",
        data: "invalid",
      }),
    ).to.eql(false);

    expect(
      isPacketValid({
        type: 2,
        nsp: "/admin",
        data: {},
      }),
    ).to.eql(false);

    expect(
      isPacketValid({
        type: 2,
        nsp: "/",
        data: { toString: "foo" },
      }),
    ).to.eql(false);

    expect(
      isPacketValid({
        type: 2,
        nsp: "/",
        data: [true, "foo"],
      }),
    ).to.eql(false);

    expect(
      isPacketValid({
        type: 2,
        nsp: "/",
        data: [null, "bar"],
      }),
    ).to.eql(false);

    expect(
      isPacketValid({
        type: 2,
        nsp: "/",
        data: ["connect"],
      }),
    ).to.eql(false);

    expect(
      isPacketValid({
        type: 2,
        nsp: "/",
        data: ["disconnect", "123"],
      }),
    ).to.eql(false);
  });
});
