kritzware / twitch-bot

🤖 Easily create chat bots for Twitch.tv
https://www.npmjs.com/package/twitch-bot
MIT License
148 stars 36 forks source link

Better IRC tests/mocking #12

Closed kritzware closed 6 years ago

kritzware commented 6 years ago

Currently several of the test cases that depend on IRC Twitch events (e.g. timeouts/bans) tend to fail, due to random timeouts or messages not being received.

Perhaps we could add mocking for IRC events rather than creating an actual instance that connects to IRC and depends on the connection stability?

PBug90 commented 6 years ago
// Example test that stubs out node.js net.Socket.write() calls
// Using Mocha to drive: http://visionmedia.github.com/mocha/
// sinon.js for stubs/mocks/spies: http://sinonjs.org
// should.js for assertions: https://github.com/visionmedia/should.js

var sinon = require('sinon')
  , should = require('should')
  , net = require('net')

const TwitchBot = require('../index');

describe('Example Stubbing net.Socket', function() {

  it ("should stub net.Socket.write( ) and echo back what was written", function(done) {
    var myBot = new TwitchBot({
      username: 'test',
      oauth: 'oauth:123abc',
      channels: ['test']
    })

    var stub = sinon.stub(myBot.irc, 'write').callsFake(function (data, encoding, cb) {

      var args = stub.args;
      console.log(args);
      // this will echo whatever they wrote
      if (args.length > 0)
        this.emit('data', stub.args[stub.callCount - 1][0]);
    });

  });
});

Using sinon to stub the TLS-Socket can work I guess, so the tests would not need to connect to a twitch IRC server.

source: https://gist.github.com/davisford/2949118

PBug90 commented 6 years ago
// Example test that stubs out node.js net.Socket.write() calls
// Using Mocha to drive: http://visionmedia.github.com/mocha/
// sinon.js for stubs/mocks/spies: http://sinonjs.org
// should.js for assertions: https://github.com/visionmedia/should.js

var sinon = require('sinon')
  , net = require('net')

const TwitchBot = require('../index');
const expect = require('chai').expect

var stub2 = sinon.stub(TwitchBot.prototype, '_connect')
var myBot = null;
var stub = null;
const samples= require('./samples');

describe('emulated IO tests', function() {
  beforeEach((done)=>{
    myBot = new TwitchBot({
      username: 'test',
      oauth: 'oauth:123abc',
      channels: ['test']
    })
    myBot.irc.on("data",(data)=>{
      console.log("rec: "+data);
    })

    stub = sinon.stub(myBot.irc, 'write')

    var myCallback = function (data, encoding, cb) {
      let received=stub.args[stub.callCount - 1][0];

      if (stub.args.length > 0){
        console.log("sent: "+received)
      }}
    stub.callsFake(myCallback);
    stub2.callsFake(function(){
      this.emit('connect');
    })
    myBot.afterConnect();
    done();
  });
describe('emulated IO tests', function() {

  it ("should handle error if invalid auth", function(done) {

    myBot.on('error', (err) => {
        expect(err.message).to.equal('Login authentication failed');
        done();
    })
    myBot.irc.emit("data","Login authentication failed\r\n");

  });

  it ("should handle a channel message", function(done) {

    myBot.on('message', (chatter) => {
      expect(chatter).to.eql(samples.PRIVMSG.expected)
      done();
    })
    myBot.irc.emit("data",samples.PRIVMSG.raw)

  });

  it ("should handle a subscription message", function(done) {

    myBot.on('subscription', (chatter) => {
      expect(chatter).to.eql(samples.USERNOTICE.subscription_expected)
      done();
    })
    myBot.irc.emit("data",samples.USERNOTICE.subscription_raw)

  });

  it ("should handle a timeout message", function(done) {

    myBot.on('timeout', (chatter) => {
      expect(chatter).to.eql(samples.CLEARCHAT.timeout_expected);
      done();
    })
    myBot.irc.emit("data",samples.CLEARCHAT.timeout_raw);

  });

  it ("should handle a ban message", function(done) {

    myBot.on('ban', (chatter) => {
      expect(chatter).to.eql(samples.CLEARCHAT.ban_expected);
      done();
    })
    myBot.irc.emit("data",samples.CLEARCHAT.ban_raw);

  });
})

describe('say()', () => {

  it('should send a message in the channel', done => {
    stub.callsFake(function (data, encoding, cb) {
      let received=stub.args[stub.callCount - 1][0];
        expect(received).to.eql(`PRIVMSG ${myBot.channels[0]} :testmessage\r\n`);
        done();
    });
    myBot.say('testmessage',myBot.channels[0]);

  })
  it('should fail when the message to send is over 500 characters', done => {
      myBot.say(samples.PRIVMSG.long, myBot.channels[0], err => {
        expect(err.sent).to.equal(false)
        expect(err.message).to.equal('Exceeded PRIVMSG character limit (500)')
        done()
    })
  })
  })
    })

With some refactoring of bot code logic this allows testing input / output without ever creating a connection to twitch servers.

kritzware commented 6 years ago

Interesting. I'll take a look at this in greater depth 👍

PBug90 commented 6 years ago

I am going to refactor the above code when I am back home today and push a working version to my fork. Will also link it here.