// jshint -W030

'use strict';

var MockWS        = require('./support/mock-ws');
var ShareJSStream = require('..');
var sinon         = require('sinon');

require('should');

describe('ShareJSStream', function() {
  var ws, stream, errors, messages;

  beforeEach(function() {
    sinon.spy(global, 'setInterval');

    messages = [];
    errors   = [];
    ws       = new MockWS();
    stream   = new ShareJSStream(ws);

    stream.on('data', function onData(data) {
      messages.push(data);
    });

    stream.on('error', function onError(err) {
      errors.push(err);
    });
  });

  afterEach(function() {
    global.setInterval.restore();
  });

  describe('::constructor', function() {
    it('sets the headers from the ws headers', function() {
      stream.headers.should.exactly(ws.upgradeReq.headers);
    });

    it('sets the remoteAddress from the ws connection', function() {
      var addr = ws.upgradeReq.connection.remoteAddress;
      stream.remoteAddress.should.exactly(addr);
    });

    it('is a Duplex stream', function() {
      stream._write.should.be.type('function');
      stream._read.should.be.type('function');
    });

    it('defaults to a 30s keep alive', function() {
      global.setInterval.calledOnce.should.be.true;
      global.setInterval.args[0].should.eql([stream.keepAlive, 30 * 1000]);
    });

    it('accepts a keep alive option', function() {
      stream = new ShareJSStream(ws, { keepAlive: 10 * 1000 });
      global.setInterval.args[1].should.eql([stream.keepAlive, 10 * 1000]);
    });

    it('accepts a no keep alive option', function() {
      stream = new ShareJSStream(ws, { keepAlive: false });
      global.setInterval.calledOnce.should.be.true;
    });
  });

  describe('keep alive', function() {
    it('sends `null` to the ws', function(done) {
      sinon.spy(ws, 'send');
      stream = new ShareJSStream(ws, { keepAlive: 6 });

      setTimeout(function() {
        ws.send.calledOnce.should.be.true;
        ws.send.args[0].should.eql([null]);
        done();
      }, 10);
    });
  });

  describe('on ws close', function() {
    beforeEach(function() {
      sinon.spy(global, 'clearInterval');
    });

    afterEach(function() {
      global.clearInterval.restore();
    });

    it('clears the keepAlive interval', function() {
      ws.emit('close');
      global.clearInterval.calledOnce.should.be.true;
      global.clearInterval.args[0].should.eql([stream.keepAliveInterval]);
    });

    it('closes the stream', function (done) {
      stream.on('close', function() {
        done();
      });

      ws.emit('close');
    });
  });

  describe('when receiving a valid ws message', function() {
    beforeEach(function() {
      ws.emit('message', '{ "foo": "bar" }');
    });

    it('emits the message', function() {
      messages.should.eql([{ foo: 'bar' }]);
    });
  });

  describe('when receiving an invalid ws message (bad JSON)', function() {
    beforeEach(function() {
      sinon.spy(ws, 'close');
      ws.emit('message', 'Not JSON');
    });

    it('does not emit the message', function() {
      messages.should.eql([]);
    });

    it('emits an error', function() {
      errors.length.should.eql(1);
      errors[0].message.should.eql('Client sent invalid JSON');
    });

    it('closes the ws', function() {
      ws.close.calledOnce.should.be.true;
      ws.close.args[0].should.eql([1008, 'Client sent invalid JSON']);
    });
  });

  describe('when the stream ends', function() {
    beforeEach(function() {
      sinon.spy(ws, 'close');
      stream.push(null);
    });

    it('closes the ws normally', function(done) {
      stream.on('end', function() {
        ws.close.calledOnce.should.be.true;
        ws.close.args[0].should.eql([1000]);
        done();
      });
    });
  });

  describe('when there is a stream error', function() {
    beforeEach(function() {
      sinon.spy(ws, 'close');
      stream.emit('error', new Error('Error message'));
    });

    it('closes the ws', function() {
      ws.close.calledOnce.should.be.true;
      ws.close.args[0].should.eql([1008, 'Error message']);
    });
  });
});

@@ -412,19 +412,41 @@
 tream(ws
+, %7B keepAlive: false %7D
 );%0A
-
 %0A    str
@@ -1216,32 +1216,70 @@
 ', function() %7B%0A
+      stream = new ShareJSStream(ws);%0A
       global.set
@@ -1510,24 +1510,76 @@
  * 1000 %7D);%0A
+      global.setInterval.calledOnce.should.be.true;%0A
       global
@@ -1596,17 +1596,17 @@
 al.args%5B
-1
+0
 %5D.should
@@ -1788,36 +1788,32 @@
 tInterval.called
-Once
 .should.be.true;
@@ -1803,27 +1803,28 @@
 d.should.be.
-tru
+fals
 e;%0A    %7D);%0A 
@@ -2224,32 +2224,32 @@
 ch(function() %7B%0A
-
       sinon.spy(
@@ -2270,24 +2270,62 @@
 Interval');%0A
+      stream = new ShareJSStream(ws);%0A
     %7D);%0A%0A   

Remove keepAlive from default test stream