Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "sockmafia",
"version": "4.0.3",
"version": "4.1.3",
"description": "Mafia plugin for sockbot",
"main": "src/mafiabot.js",
"scripts": {
Expand All @@ -27,6 +27,7 @@
},
"homepage": "https://github.com/yamikuronue/sockMafia#readme",
"dependencies": {
"axios": "^0.16.2",
"bluebird": "^3.1.1",
"debug": "^2.2.0",
"fs-readfile-promise": "^3.0.0",
Expand All @@ -46,6 +47,7 @@
"istanbul": "^0.4.1",
"jsdoc-to-markdown": "^2.0.1",
"mocha": "^2.3.4",
"nock": "^9.0.14",
"publish-please": "2.1.4",
"sinon": "^1.15.1",
"sinon-as-promised": "^4.0.0",
Expand Down
46 changes: 46 additions & 0 deletions src/mod_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const view = require('./view');
const Promise = require('bluebird');
const debug = require('debug')('sockbot:mafia:modController');
const Utils = require('./utils');
const axios = require('axios');


exports.internals = {};
Expand Down Expand Up @@ -179,6 +180,10 @@ class MafiaModController {
'You are a **cop**! Each night you can investigate one person using `!target playerName in TargetGame`.\n' +
'!send-rolecard TargetUsername in TargetGame\n' +
'```\n');

forum.Commands.add('endGame', 'End the game and declare the winner (mod only)', this.endHandler.bind(this));
forum.Commands.addExtendedHelp('endGame', 'End the game\n\n' +
'Usage: `!endGame [winner]`');
}


Expand Down Expand Up @@ -722,6 +727,47 @@ class MafiaModController {
view.reportError(command, 'Error listing night actions: ', err);
});
}

endHandler(command) {
let game, mod;
let winner = command.args[0];

if (!winner) {
view.reportError(command, 'No winner set. Please use `!winner scum`, `!winner town`, or `!winner 3party` to declare a winner');
return Promise.resolve();
}

winner = winner.toLowerCase();

if (['town', 'scum', '3party'].indexOf(winner) === -1) {
view.reportError(command, 'No winner set. Please use `!winner scum`, `!winner town`, or `!winner 3party` to declare a winner');
return Promise.resolve();
}


return Promise.all([this.getGame(command), command.getUser()])
.then((responses) => {
game = responses[0];

debug('Ending game ' + game.id);
try {
mod = game.getModerator(responses[1].username);
} catch (_) {
return Promise.reject(new Error('You are not a moderator'));
}
return mod.isModerator ? Promise.resolve() : Promise.reject('You are not a moderator');
})
.then(() => game.setActive(false))
.then(() => game.setValue('winner', winner))
.then(() => view.respondInThread(game.topicId, `The game is over! ${winner} has won!`))
.then(() => debug('about to send data'))
.then(() => axios.post('http://mafia.sockdrawer.io:5984/games', game.toJSON()))
.then(() => debug('data sent!'))
.catch((err) => {
logRecoveredError('Error ending game: ' + err);
view.reportError(command, 'Error ending game: ', err);
});
}
}

module.exports = MafiaModController;
199 changes: 199 additions & 0 deletions test/integration/statsTests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
'use strict';
/*globals describe, it, beforeEach, afterEach, before*/

const chai = require('chai'),
sinon = require('sinon'),
nock = require('nock');

//do NOT hit real servers
nock.disableNetConnect();

//promise library plugins
require('sinon-as-promised');
const chaiAsPromised = require('chai-as-promised');
chai.use(chaiAsPromised);

chai.should();

const PlayerController = require('../../src/player_controller');
const ModController = require('../../src/mod_controller');
const DAO = require('../../src/dao');
const view = require('../../src/view.js');

const testConfig = {
db: ':memory:',
};

const mockForum = {
Chat: {
create: () => 1
},
User: {
getByName: (user) => {
return user;
}
},
Post: {
reply: () => {
return Promise.resolve();
}
},
supports: (input) => {
return input === 'Chats' || input === 'Formatting.Markup.HTML' || input === 'Formatting.Multiline';
},
Format: {
urlForTopic: (topicId, slug, postId) => {
return '/t/' + slug + '/' + topicId + '/' + postId;
},
urlForPost: (postId) => {
return '/p/' + postId;
},
header2: (text) => `<h2>${text}</h2>`,
header3: (text) => `<h3>${text}</h3>`,
bold: (text) => `<b>${text}</b>`,
link: (url, text) => `<a href="${url}">${text}</a>`,
}
};

describe('MafiaStats', function () {
let mockCalls = [];
let sandbox;

beforeEach(() => {
sandbox = sinon.sandbox.create();
mockCalls = [];
});

afterEach(() => {
nock.cleanAll();
sandbox.restore();
});

describe('Basic game stats', () => {
let dao, playerController, modController, game;

before(() => {
//Set up the database
dao = new DAO(':memory:');
playerController = new PlayerController(dao, testConfig);
playerController.formatter = {
urlForPost: () => '',
quoteText: (input) => input
};
modController = new ModController(dao, testConfig);


view.activate(mockForum);

return dao.createGame(1, 'Game 1')
.then((g) => {
game = g;
sinon.stub(dao, 'getGameByTopicId').resolves(game);
return game.addPlayer('yamikuronue');
})
.then(() => game.addPlayer('accalia'))
.then(() => game.addPlayer('dreikin'))
.then(() => game.addPlayer('tehninja'))
.then(() => game.addModerator('moddyMcModFace'))
.then(() => game.newDay());
});

it('should record basic stats', () => {
let command = {
args: ['@accalia'],
input: '!vote @accalia',
reply: sandbox.stub(),
getTopic: () => Promise.resolve({id: 2}),
getPost: () => Promise.resolve({id: 1}),
getUser: () => Promise.resolve({username: 'yamikuronue'}),
parent: {
ids: {
topic: 1
}
}
};

mockCalls.push(nock('http://mafia.sockdrawer.io:5984')
.post('/games')
.reply(201, JSON.stringify({ok: true})));

//First, register a vote

return playerController.voteHandler(command).then(() => {
command = {
args: [],
input: '!unvote',
reply: sandbox.stub(),
getTopic: () => Promise.resolve({id: 2}),
getPost: () => Promise.resolve({id: 2}),
getUser: () => Promise.resolve({username: 'yamikuronue'}),
parent: {
ids: {
topic: 2
}
}
};

//Then, unvote
return playerController.unvoteHandler(command);
}).then(() => {
command.reply.lastCall.args[0].should.include('@yamikuronue unvoted');

command = {
args: ['@accalia'],
input: '!vote @accalia',
reply: sandbox.stub(),
getTopic: () => Promise.resolve({id: 2}),
getPost: () => Promise.resolve({id: 3}),
getUser: () => Promise.resolve({username: 'yamikuronue'}),
parent: {
ids: {
topic: 2
}
}
};

//Vote for the same person again
return playerController.voteHandler(command);
}).then(() => {

command = {
args: [],
input: '!unvote',
reply: sandbox.stub(),
getTopic: () => Promise.resolve({id: 2}),
getPost: () => Promise.resolve({id: 4}),
getUser: () => Promise.resolve({username: 'yamikuronue'}),
parent: {
ids: {
topic: 2
}
}
};

//Then unvote
return playerController.unvoteHandler(command);
}).then(() => {

command = {
args: ['scum'],
input: '!endGame',
reply: sandbox.stub(),
getTopic: () => Promise.resolve({id: 2}),
getPost: () => Promise.resolve({id: 5}),
getUser: () => Promise.resolve({username: 'moddyMcModFace'}),
parent: {
ids: {
topic: 2
}
}
};

return modController.endHandler(command);
}).then(() => {
mockCalls[0].isDone().should.be.true;
game.getValue('winner').should.equal('scum');
});
});
});
});
Loading