import connection from '../networking/connection';
import packet from '../networking/packet';
import Socket from 'net';

test(`connect fails when error occurs during createConnection`, async () => {
  const mock_create = (port, host, cb) => {
    cb(new Error('mock create error'));
  };
  jest.spyOn(Socket, 'createConnection').mockImplementation(mock_create);

  const conn = new connection('localhost', 20000);
  await expect(conn.connect()).rejects.toThrow(Error);
});

test(`socket receive data fails when decryption fails`, async () => {
  let cbl = {};
  const socket = {
    on: (name, cb) => {
      cbl[name] = cb;
    },
  };

  const conn = new connection('', 0, 'b', socket);
  let reject;
  const mock_reject = jest.fn().mockImplementation((e) => reject(e));
  conn.reject = mock_reject;
  conn.resolve = jest.fn();

  const p = new packet('a');
  await p.encrypt();
  p.encode_top_ui32(p.buffer.length);
  await expect(
    new Promise((_, r) => {
      reject = r;
      cbl['data'](Buffer.from(p.buffer));
    })
  ).rejects.toThrow(Error);
  expect(mock_reject.mock.calls.length).toBe(1);
});

test(`disconnect succeeds if an error is thrown`, async () => {
  const socket = {
    destroy: () => {
      throw new Error('mock destroy error');
    },
    on: () => {},
  };

  const conn = new connection('', 0, 'b', socket);
  await conn.disconnect();
});

test(`send fails on socket error`, async () => {
  let cbl = {};
  const socket = {
    on: (name, cb) => {
      cbl[name] = cb;
    },
  };

  const conn = new connection('', 0, 'b', socket);
  const mock_reject = jest.fn();
  conn.reject = mock_reject;
  conn.resolve = jest.fn();

  cbl['error']('socket error');
  expect(mock_reject).toBeCalled();
});

test(`error is thrown when socket is closed`, async () => {
  let cbl = {};
  const socket = {
    on: (name, cb) => {
      cbl[name] = cb;
    },
  };

  const conn = new connection('', 0, 'b', socket);
  const mock_reject = jest.fn();
  conn.reject = mock_reject;
  conn.resolve = jest.fn();

  cbl['close']();
  expect(mock_reject).toBeCalled();
});

test(`send fails when write error occurs`, async () => {
  let cbl = {};
  const socket = {
    on: (name, cb) => {
      cbl[name] = cb;
    },
    write: (b, c, cb) => {
      cb('mock write error');
    },
  };

  const conn = new connection('', 0, 'b', socket);
  try {
    await conn.send('c', new packet('b'));
    expect('send should fail').toBeNull();
  } catch (err) {
    expect(err).toBeDefined();
  }
});
