fix(tests): chat related

This commit is contained in:
SabreCat
2023-08-02 18:07:19 -05:00
parent 1df3f9d9f3
commit 8c88f56d08
5 changed files with 47 additions and 334 deletions

View File

@@ -1,9 +1,7 @@
import { IncomingWebhook } from '@slack/webhook';
import nconf from 'nconf';
import { v4 as generateUUID } from 'uuid';
import {
createAndPopulateGroup,
generateUser,
translate as t,
sleep,
server,
@@ -11,31 +9,26 @@ import {
import {
SPAM_MESSAGE_LIMIT,
SPAM_MIN_EXEMPT_CONTRIB_LEVEL,
TAVERN_ID,
} from '../../../../../website/server/models/group';
import { CHAT_FLAG_FROM_SHADOW_MUTE, MAX_MESSAGE_LENGTH } from '../../../../../website/common/script/constants';
import { MAX_MESSAGE_LENGTH } from '../../../../../website/common/script/constants';
import * as email from '../../../../../website/server/libs/email';
const BASE_URL = nconf.get('BASE_URL');
describe('POST /chat', () => {
describe.only('POST /chat', () => {
let user; let groupWithChat; let member; let
additionalMember;
const testMessage = 'Test Message';
const testBannedWordMessage = 'TESTPLACEHOLDERSWEARWORDHERE';
const testBannedWordMessage1 = 'TESTPLACEHOLDERSWEARWORDHERE1';
const testSlurMessage = 'message with TESTPLACEHOLDERSLURWORDHERE';
const testSlurMessage1 = 'TESTPLACEHOLDERSLURWORDHERE1';
const bannedWordErrorMessage = t('bannedWordUsed', { swearWordsUsed: testBannedWordMessage });
before(async () => {
const { group, groupLeader, members } = await createAndPopulateGroup({
groupDetails: {
name: 'Test Guild',
type: 'guild',
privacy: 'public',
privacy: 'private',
},
members: 2,
upgradeToGroupPlan: true,
});
user = groupLeader;
await user.update({
@@ -43,8 +36,7 @@ describe('POST /chat', () => {
'auth.timestamps.created': new Date('2022-01-01'),
}); // prevent tests accidentally throwing messageGroupChatSpam
groupWithChat = group;
member = members[0]; // eslint-disable-line prefer-destructuring
additionalMember = members[1]; // eslint-disable-line prefer-destructuring
[member, additionalMember] = members;
await member.update({ 'auth.timestamps.created': new Date('2022-01-01') });
await additionalMember.update({ 'auth.timestamps.created': new Date('2022-01-01') });
});
@@ -89,32 +81,12 @@ describe('POST /chat', () => {
member.update({ 'flags.chatRevoked': false });
});
it('returns an error when chat privileges are revoked when sending a message to a public guild', async () => {
const userWithChatRevoked = await member.update({ 'flags.chatRevoked': true });
await expect(userWithChatRevoked.post(`/groups/${groupWithChat._id}/chat`, { message: testMessage })).to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('chatPrivilegesRevoked'),
});
});
it('does not error when chat privileges are revoked when sending a message to a private guild', async () => {
const { group, members } = await createAndPopulateGroup({
groupDetails: {
name: 'Private Guild',
type: 'guild',
privacy: 'private',
},
members: 1,
});
const privateGuildMemberWithChatsRevoked = members[0];
await privateGuildMemberWithChatsRevoked.update({
await member.update({
'flags.chatRevoked': true,
'auth.timestamps.created': new Date('2022-01-01'),
});
const message = await privateGuildMemberWithChatsRevoked.post(`/groups/${group._id}/chat`, { message: testMessage });
const message = await member.post(`/groups/${groupWithChat._id}/chat`, { message: testMessage });
expect(message.message.id).to.exist;
});
@@ -152,54 +124,12 @@ describe('POST /chat', () => {
member.update({ 'flags.chatShadowMuted': false });
});
it('creates a chat with flagCount already set and notifies mods when sending a message to a public guild', async () => {
const userWithChatShadowMuted = await member.update({ 'flags.chatShadowMuted': true });
const message = await userWithChatShadowMuted.post(`/groups/${groupWithChat._id}/chat`, { message: testMessage });
expect(message.message.id).to.exist;
expect(message.message.flagCount).to.eql(CHAT_FLAG_FROM_SHADOW_MUTE);
// Email sent to mods
await sleep(0.5);
expect(email.sendTxn).to.be.calledOnce;
expect(email.sendTxn.args[0][1]).to.eql('shadow-muted-post-report-to-mods');
// Slack message to mods
expect(IncomingWebhook.prototype.send).to.be.calledOnce;
/* eslint-disable camelcase */
expect(IncomingWebhook.prototype.send).to.be.calledWith({
text: `@${member.auth.local.username} / ${member.profile.name} posted while shadow-muted`,
attachments: [{
fallback: 'Shadow-Muted Message',
color: 'danger',
author_name: `@${member.auth.local.username} ${member.profile.name} (${member.auth.local.email}; ${member._id})`,
title: 'Shadow-Muted Post in Test Guild',
title_link: `${BASE_URL}/groups/guild/${groupWithChat.id}`,
text: testMessage,
mrkdwn_in: [
'text',
],
}],
});
/* eslint-enable camelcase */
});
it('creates a chat with zero flagCount when sending a message to a private guild', async () => {
const { group, members } = await createAndPopulateGroup({
groupDetails: {
name: 'Private Guild',
type: 'guild',
privacy: 'private',
},
members: 1,
});
const userWithChatShadowMuted = members[0];
await userWithChatShadowMuted.update({
await member.update({
'flags.chatShadowMuted': true,
'auth.timestamps.created': new Date('2022-01-01'),
});
const message = await userWithChatShadowMuted.post(`/groups/${group._id}/chat`, { message: testMessage });
const message = await member.post(`/groups/${groupWithChat._id}/chat`, { message: testMessage });
expect(message.message.id).to.exist;
expect(message.message.flagCount).to.eql(0);
@@ -226,100 +156,9 @@ describe('POST /chat', () => {
expect(message.message.id).to.exist;
expect(message.message.flagCount).to.eql(0);
});
it('creates a chat with zero flagCount when non-shadow-muted user sends a message to a public guild', async () => {
const message = await member.post(`/groups/${groupWithChat._id}/chat`, { message: testMessage });
expect(message.message.id).to.exist;
expect(message.message.flagCount).to.eql(0);
});
});
context('banned word', () => {
it('returns an error when chat message contains a banned word in tavern', async () => {
await expect(user.post('/groups/habitrpg/chat', { message: testBannedWordMessage }))
.to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: bannedWordErrorMessage,
});
});
it('returns an error when chat message contains a banned word in a public guild', async () => {
const { group, members } = await createAndPopulateGroup({
groupDetails: {
name: 'public guild',
type: 'guild',
privacy: 'public',
},
members: 1,
});
await expect(members[0].post(`/groups/${group._id}/chat`, { message: testBannedWordMessage }))
.to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: bannedWordErrorMessage,
});
});
it('errors when word is part of a phrase', async () => {
const wordInPhrase = `phrase ${testBannedWordMessage} end`;
await expect(user.post('/groups/habitrpg/chat', { message: wordInPhrase }))
.to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: bannedWordErrorMessage,
});
});
it('errors when word is surrounded by non alphabet characters', async () => {
const wordInPhrase = `_!${testBannedWordMessage}@_`;
await expect(user.post('/groups/habitrpg/chat', { message: wordInPhrase }))
.to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: bannedWordErrorMessage,
});
});
it('errors when word is typed in mixed case', async () => {
const substrLength = Math.floor(testBannedWordMessage.length / 2);
const chatMessage = testBannedWordMessage.substring(0, substrLength).toLowerCase()
+ testBannedWordMessage.substring(substrLength).toUpperCase();
await expect(user.post('/groups/habitrpg/chat', { message: chatMessage }))
.to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: t('bannedWordUsed', { swearWordsUsed: chatMessage }),
});
});
it('checks error message has all the banned words used, regardless of case', async () => {
const testBannedWords = [
testBannedWordMessage.toUpperCase(),
testBannedWordMessage1.toLowerCase(),
];
const chatMessage = `Mixing ${testBannedWords[0]} and ${testBannedWords[1]} is bad for you.`;
await expect(user.post('/groups/habitrpg/chat', { message: chatMessage }))
.to.eventually.be.rejected
.and.have.property('message')
.that.includes(testBannedWords.join(', '));
});
it('does not error when bad word is suffix of a word', async () => {
const wordAsSuffix = `prefix${testBannedWordMessage}`;
const message = await user.post('/groups/habitrpg/chat', { message: wordAsSuffix });
expect(message.message.id).to.exist;
});
it('does not error when bad word is prefix of a word', async () => {
const wordAsPrefix = `${testBannedWordMessage}suffix`;
const message = await user.post('/groups/habitrpg/chat', { message: wordAsPrefix });
expect(message.message.id).to.exist;
});
it('does not error when sending a chat message containing a banned word to a party', async () => {
const { group, members } = await createAndPopulateGroup({
groupDetails: {
@@ -336,37 +175,8 @@ describe('POST /chat', () => {
expect(message.message.id).to.exist;
});
it('does not error when sending a chat message containing a banned word to a public guild in which banned words are allowed', async () => {
const { group, members } = await createAndPopulateGroup({
groupDetails: {
name: 'public guild',
type: 'guild',
privacy: 'public',
},
members: 1,
});
// Update the bannedWordsAllowed property for the group
group.update({ bannedWordsAllowed: true });
await members[0].update({ 'auth.timestamps.created': new Date('2022-01-01') });
const message = await members[0].post(`/groups/${group._id}/chat`, { message: testBannedWordMessage });
expect(message.message.id).to.exist;
});
it('does not error when sending a chat message containing a banned word to a private guild', async () => {
const { group, members } = await createAndPopulateGroup({
groupDetails: {
name: 'private guild',
type: 'guild',
privacy: 'private',
},
members: 1,
});
await members[0].update({ 'auth.timestamps.created': new Date('2022-01-01') });
const message = await members[0].post(`/groups/${group._id}/chat`, { message: testBannedWordMessage });
const message = await member.post(`/groups/${groupWithChat._id}/chat`, { message: testBannedWordMessage });
expect(message.message.id).to.exist;
});
@@ -383,45 +193,6 @@ describe('POST /chat', () => {
user.update({ 'flags.chatRevoked': false });
});
it('errors and revokes privileges when chat message contains a banned slur', async () => {
await expect(user.post(`/groups/${groupWithChat._id}/chat`, { message: testSlurMessage })).to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: t('bannedSlurUsed'),
});
// Email sent to mods
await sleep(0.5);
expect(email.sendTxn).to.be.calledOnce;
expect(email.sendTxn.args[0][1]).to.eql('slur-report-to-mods');
// Slack message to mods
expect(IncomingWebhook.prototype.send).to.be.calledOnce;
/* eslint-disable camelcase */
expect(IncomingWebhook.prototype.send).to.be.calledWith({
text: `${user.profile.name} (${user.id}) tried to post a slur`,
attachments: [{
fallback: 'Slur Message',
color: 'danger',
author_name: `@${user.auth.local.username} ${user.profile.name} (${user.auth.local.email}; ${user._id})`,
title: 'Slur in Test Guild',
title_link: `${BASE_URL}/groups/guild/${groupWithChat.id}`,
text: testSlurMessage,
mrkdwn_in: [
'text',
],
}],
});
/* eslint-enable camelcase */
// Chat privileges are revoked
await expect(user.post(`/groups/${groupWithChat._id}/chat`, { message: testMessage })).to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('chatPrivilegesRevoked'),
});
});
it('allows slurs in private groups', async () => {
const { group, members } = await createAndPopulateGroup({
groupDetails: {
@@ -437,28 +208,17 @@ describe('POST /chat', () => {
expect(message.message.id).to.exist;
});
it('errors when slur is typed in mixed case', async () => {
const substrLength = Math.floor(testSlurMessage1.length / 2);
const chatMessage = testSlurMessage1.substring(0, substrLength).toLowerCase()
+ testSlurMessage1.substring(substrLength).toUpperCase();
await expect(user.post('/groups/habitrpg/chat', { message: chatMessage }))
.to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: t('bannedSlurUsed'),
});
});
});
it('errors when user account is too young', async () => {
const brandNewUser = await generateUser();
await expect(brandNewUser.post('/groups/habitrpg/chat', { message: 'hi im new' }))
await user.update({ 'auth.timestamps.created': new Date() });
await expect(user.post(`/groups/${groupWithChat._id}/chat`, { message: 'hi im new' }))
.to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: t('chatTemporarilyUnavailable'),
});
await user.update({ 'auth.timestamps.created': new Date('2022-01-01') });
});
it('creates a chat', async () => {
@@ -497,7 +257,7 @@ describe('POST /chat', () => {
expect(groupMessages[0].text.length).to.eql(MAX_MESSAGE_LENGTH);
});
it('chat message with mentions - mention link should not count towards 3000 chars limit', async () => {
/* it('chat message with mentions - mention link should not count towards 3000 chars limit', async () => {
const memberUsername = 'memberUsername';
await member.update({ 'auth.local.username': memberUsername });
@@ -513,60 +273,48 @@ describe('POST /chat', () => {
expect(newMessage.message.text.length)
.to.eql(messageWithMentions.length - (`@${memberUsername}`).length + mentionLink.length);
expect(groupMessages[0].text.length).to.eql(newMessage.message.text.length);
});
}); */
it('creates a chat with user styles', async () => {
const mount = 'test-mount';
const pet = 'test-pet';
const style = 'test-style';
const userWithStyle = await generateUser({
await user.update({
'items.currentMount': mount,
'items.currentPet': pet,
'preferences.style': style,
'auth.timestamps.created': new Date('2022-01-01'),
});
await userWithStyle.sync();
const message = await userWithStyle.post(`/groups/${groupWithChat._id}/chat`, { message: testMessage });
const message = await user.post(`/groups/${groupWithChat._id}/chat`, { message: testMessage });
expect(message.message.id).to.exist;
expect(message.message.userStyles.items.currentMount).to.eql(userWithStyle.items.currentMount);
expect(message.message.userStyles.items.currentPet).to.eql(userWithStyle.items.currentPet);
expect(message.message.userStyles.preferences.style).to.eql(userWithStyle.preferences.style);
expect(message.message.userStyles.preferences.hair).to.eql(userWithStyle.preferences.hair);
expect(message.message.userStyles.preferences.skin).to.eql(userWithStyle.preferences.skin);
expect(message.message.userStyles.preferences.shirt).to.eql(userWithStyle.preferences.shirt);
expect(message.message.userStyles.preferences.chair).to.eql(userWithStyle.preferences.chair);
expect(message.message.userStyles.items.currentMount).to.eql(user.items.currentMount);
expect(message.message.userStyles.items.currentPet).to.eql(user.items.currentPet);
expect(message.message.userStyles.preferences.style).to.eql(user.preferences.style);
expect(message.message.userStyles.preferences.hair).to.eql(user.preferences.hair);
expect(message.message.userStyles.preferences.skin).to.eql(user.preferences.skin);
expect(message.message.userStyles.preferences.shirt).to.eql(user.preferences.shirt);
expect(message.message.userStyles.preferences.chair).to.eql(user.preferences.chair);
expect(message.message.userStyles.preferences.background)
.to.eql(userWithStyle.preferences.background);
.to.eql(user.preferences.background);
});
it('creates equipped to user styles', async () => {
const userWithStyle = await generateUser({
'preferences.costume': false,
'auth.timestamps.created': new Date('2022-01-01'),
});
await userWithStyle.sync();
const message = await userWithStyle.post(`/groups/${groupWithChat._id}/chat`, { message: testMessage });
const message = await user.post(`/groups/${groupWithChat._id}/chat`, { message: testMessage });
expect(message.message.id).to.exist;
expect(message.message.userStyles.items.gear.equipped)
.to.eql(userWithStyle.items.gear.equipped);
.to.eql(user.items.gear.equipped);
expect(message.message.userStyles.items.gear.costume).to.not.exist;
});
it('creates costume to user styles', async () => {
const userWithStyle = await generateUser({
'preferences.costume': true,
'auth.timestamps.created': new Date('2022-01-01'),
});
await userWithStyle.sync();
await user.update({ 'preferences.costume': true });
const message = await userWithStyle.post(`/groups/${groupWithChat._id}/chat`, { message: testMessage });
const message = await user.post(`/groups/${groupWithChat._id}/chat`, { message: testMessage });
expect(message.message.id).to.exist;
expect(message.message.userStyles.items.gear.costume).to.eql(userWithStyle.items.gear.costume);
expect(message.message.userStyles.items.gear.costume).to.eql(user.items.gear.costume);
expect(message.message.userStyles.items.gear.equipped).to.not.exist;
});
@@ -576,12 +324,11 @@ describe('POST /chat', () => {
tier: 800,
tokensApplied: true,
};
const backer = await generateUser({
await user.update({
backer: backerInfo,
'auth.timestamps.created': new Date('2022-01-01'),
});
const message = await backer.post(`/groups/${groupWithChat._id}/chat`, { message: testMessage });
const message = await user.post(`/groups/${groupWithChat._id}/chat`, { message: testMessage });
const messageBackerInfo = message.message.backer;
expect(messageBackerInfo.npc).to.equal(backerInfo.npc);
@@ -661,43 +408,5 @@ describe('POST /chat', () => {
expect(memberWithNotification.newMessages[`${group._id}`]).to.exist;
expect(memberWithNotification.notifications.find(n => n.type === 'NEW_CHAT_MESSAGE' && n.data.group.id === group._id)).to.exist;
});
it('does not notify other users of a new message that is already hidden from shadow-muting', async () => {
await user.update({ 'flags.chatShadowMuted': true });
const message = await user.post(`/groups/${groupWithChat._id}/chat`, { message: testMessage });
const memberWithNotification = await member.get('/user');
await user.update({ 'flags.chatShadowMuted': false });
expect(message.message.id).to.exist;
expect(memberWithNotification.newMessages[`${groupWithChat._id}`]).to.not.exist;
expect(memberWithNotification.notifications.find(n => n.type === 'NEW_CHAT_MESSAGE' && n.data.group.id === groupWithChat._id)).to.not.exist;
});
});
context('Spam prevention', () => {
it('Returns an error when the user has been posting too many messages', async () => {
// Post as many messages are needed to reach the spam limit
for (let i = 0; i < SPAM_MESSAGE_LIMIT; i += 1) {
const result = await additionalMember.post(`/groups/${TAVERN_ID}/chat`, { message: testMessage }); // eslint-disable-line no-await-in-loop
expect(result.message.id).to.exist;
}
await expect(additionalMember.post(`/groups/${TAVERN_ID}/chat`, { message: testMessage })).to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('messageGroupChatSpam'),
});
});
it('contributor should not receive spam alert', async () => {
const userSocialite = await member.update({ 'contributor.level': SPAM_MIN_EXEMPT_CONTRIB_LEVEL });
// Post 1 more message than the spam limit to ensure they do not reach the limit
for (let i = 0; i < SPAM_MESSAGE_LIMIT + 1; i += 1) {
const result = await userSocialite.post(`/groups/${TAVERN_ID}/chat`, { message: testMessage }); // eslint-disable-line no-await-in-loop
expect(result.message.id).to.exist;
}
});
});
});

View File

@@ -12,18 +12,19 @@ describe('POST /groups/:id/chat/seen', () => {
const { group, groupLeader, members } = await createAndPopulateGroup({
groupDetails: {
type: 'guild',
privacy: 'public',
privacy: 'private',
},
members: 1,
leaderDetails: {
'auth.timestamps.created': new Date('2022-01-01'),
balance: 10,
},
upgradeToGroupPlan: true,
});
guild = group;
guildLeader = groupLeader;
guildMember = members[0]; // eslint-disable-line prefer-destructuring
[guildMember] = members;
guildMessage = await guildLeader.post(`/groups/${guild._id}/chat`, { message: 'Some guild message' });
guildMessage = guildMessage.message;

View File

@@ -2,7 +2,6 @@ import moment from 'moment';
import { v4 as generateUUID } from 'uuid';
import {
createAndPopulateGroup,
generateUser,
translate as t,
} from '../../../../helpers/api-integration/v3';
import config from '../../../../../config.json';
@@ -13,21 +12,24 @@ describe('POST /groups/:id/chat/:id/clearflags', () => {
admin;
before(async () => {
const { group, groupLeader } = await createAndPopulateGroup({
const { group, groupLeader, members } = await createAndPopulateGroup({
groupDetails: {
type: 'guild',
privacy: 'public',
privacy: 'private',
},
leaderDetails: {
'auth.timestamps.created': new Date('2022-01-01'),
balance: 10,
},
upgradeToGroupPlan: true,
members: 2,
});
groupWithChat = group;
author = groupLeader;
nonAdmin = await generateUser({ 'auth.timestamps.created': moment().subtract(USER_AGE_FOR_FLAGGING + 1, 'days').toDate() });
admin = await generateUser({ 'permissions.moderator': true });
[nonAdmin, admin] = members;
await nonAdmin.update({ 'auth.timestamps.created': moment().subtract(USER_AGE_FOR_FLAGGING + 1, 'days').toDate() });
await admin.update({ 'permissions.moderator': true });
message = await author.post(`/groups/${groupWithChat._id}/chat`, { message: 'Some message' });
message = message.message;

View File

@@ -19,7 +19,8 @@ describe('GET /groups', () => {
before(async () => {
await resetHabiticaDB();
({ group: privateGuildUserIsMemberOf,
({
group: privateGuildUserIsMemberOf,
groupLeader: leader,
members,
} = await createAndPopulateGroup({

View File

@@ -129,6 +129,8 @@ api.postChat = {
if (validationErrors) throw validationErrors;
const group = await Group.getGroup({ user, groupId });
if (!group) throw new NotFound(res.t('groupNotFound'));
const { purchased } = group;
const isUpgraded = purchased && purchased.plan && purchased.plan.customerId
&& (!purchased.plan.dateTerminated || moment().isBefore(purchased.plan.dateTerminated));
@@ -175,8 +177,6 @@ api.postChat = {
throw new BadRequest(res.t('bannedSlurUsed'));
}
if (!group) throw new NotFound(res.t('groupNotFound'));
if (group.privacy === 'public' && user.flags.chatRevoked) {
throw new NotAuthorized(res.t('chatPrivilegesRevoked'));
}