<commit_before>'use strict';

var sinon = require('sinon');

var EventEmitter = require('events').EventEmitter;

var Stream = require('../lib/stream');
var Client = require('../lib/client');

var Subscriptions = Stream.Subscriptions;

describe('Zotero.Stream', function () {


  before(function () {
    // Stub the open method to create a simple
    // EventEmitter instead of a WebSocket!
    sinon.stub(Stream.prototype, 'open', function () {
      this.socket = new EventEmitter();
      this.bind();
      return this;
    });
  });

  it('is a constructor', function () { Stream.should.be.a.Function; });

  describe('given a simple single-key stream', function () {
    var s;

    var MSG = {
      connected: {
        event: 'connected', retry: 333, topics: ['/users/123456', '/groups/234567']
      },
      updated: {
        event: 'topicUpdated', topic: '/users/123456', version: 678
      },
      added: {
        event: 'topicAdded', topic: '/groups/345678'
      },
      removed: {
        event: 'topicRemoved', topic: '/groups/234567'
      }
    };

    beforeEach(function () { s = new Stream({ key: 'abc123' }); });

    it('creates a websocket instance', function () {
      s.should.have.property('socket');
    });

    it('inherits from Client and EventEmitter', function () {
      s.should.be.instanceof(Client);
      s.should.be.instanceof(EventEmitter);
    });

    it('is not a multi-key stream', function () {
      s.should.have.property('multi', false);
    });

    it('sets the API key header', function () {
      s.options
        .should.have.property('headers')
        .and.have.property('Zotero-API-Key', 'abc123');
    });

    describe('on connected', function () {
      var message = MSG.connected;
      var cb = sinon.spy();

      beforeEach(function () {
        s.on('connected', cb);

        s.socket.emit('open');
        s.socket.emit('message', JSON.stringify(message));
      });

      it('adds the topics to the subscription list', function () {
        s.subscriptions.topics.should.eql(message.topics);
      });

      it('updates the retry value', function () {
        s.retry.delay.should.eql(message.retry);
      });

      it('emits the connected event', function () {
        cb.called.should.be.true;
      });
    });

    describe('topic added/removed events', function () {
      beforeEach(function () {
        s.socket.emit('open');
        s.socket.emit('message', JSON.stringify(MSG.connected));
      });

      it('adds topics to existing subscriptions', function () {
      });

      it('removes topics to existing subscriptions', function () {
      });

      it('emits the events and data', function () {
        var spy = sinon.spy();

        s.on('topicAdded', spy);
        s.on('topicRemoved', spy);
        s.on('topicUpdated', spy);
        s.on('foo', spy);

        s.socket.emit('message', JSON.stringify(MSG.updated));
        s.socket.emit('message', JSON.stringify(MSG.added));
        s.socket.emit('message', JSON.stringify(MSG.updated));
        s.socket.emit('message', JSON.stringify(MSG.removed));
        s.socket.emit('message', JSON.stringify({ event: 'foo' }));

        spy.callCount.should.eql(5);
        spy.args[0][0].should.have.property('topic', MSG.updated.topic);
        spy.args[1][0].should.have.property('topic', MSG.added.topic);
        spy.args[2][0].should.have.property('version', MSG.updated.version);
        spy.args[3][0].should.have.property('topic', MSG.removed.topic);
        spy.args[4][0].should.be.empty;
      });
    });
  });

  describe('given a simple multi-key stream', function () {

    it('creates a websocket instance', function () {
      (new Stream()).should.have.property('socket');
    });

    it('is a multi-key stream', function () {
      (new Stream()).should.have.property('multi', true);
    });

    it('does not set the API key header', function () {
      (new Stream().options)
        .should.have.property('headers')
        .and.not.have.property('Zotero-API-Key');
    });

  });
});

describe('Zotero.Stream.Subscriptions', function () {

  it('is a constructor', function () { Subscriptions.should.be.a.Function; });

  describe('instance', function () {
    var s;

    beforeEach(function () { s = new Subscriptions(); });

    it('has an array of all subscriptions', function () {
      s.should.have.property('all', []);
    });

    it('has an array of all topics', function () {
      s.should.have.property('topics', []);
    });

    describe('.update', function () {
      it('adds new subscriptions', function () {
        s.update([
          { apiKey: 'foo', topics: ['foo', 'bar'] },
          { topics: ['baz'] },
          { apiKey: 'qux' }
        ]);

        s.all.should.have.length(3);
        s.topics.should.eql(['foo', 'bar', 'baz']);
      });

      it('updates existing subscriptions', function () {
        s.update([{ topics: ['foo', 'bar'] }]);
        s.update([{ topics: ['foo' ] }]);

        s.all.should.have.length(1);
        s.topics.should.eql(['foo']);

        s.update([{ apiKey: 'x' }]);

        s.all.should.have.length(2);
        s.topics.should.eql(['foo']);

        s.update([{ apiKey: 'x', topics: ['bar'] }]);

        s.all.should.have.length(2);
        s.topics.should.eql(['foo', 'bar']);

        s.update([{ apiKey: 'x' }]);

        s.all.should.have.length(2);
        s.topics.should.eql(['foo']);
      });
    });

    describe('.cancel', function () {
      beforeEach(function () {
        s.update([ { topics: ['foo'] }, { apiKey: 'x', topics: ['bar', 'baz'] } ]);
      });

      it('cancels all subscriptions for a given key', function () {
        s.cancel([{ apiKey: 'x' }]);

        s.all.should.have.length(1);
        s.topics.should.eql(['foo']);
      });

      it('cancels specific key/topic pair', function () {
        s.cancel([{ apiKey: 'x', topic: 'baz' }]);

        s.all.should.have.length(2);
        s.topics.should.eql(['foo', 'bar']);
      });

      it('cancels public topic', function () {
        s.cancel([{ topic: 'foo' }]);

        s.all.should.have.length(2);
        s.topics.should.eql(['bar', 'baz']);
      });

      it('does not fail on non-existent keys/topics', function () {
        s.cancel.bind(s, [{ apiKey: 'y' }]).should.not.throw();
        s.cancel.bind(s, [{ apiKey: 'y', topic: 'qux' }]).should.not.throw();
        s.cancel.bind(s, [{ topic: 'qux' }]).should.not.throw();

        s.all.should.have.length(2);
        s.topics.should.eql(['foo', 'bar', 'baz']);
      });
    });

    describe('.add', function () {
      beforeEach(function () {
        s.update([ { topics: ['foo'] }, { apiKey: 'x' } ]);
      });

      it('adds a topic to an existing subscription', function () {
        s.add({ topic: 'bar' });

        s.topics.should.eql(['foo', 'bar']);
        s.get().topics.should.eql(['foo', 'bar']);

        s.add({ topic: 'baz', apiKey: 'x' });

        s.topics.should.eql(['foo', 'bar', 'baz']);
        s.get().topics.should.eql(['foo', 'bar']);
        s.get('x').topics.should.eql(['baz']);
      });

      it('fails if subscription does not exist', function () {
        s.add.bind(s, [{ topic: 'baz', apiKey: 'y' }]).should.throw();
      });
    });

    describe('.remove', function () {
      beforeEach(function () {
        s.update([ { topics: ['foo'] }, { apiKey: 'x' } ]);
      });

      it('removes a single topic from existing subscriptions', function () {
        s.remove({ topic: 'foo' });

        s.all.should.have.length(2);
        s.topics.should.be.empty;
      });

      it('does not fail if subscription does not exist', function () {
        s.remove.bind(s, { topic: 'baz', apiKey: 'y' }).should.not.throw();
      });

      it('does not fails if the topic was not part of the subscription', function () {
        s.remove.bind(s, { topic: 'baz' }).should.not.throw();
      });
    });
  });
});
<commit_msg>add topic event tests<commit_after>'use strict';

var sinon = require('sinon');

var EventEmitter = require('events').EventEmitter;

var Stream = require('../lib/stream');
var Client = require('../lib/client');

var Subscriptions = Stream.Subscriptions;

describe('Zotero.Stream', function () {


  before(function () {
    // Stub the open method to create a simple
    // EventEmitter instead of a WebSocket!
    sinon.stub(Stream.prototype, 'open', function () {
      this.socket = new EventEmitter();
      this.bind();
      return this;
    });
  });

  it('is a constructor', function () { Stream.should.be.a.Function; });

  describe('given a simple single-key stream', function () {
    var s;

    var MSG = {
      connected: {
        event: 'connected', retry: 333, topics: ['/users/123456', '/groups/234567']
      },
      updated: {
        event: 'topicUpdated', topic: '/users/123456', version: 678
      },
      added: {
        event: 'topicAdded', topic: '/groups/345678'
      },
      removed: {
        event: 'topicRemoved', topic: '/groups/234567'
      }
    };

    beforeEach(function () { s = new Stream({ key: 'abc123' }); });

    it('creates a websocket instance', function () {
      s.should.have.property('socket');
    });

    it('inherits from Client and EventEmitter', function () {
      s.should.be.instanceof(Client);
      s.should.be.instanceof(EventEmitter);
    });

    it('is not a multi-key stream', function () {
      s.should.have.property('multi', false);
    });

    it('sets the API key header', function () {
      s.options
        .should.have.property('headers')
        .and.have.property('Zotero-API-Key', 'abc123');
    });

    describe('on connected', function () {
      var message = MSG.connected;
      var cb = sinon.spy();

      beforeEach(function () {
        s.on('connected', cb);

        s.socket.emit('open');
        s.socket.emit('message', JSON.stringify(message));
      });

      it('adds the topics to the subscription list', function () {
        s.subscriptions.topics.should.eql(message.topics);
      });

      it('updates the retry value', function () {
        s.retry.delay.should.eql(message.retry);
      });

      it('emits the connected event', function () {
        cb.called.should.be.true;
      });
    });

    describe('topic added/removed events', function () {
      beforeEach(function () {
        s.socket.emit('open');
        s.socket.emit('message', JSON.stringify(MSG.connected));
      });

      it('adds topics to existing subscriptions', function () {
        s.subscriptions.topics.should.not.contain(MSG.added.topic);
      });

      it('removes topics to existing subscriptions', function () {
      });

      it('emits the events and data', function () {
        var spy = sinon.spy();

        s.on('topicAdded', spy);
        s.on('topicRemoved', spy);
        s.on('topicUpdated', spy);
        s.on('foo', spy);

        s.socket.emit('message', JSON.stringify(MSG.updated));
        s.socket.emit('message', JSON.stringify(MSG.added));
        s.socket.emit('message', JSON.stringify(MSG.updated));
        s.socket.emit('message', JSON.stringify(MSG.removed));
        s.socket.emit('message', JSON.stringify({ event: 'foo' }));

        spy.callCount.should.eql(5);
        spy.args[0][0].should.have.property('topic', MSG.updated.topic);
        spy.args[1][0].should.have.property('topic', MSG.added.topic);
        spy.args[2][0].should.have.property('version', MSG.updated.version);
        spy.args[3][0].should.have.property('topic', MSG.removed.topic);
        spy.args[4][0].should.be.empty;
      });
    });
  });

  describe('given a simple multi-key stream', function () {

    it('creates a websocket instance', function () {
      (new Stream()).should.have.property('socket');
    });

    it('is a multi-key stream', function () {
      (new Stream()).should.have.property('multi', true);
    });

    it('does not set the API key header', function () {
      (new Stream().options)
        .should.have.property('headers')
        .and.not.have.property('Zotero-API-Key');
    });

  });
});

describe('Zotero.Stream.Subscriptions', function () {

  it('is a constructor', function () { Subscriptions.should.be.a.Function; });

  describe('instance', function () {
    var s;

    beforeEach(function () { s = new Subscriptions(); });

    it('has an array of all subscriptions', function () {
      s.should.have.property('all', []);
    });

    it('has an array of all topics', function () {
      s.should.have.property('topics', []);
    });

    describe('.update', function () {
      it('adds new subscriptions', function () {
        s.update([
          { apiKey: 'foo', topics: ['foo', 'bar'] },
          { topics: ['baz'] },
          { apiKey: 'qux' }
        ]);

        s.all.should.have.length(3);
        s.topics.should.eql(['foo', 'bar', 'baz']);
      });

      it('updates existing subscriptions', function () {
        s.update([{ topics: ['foo', 'bar'] }]);
        s.update([{ topics: ['foo' ] }]);

        s.all.should.have.length(1);
        s.topics.should.eql(['foo']);

        s.update([{ apiKey: 'x' }]);

        s.all.should.have.length(2);
        s.topics.should.eql(['foo']);

        s.update([{ apiKey: 'x', topics: ['bar'] }]);

        s.all.should.have.length(2);
        s.topics.should.eql(['foo', 'bar']);

        s.update([{ apiKey: 'x' }]);

        s.all.should.have.length(2);
        s.topics.should.eql(['foo']);
      });
    });

    describe('.cancel', function () {
      beforeEach(function () {
        s.update([ { topics: ['foo'] }, { apiKey: 'x', topics: ['bar', 'baz'] } ]);
      });

      it('cancels all subscriptions for a given key', function () {
        s.cancel([{ apiKey: 'x' }]);

        s.all.should.have.length(1);
        s.topics.should.eql(['foo']);
      });

      it('cancels specific key/topic pair', function () {
        s.cancel([{ apiKey: 'x', topic: 'baz' }]);

        s.all.should.have.length(2);
        s.topics.should.eql(['foo', 'bar']);
      });

      it('cancels public topic', function () {
        s.cancel([{ topic: 'foo' }]);

        s.all.should.have.length(2);
        s.topics.should.eql(['bar', 'baz']);
      });

      it('does not fail on non-existent keys/topics', function () {
        s.cancel.bind(s, [{ apiKey: 'y' }]).should.not.throw();
        s.cancel.bind(s, [{ apiKey: 'y', topic: 'qux' }]).should.not.throw();
        s.cancel.bind(s, [{ topic: 'qux' }]).should.not.throw();

        s.all.should.have.length(2);
        s.topics.should.eql(['foo', 'bar', 'baz']);
      });
    });

    describe('.add', function () {
      beforeEach(function () {
        s.update([ { topics: ['foo'] }, { apiKey: 'x' } ]);
      });

      it('adds a topic to an existing subscription', function () {
        s.add({ topic: 'bar' });

        s.topics.should.eql(['foo', 'bar']);
        s.get().topics.should.eql(['foo', 'bar']);

        s.add({ topic: 'baz', apiKey: 'x' });

        s.topics.should.eql(['foo', 'bar', 'baz']);
        s.get().topics.should.eql(['foo', 'bar']);
        s.get('x').topics.should.eql(['baz']);
      });

      it('fails if subscription does not exist', function () {
        s.add.bind(s, [{ topic: 'baz', apiKey: 'y' }]).should.throw();
      });
    });

    describe('.remove', function () {
      beforeEach(function () {
        s.update([ { topics: ['foo'] }, { apiKey: 'x' } ]);
      });

      it('removes a single topic from existing subscriptions', function () {
        s.remove({ topic: 'foo' });

        s.all.should.have.length(2);
        s.topics.should.be.empty;
      });

      it('does not fail if subscription does not exist', function () {
        s.remove.bind(s, { topic: 'baz', apiKey: 'y' }).should.not.throw();
      });

      it('does not fails if the topic was not part of the subscription', function () {
        s.remove.bind(s, { topic: 'baz' }).should.not.throw();
      });
    });
  });
});

{'fraction_non_alphanumeric': 0.2013767209011264, 'fraction_numerical': 0.00788485607008761, 'mean_word_length': 2.404559011504048, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6460786', 'n_tokens_mistral': 5219, 'n_tokens_neox': 5044, 'n_words': 1008}