'use strict';

const Mongos = require('../../../../lib/topologies/mongos');
const expect = require('chai').expect;
const mock = require('mongodb-mock-server');
const genClusterTime = require('../common').genClusterTime;

const Connection = require('../../../../lib/connection/connection');

describe('Reconnect (Mongos)', function() {
  const fixture = {};

  function startServer() {
    return mock.createServer(fixture.port).then(mockServer => {
      mockServer.setMessageHandler(request => {
        request.reply(
          Object.assign({}, mock.DEFAULT_ISMASTER, {
            $clusterTime: genClusterTime(Date.now()),
            msg: 'isdbgrid'
          })
        );
      });
      fixture.server = mockServer;
      fixture.port = mockServer.port;
    });
  }

  function stopServer() {
    return mock.cleanup();
  }

  beforeEach(() => startServer());
  beforeEach(() => Connection.enableConnectionAccounting());
  afterEach(() => Connection.disableConnectionAccounting());
  afterEach(() => stopServer());

  it('should not connection swarm when reconnecting', function(done) {
    const reconnectInterval = 500;
    const socketTimeout = reconnectInterval * 5;
    const haInterval = reconnectInterval * 10;
    const reconnectTries = Number.MAX_VALUE;

    const connectOptions = {
      haInterval,
      reconnectInterval,
      socketTimeout,
      reconnectTries,
      reconnect: true,
      poolSize: 500
    };

    const mongos = new Mongos([fixture.server.address()], connectOptions);

    function runIsMaster(assertion) {
      return new Promise((resolve, reject) => {
        mongos.command('admin.$cmd', { ismaster: 1 }, {}, (err, response) => {
          try {
            assertion(err, response);
            resolve();
          } catch (e) {
            reject(e);
          }
        });
      });
    }

    function connectMongos() {
      return new Promise((resolve, reject) => {
        mongos.once('error', reject);
        mongos.once('connect', resolve);
        mongos.connect(connectOptions);
      });
    }

    function assertSuccess(err, response) {
      expect(err).to.not.exist;
      expect(response).to.exist;
    }

    function assertError(err, response) {
      expect(err).to.exist;
      expect(response).to.not.exist;
    }

    function delay(ms) {
      return new Promise(resolve => setTimeout(resolve, ms));
    }

    function cleanup(err) {
      mongos.destroy();
      return err;
    }

    Promise.resolve()
      .then(() => connectMongos())
      .then(() => runIsMaster(assertSuccess))
      .then(() => stopServer())
      .then(() => runIsMaster(assertError))
      .then(() => delay(haInterval * 2))
      .then(() => startServer())
      .then(() => delay(haInterval * 2))
      .then(() => expect(Object.keys(Connection.connections())).to.have.a.lengthOf(1))
      .then(() => cleanup(), cleanup)
      .then(done);
  });
});

@@ -282,16 +282,88 @@
 ction');
+%0Aconst ConnectionSpy = require('../../functional/shared').ConnectionSpy;
 %0A%0Adescri
@@ -442,32 +442,128 @@
 startServer() %7B%0A
+    fixture.spy = new ConnectionSpy();%0A    Connection.enableConnectionAccounting(fixture.spy);%0A%0A
     return mock.
@@ -958,24 +958,70 @@
 pServer() %7B%0A
+    Connection.disableConnectionAccounting();%0A
     return m
@@ -1079,130 +1079,8 @@
 ));%0A
-  beforeEach(() =%3E Connection.enableConnectionAccounting());%0A  afterEach(() =%3E Connection.disableConnectionAccounting());%0A
   af
@@ -2820,116 +2820,436 @@
  =%3E 
-delay(haInterval * 2))%0A      .then(
+%7B%0A        return new Promise((resolve, reject) =%3E %7B%0A          const timeoutTimer = setTimeout(%0A            
 () =%3E 
-exp
+rej
 ect(
-Object.keys(Connection.connections())).to.have.a.lengthOf(1)
+new Error('timed out waiting for connection count')),%0A            5000%0A          );%0A%0A          fixture.spy.on('connectionRemoved', () =%3E %7B%0A            if (fixture.spy.connectionCount() === 1) %7B%0A              clearTimeout(timeoutTimer);%0A              resolve();%0A            %7D%0A          %7D);%0A        %7D);%0A      %7D
 )%0A  

use connection spy for all connection spying