180 passing / 24 failing

This commit is contained in:
ADAMJR 2021-09-30 13:39:32 +01:00
parent 7ccbc5c3c7
commit afce78a86f
21 changed files with 118 additions and 435 deletions

View File

@ -1,6 +1,6 @@
import { Document, model, Schema } from 'mongoose';
import patterns from '../../types/patterns';
import { createdAtToDate, getNameAcronym, useId } from '../../utils/utils';
import { createdAtToDate, useId } from '../../utils/utils';
import { generateSnowflake } from '../snowflake-entity';
// properties we don't need to define when creating a guild

View File

@ -67,15 +67,4 @@ export class WSGuard {
const id = this.users.verifyToken(token);
return { id };
}
public validateKeys<K extends keyof typeof Prohibited>(type: K, partial: any) {
const contains = this.includesProhibited<K>(partial, type);
if (contains)
throw new TypeError('Contains readonly values');
}
private includesProhibited<K extends keyof typeof Prohibited>(partial: any, type: K) {
const keys = Object.keys(partial);
return Prohibited[type].some(k => keys.includes(k));
}
}

View File

@ -18,18 +18,18 @@ export default class implements WSEvent<'GUILD_UPDATE'> {
public async invoke(ws: WebSocket, client: Socket, { guildId, name, iconURL }: WS.Params.GuildUpdate) {
await this.guard.validateCan(client, guildId, 'MANAGE_GUILD');
const partialGuild = { name, iconURL };
this.guard.validateKeys('guild', partialGuild);
const guild = await this.guilds.get(guildId);
const partial: Partial<Entity.Guild> = {};
const hasChanged = (key: string, value: any) => value && guild[key] !== value;
if (iconURL) guild.iconURL = iconURL;
if (name) guild.name = name;
if (hasChanged('iconURL', iconURL)) guild.iconURL = iconURL;
if (hasChanged('name', name)) guild.name = name!;
Object.assign(guild, partial);
await guild.save();
ws.io
.to(guildId)
.emit('GUILD_UPDATE', { guildId, partialGuild } as WS.Args.GuildUpdate);
.emit('GUILD_UPDATE', { guildId, partialGuild: partial } as WS.Args.GuildUpdate);
}
}

View File

@ -1,119 +0,0 @@
import AddFriend from '../../../src/ws/ws-events/add-friend';
import RemoveFriend from '../../../src/ws/ws-events/remove-friend';
import { WebSocket } from '../../../src/ws/websocket';
import io from 'socket.io-client';
import { Mock } from '../../mock/mock';
import { expect } from 'chai';
import { SelfUserDocument, User } from '../../../src/data/models/user';
import { Channel } from '../../../src/data/models/channel';
describe('add-friend', () => {
const client = (io as any)(`http://localhost:${process.env.PORT}`) as any;
let event: AddFriend;
let ws: WebSocket;
let sender: SelfUserDocument;
let friend: SelfUserDocument;
beforeEach(async () => {
({ event, ws, user: sender as any } = await Mock.defaultSetup(client, AddFriend));
friend = await Mock.self();
});
afterEach(async () => await Mock.afterEach(ws));
after(async () => await Mock.after(client));
it('user sends request, fulfilled', async () => {
await expect(addFriend()).to.be.fulfilled;
});
it('user adds self as friend, rejected', async () => {
friend.username = sender.username;
await expect(addFriend()).to.be.rejectedWith('You cannot add yourself as a friend');
});
it('friend blocked sender, rejected', async () => {
friend.ignored?.userIds.push(sender.id);
await friend.updateOne(friend);
await expect(addFriend()).to.be.rejectedWith('This user is blocking you');
});
it('user adds non existing user, rejected', async () => {
await friend.deleteOne();
await expect(addFriend()).to.be.rejectedWith('User Not Found');
});
it('both users add each other, creates one dm channel', async () => {
await addFriend();
await returnFriend();
const exists = await Channel.countDocuments({ memberIds: sender.id });
expect(exists).to.equal(1);
});
it('user add, remove, then re-add each other, reuses old dm channel', async () => {
await addFriend();
await returnFriend();
await removeFriend();
await addFriend();
await returnFriend();
const docs = await Channel.countDocuments({ type: 'DM' });
expect(docs).to.equal(1);
});
it('both users add each other, friend request removed', async () => {
await addFriend();
await returnFriend();
sender = await User.findById(sender.id) as any;
friend = await User.findById(friend.id) as any;
expect(sender.friendRequestIds).to.be.empty;
expect(friend.friendRequestIds).to.be.empty;
});
it('both users add each other, friend added', async () => {
await addFriend();
await returnFriend();
friend = await User.findById(friend.id) as any;
sender = await User.findById(sender.id) as any;
expect(friend.friendIds.length).to.equal(1);
expect(sender.friendIds.length).to.equal(1);
});
it('user adds friends twice, rejected', async () => {
await addFriend();
await expect(addFriend()).to.be.rejectedWith('Friend request already sent');
});
it('user already friends, rejected', async () => {
await addFriend();
await returnFriend();
await expect(addFriend()).to.be.rejectedWith('You are already friends');
});
async function addFriend() {
return event.invoke(ws, client, { username: friend.username });
}
async function returnFriend() {
ws.sessions.set(client.id, friend.id);
await event.invoke(ws, client, { username: sender.username });
ws.sessions.set(client.id, sender.id);
}
async function removeFriend() {
await new RemoveFriend().invoke(ws, client, { friendId: friend.id });
}
});

View File

@ -11,11 +11,10 @@ describe('guild-create', () => {
let ws: WebSocket;
let event: GuildCreate;
let user: UserDocument;
let member: GuildMemberDocument;
let user: SelfUserDocument;
beforeEach(async () => {
({ ws, event, member, user } = await Mock.defaultSetup(client, GuildCreate));
({ ws, event, user } = await Mock.defaultSetup(client, GuildCreate));
});
afterEach(async () => Mock.afterEach(ws));
@ -26,19 +25,17 @@ describe('guild-create', () => {
});
it('user creates guild, added to user.guilds', async () => {
const oldCount = user.guilds.length;
const oldCount = user.guildIds.length;
await guildCreate();
user = await User.findById(user.id);
expect(user.guilds.length).to.be.greaterThan(oldCount);
expect(user.guildIds.length).to.be.greaterThan(oldCount);
});
function guildCreate(partialGuild?: Partial<Entity.Guild>) {
return event.invoke(ws, client, {
partialGuild: {
name: 'Mock Guild',
...partialGuild,
},
});
}
});

View File

@ -73,12 +73,12 @@ describe('guild-delete', () => {
expect(guild).to.be.null;
});
it('owner deletes guild, removed from all user.guilds', async () => {
const oldCount = user.guilds.length;
it('owner deletes guild, removed from all user guildIds', async () => {
const oldCount = user.guildIds.length;
await guildDelete();
user = await User.findById(user.id);
expect(user.guilds.length).to.be.lessThan(oldCount);
expect(user.guildIds.length).to.be.lessThan(oldCount);
});
function guildDelete() {
@ -90,7 +90,6 @@ describe('guild-delete', () => {
user = await User.findById(guild.ownerId);
}
async function makeNoob() {
user = await User.findById(guild.members[1].userId);
ws.sessions.set(client.id, user.id);
}
});

View File

@ -9,24 +9,20 @@ import { Invite, InviteDocument } from '../../../src/data/models/invite';
import { expect, spy } from 'chai';
import Guilds from '../../../src/data/guilds';
import Users from '../../../src/data/users';
import { GuildMember } from '../../../src/data/models/guild-member';
describe('guild-member-add', () => {
const client = (io as any)(`http://localhost:${process.env.PORT}`) as any;
let event: GuildMemberAdd;
let guilds: Guilds;
let users: Users;
let ws: WebSocket;
let user: UserDocument;
let user: SelfUserDocument;
let guild: GuildDocument;
let invite: InviteDocument;
beforeEach(async() => {
({ event, ws, user, guild } = await Mock.defaultSetup(client, GuildMemberAdd));
guilds = Deps.get<Guilds>(Guilds);
users = Deps.get<Users>(Users);
user = await Mock.user([]);
ws.sessions.set(client.id, user.id);
@ -42,7 +38,6 @@ describe('guild-member-add', () => {
it('user already joined, rejected', async () => {
await guildMemberAdd();
await expect(guildMemberAdd()).to.be.rejectedWith('User already in guild');
});
@ -54,18 +49,18 @@ describe('guild-member-add', () => {
});
it('valid invite and code, member added to guild', async () => {
const oldMemberCount = guild.members.length;
const oldMemberCount = await GuildMember.count({ guildId: guild.id });
await guildMemberAdd();
guild = await guilds.get(guild.id);
expect(guild.members.length).to.be.greaterThan(oldMemberCount);
const newMemberCount = await GuildMember.count({ guildId: guild.id });
expect(newMemberCount).to.be.greaterThan(oldMemberCount);
});
it('valid invite and code, guild added to user guilds', async () => {
await guildMemberAdd();
user = await users.get(user.id);
expect(user.guilds.length).to.equal(1);
user = await Mock.users.get(user.id);
expect(user.guildIds.length).to.equal(1);
});
it('invite has reached max uses, is deleted', async () => {
@ -86,19 +81,9 @@ describe('guild-member-add', () => {
it('invalid invite code, rejected', async () => {
invite.id = '';
await expect(guildMemberAdd()).to.be.rejectedWith('Invite Not Found');
});
it('valid invite and code, emits to guild room', async () => {
const to = spy.on(ws.io, 'to');
await guildMemberAdd();
guild = await guilds.get(guild.id);
expect(to).to.have.been.called.with(guild.id);
});
function guildMemberAdd() {
return event.invoke(ws, client, { inviteCode: invite.id });
}

View File

@ -4,15 +4,15 @@ import io from 'socket.io-client';
import { Mock } from '../../mock/mock';
import { GuildDocument } from '../../../src/data/models/guild';
import { expect } from 'chai';
import { GuildMemberDocument } from '../../../src/data/models/guild-member';
import { User, UserDocument } from '../../../src/data/models/user';
import { GuildMember, GuildMemberDocument } from '../../../src/data/models/guild-member';
import { SelfUserDocument, User } from '../../../src/data/models/user';
describe('guild-member-remove', () => {
const client = (io as any)(`http://localhost:${process.env.PORT}`) as any;
let event: GuildMemberRemove;
let ws: WebSocket;
let user: UserDocument;
let user: SelfUserDocument;
let member: GuildMemberDocument;
let guild: GuildDocument;
@ -32,8 +32,7 @@ describe('guild-member-remove', () => {
});
it('cannot leave owned guild, rejected', async () => {
makeGuildOwner();
await makeGuildOwner();
await expect(guildMemberRemove()).to.be.rejectedWith('You cannot leave a guild you own');
});
@ -41,34 +40,36 @@ describe('guild-member-remove', () => {
await guildMemberRemove();
user = await User.findById(user.id);
expect(user.guilds).to.not.include(guild.id);
expect(user.guildIds).to.not.include(guild.id);
});
it('kick noob member, as noob member, missing permissions', async () => {
await makeAnotherNoob();
await expect(guildMemberRemove()).to.be.rejectedWith('Missing Permissions');
});
it('kick noob member, as guild owner, removed from user guilds', async () => {
makeGuildOwner();
await makeGuildOwner();
member = guild.members[1] as GuildMemberDocument;
member = await GuildMember.findOne({
guildId: guild.id,
userId: { $ne: guild.ownerId },
});
await guildMemberRemove();
user = await User.findById(member.userId);
expect(user.guilds).to.not.include(guild.id);
expect(user.guildIds).to.not.include(guild.id);
});
function guildMemberRemove() {
return event.invoke(ws, client, {
guildId: guild.id,
memberId: member.id,
});
return event.invoke(ws, client, { memberId: member.id });
}
function makeGuildOwner() {
member = guild.members[0] as GuildMemberDocument;
async function makeGuildOwner() {
const member = await GuildMember.findOne({
guildId: guild.id,
userId: guild.ownerId,
});
ws.sessions.set(client.id, guild.ownerId);
}

View File

@ -2,11 +2,12 @@ import { WebSocket } from '../../../src/ws/websocket';
import io from 'socket.io-client';
import GuildUpdate from '../../../src/ws/ws-events/guild-update';
import { Guild, GuildDocument } from '../../../src/data/models/guild';
import { UserDocument } from '../../../src/data/models/user';
import { SelfUserDocument, UserDocument } from '../../../src/data/models/user';
import { Mock } from '../../mock/mock';
import { expect } from 'chai';
import { GuildMemberDocument } from '../../../src/data/models/guild-member';
import { generateSnowflake } from '../../../src/data/snowflake-entity';
import { PermissionTypes } from '../../../src/types/permission-types';
describe('guild-update', () => {
const client = (io as any)(`http://localhost:${process.env.PORT}`) as any;
@ -14,11 +15,10 @@ describe('guild-update', () => {
let event: GuildUpdate;
let guild: GuildDocument;
let user: UserDocument;
let member: GuildMemberDocument;
beforeEach(async () => {
({ ws, event, guild, member, user } = await Mock.defaultSetup(client, GuildUpdate));
({ ws, event, guild, member } = await Mock.defaultSetup(client, GuildUpdate));
});
afterEach(async () => Mock.afterEach(ws));
@ -44,76 +44,10 @@ describe('guild-update', () => {
await expect(guildUpdate({})).to.be.fulfilled;
});
it('name acronym updated', async () => {
makeOwner();
await guildUpdate({ name: 'K E K K' });
guild = await Guild.findById(guild.id);
expect(guild.nameAcronym).to.equal('KEK');
});
it('contains banned keys, rejected', async () => {
makeOwner();
await expect(guildUpdate({ id: '123' })).to.be.rejectedWith('Contains readonly values');
});
it('reordered roles correctly, fulfilled', async () => {
makeOwner();
const roleIds = guild.roles.map(r => r.id);
await expect(guildUpdate({ roles: roleIds as any })).to.be.fulfilled;
});
it('reordered roles, added a role, rejected', async () => {
makeOwner();
const newRole = await Mock.role(guild);
const roleIds = guild.roles.concat(newRole).map(r => r.id);
await expect(guildUpdate({ roles: roleIds as any })).to.be.rejectedWith('Cannot add or remove roles this way');
});
it('reordered roles, removed a role, rejected', async () => {
makeOwner();
await Mock.role(guild);
const roleIds = guild.roles.slice(0, 1);
await expect(guildUpdate({ roles: roleIds as any })).to.be.rejectedWith('Cannot add or remove roles this way');
});
it('reordered roles, moved everyone role, rejected', async () => {
makeOwner();
const roleIds = [generateSnowflake(), ...guild.roles];
await expect(guildUpdate({ roles: roleIds as any })).to.be.rejectedWith('Cannot reorder the @everyone role');
});
it('sorted channels, did not add channels, fulfilled', async () => {
makeOwner();
const roleIds = guild.channels.map(r => r.id);
await expect(guildUpdate({ channels: roleIds as any })).to.be.fulfilled;
});
it('sorted channels, added a channel, rejected', async () => {
makeOwner();
const channel = await Mock.channel(guild);
const channelIds = guild.channels.concat(channel).map(r => r.id);
await expect(guildUpdate({ channels: channelIds as any })).to.be.rejectedWith('Cannot add or remove channels this way');
});
it('sorted channels, removed a channel, rejected', async () => {
makeOwner();
await Mock.channel(guild);
const channelIds = guild.channels.slice(0, 1);
await expect(guildUpdate({ channels: channelIds as any })).to.be.rejectedWith('Cannot add or remove channels this way');
});
function guildUpdate(partialGuild: Partial<Entity.Guild>) {
function guildUpdate(partial: Partial<Entity.Guild>) {
return event.invoke(ws, client, {
guildId: guild.id,
partialGuild,
...partial,
});
}

View File

@ -4,7 +4,8 @@ import io from 'socket.io-client';
import { Mock } from '../../mock/mock';
import { GuildDocument } from '../../../src/data/models/guild';
import { expect } from 'chai';
import { Role, RoleDocument } from '../../../src/data/models/role';
import { RoleDocument } from '../../../src/data/models/role';
import { PermissionTypes } from '../../../src/types/permission-types';
describe('invite-create', () => {
const client = (io as any)(`http://localhost:${process.env.PORT}`) as any;
@ -31,10 +32,10 @@ describe('invite-create', () => {
await expect(inviteCreate()).to.be.rejectedWith('Missing Permissions');
});
function inviteCreate() {
function inviteCreate(options?: Partial<InviteTypes.Options>) {
return event.invoke(ws, client, {
guildId: guild.id,
options: {},
options,
});
}
});

View File

@ -6,6 +6,7 @@ import { GuildDocument } from '../../../src/data/models/guild';
import { Invite, InviteDocument } from '../../../src/data/models/invite';
import { expect } from 'chai';
import { RoleDocument } from '../../../src/data/models/role';
import { PermissionTypes } from '../../../src/types/permission-types';
describe('invite-delete', () => {
const client = (io as any)(`http://localhost:${process.env.PORT}`) as any;
@ -38,7 +39,6 @@ describe('invite-delete', () => {
it('guild member, has manage guild perms, fulfilled', async () => {
await Mock.giveRolePerms(role, PermissionTypes.General.MANAGE_GUILD);
await expect(inviteDelete()).to.be.fulfilled;
});
@ -52,7 +52,6 @@ describe('invite-delete', () => {
it('invite does not exist, rejected', async () => {
await invite.deleteOne();
await expect(inviteDelete()).to.be.rejectedWith('Invite Not Found');
});

View File

@ -36,13 +36,11 @@ describe('message-create', () => {
it('user is guild member without chat perms, rejected', async () => {
await Mock.clearRolePerms(guild);
await expect(messageCreate()).to.be.rejectedWith('Missing Permissions');
});
it('user is guild owner, fulfilled', async () => {
ws.sessions.set(client.id, guild.ownerId);
await expect(messageCreate()).to.be.fulfilled;
});
@ -53,20 +51,19 @@ describe('message-create', () => {
expect(channel.lastMessageId).to.be.a('string');
});
it('lastReadMessages updated in author', async () => {
it('lastReadMessageIds updated on author document', async () => {
await messageCreate();
channel = await Channel.findById(channel.id) as any;
user = await User.findById(user.id) as any;
expect(user.lastReadMessages[channel.id]).to.equal(channel.lastMessageId);
expect(user.lastReadMessageIds[channel.id]).to.equal(channel.lastMessageId);
});
function messageCreate() {
function messageCreate(options?: Partial<Entity.Message>) {
return event.invoke(ws, client, {
channelId: channel.id,
partialMessage: {
content: 'hi'
},
content: 'hi',
...options,
});
}
});

View File

@ -7,7 +7,7 @@ import { GuildDocument } from '../../../src/data/models/guild';
import { UserDocument } from '../../../src/data/models/user';
import { Message, MessageDocument } from '../../../src/data/models/message';
import { generateSnowflake } from '../../../src/data/snowflake-entity';
import { Channel } from '../../../src/data/models/channel';
import { Channel, ChannelDocument } from '../../../src/data/models/channel';
describe('message-delete', () => {
const client = (io as any)(`http://localhost:${process.env.PORT}`) as any;
@ -17,13 +17,12 @@ describe('message-delete', () => {
let user: UserDocument;
let guild: GuildDocument;
let message: MessageDocument;
let channelId: string;
let channel: ChannelDocument;
beforeEach(async () => {
({ ws, event, user, guild } = await Mock.defaultSetup(client, MessageDelete));
({ ws, event, channel, guild } = await Mock.defaultSetup(client, MessageDelete));
channelId = guild.channels[0].id;
message = await Mock.message(user, channelId);
message = await Mock.message(user, channel.id);
});
afterEach(async () => await Mock.afterEach(ws));
@ -62,25 +61,24 @@ describe('message-delete', () => {
it('message does not exist, rejected', async () => {
await message.deleteOne();
await expect(deleteMessage()).to.be.rejectedWith('Message Not Found');
});
it('message is last channel message, channel last message updated to previous', async () => {
const previousMessage = message;
message = await Mock.message(user, channelId);
message = await Mock.message(user, channel.id);
await deleteMessage();
const channel = await Channel.findById(channelId);
expect(channel.lastMessageId).to.equal(previousMessage.id);
const newChannel = await Channel.findById(channel.id);
expect(newChannel.lastMessageId).to.equal(previousMessage.id);
});
it('message is only channel message, channel last message updated to null', async () => {
await deleteMessage();
const channel = await Channel.findById(channelId);
expect(channel.lastMessageId).to.be.null;
const newChannel = await Channel.findById(channel.id);
expect(newChannel.lastMessageId).to.be.null;
});
async function deleteMessage() {

View File

@ -1,15 +1,13 @@
import MessageUpdate from '../../../src/ws/ws-events/message-update';
import { WebSocket } from '../../../src/ws/websocket';
import Deps from '../../../src/utils/deps';
import { expect } from 'chai';
import io from 'socket.io-client';
import { Mock } from '../../mock/mock';
import { GuildDocument } from '../../../src/data/models/guild';
import { User, UserDocument } from '../../../src/data/models/user';
import { SelfUserDocument } from '../../../src/data/models/user';
import { ChannelDocument } from '../../../src/data/models/channel';
import { MessageDocument } from '../../../src/data/models/message';
import { generateSnowflake } from '../../../src/data/snowflake-entity';
import { Partial } from '../../../src/data/types/ws-types';
describe('message-update', () => {
const client = (io as any)(`http://localhost:${process.env.PORT}`) as any;
@ -17,7 +15,7 @@ describe('message-update', () => {
let channel: ChannelDocument;
let event: MessageUpdate;
let ws: WebSocket;
let user: UserDocument;
let user: SelfUserDocument;
let guild: GuildDocument;
let message: MessageDocument;
@ -30,30 +28,22 @@ describe('message-update', () => {
afterEach(async () => await Mock.afterEach(ws));
after(async () => await Mock.after(client));
it('user not author, rejected', async () => {
it('user not message author, rejected', async () => {
await makeGuildOwner();
await expect(messageUpdate()).to.be.rejectedWith('Unauthorized');
});
it('message does not exist, rejected', async () => {
message.id = generateSnowflake();
await expect(messageUpdate()).to.be.rejectedWith('Message Not Found');
});
it('update includes banned keys, rejected', async () => {
await expect(messageUpdate({ id: '123' })).to.be.rejectedWith('Contains readonly values');
await expect(messageUpdate({
messageId: generateSnowflake(),
})).to.be.rejectedWith('Message Not Found');
});
function messageUpdate(options?: Partial<Entity.Message>) {
return event.invoke(ws, client, {
messageId: message.id,
partialMessage: {
...options,
content: 'test',
},
withEmbed: true
...options,
});
}

View File

@ -7,8 +7,8 @@ import Users from '../../../src/data/users';
import { Mock } from '../../mock/mock';
import { WebSocket } from '../../../src/ws/websocket';
import io from 'socket.io-client';
import { SystemBot } from '../../../src/system/bot';
import { GuildDocument } from '../../../src/data/models/guild';
import { Channel } from '../../../src/data/models/channel';
describe('ready', () => {
const client = (io as any)(`http://localhost:${process.env.PORT}`) as any;
@ -16,7 +16,7 @@ describe('ready', () => {
let event: Ready;
let users: Users;
let token: string;
let user: UserDocument;
let user: SelfUserDocument;
let guild: GuildDocument;
let ws: WebSocket;
@ -45,25 +45,6 @@ describe('ready', () => {
expect(rooms()).to.include(user.id);
});
it('joins system bot room', async () => {
const systemBot = Deps.get<SystemBot>(SystemBot);
await systemBot.init();
await ready();
expect(rooms()).to.include(systemBot.self.id);
});
it('joins dm channel room', async () => {
const dm = await Mock.channel({ type: 'DM' });
dm.memberIds.push(user.id);
await dm.update(dm);
await ready();
expect(rooms()).to.include(dm.id);
});
it('joins self room', async () => {
await ready();
expect(rooms()).to.include(user.id);
@ -82,8 +63,11 @@ describe('ready', () => {
await makeOwner();
await ready();
expect(rooms()).to.contain(guild.channels[0].id);
expect(rooms()).to.contain(guild.channels[1].id);
const guildChannels = await Channel.find({ guildId: guild.id });
const ids = guildChannels.map(c => c.id);
expect(rooms()).to.contain(ids[0]);
expect(rooms()).to.contain(ids[1]);
expect(rooms()).to.contain(newChannel.id);
});

View File

@ -1,62 +0,0 @@
import RemoveFriend from '../../../src/ws/ws-events/remove-friend';
import { WebSocket } from '../../../src/ws/websocket';
import io from 'socket.io-client';
import { Mock } from '../../mock/mock';
import { expect } from 'chai';
import { generateSnowflake } from '../../../src/data/snowflake-entity';
import { SelfUserDocument, User, UserDocument } from '../../../src/data/models/user';
describe('remove-friend', () => {
const client = (io as any)(`http://localhost:${process.env.PORT}`) as any;
let event: RemoveFriend;
let ws: WebSocket;
let sender: SelfUserDocument;
let friend: SelfUserDocument;
beforeEach(async () => {
({ event, ws, user: sender as any } = await Mock.defaultSetup(client, RemoveFriend));
friend = await Mock.self();
});
afterEach(async () => await Mock.afterEach(ws));
after(async () => await Mock.after(client));
it('user sends request, fulfilled', async () => {
await expect(removeFriend()).to.be.fulfilled;
});
it('user removes self as friend, rejected', async () => {
friend.id = sender.id;
await expect(removeFriend()).to.be.rejectedWith('You cannot remove yourself as a friend');
});
it('removes non existing friend, rejected', async () => {
friend.id = generateSnowflake();
await expect(removeFriend()).to.be.rejectedWith('User Not Found');
});
it('sender cancels request, friend request removed', async () => {
await removeFriend();
sender = await User.findById(sender.id) as any;
expect(sender.friendRequestIds).to.be.empty;
});
it('sender removes friend, friend removed on both users', async () => {
await removeFriend();
friend = await User.findById(friend.id) as any;
sender = await User.findById(sender.id) as any;
expect(friend.friendIds.length).to.equal(0);
expect(sender.friendIds.length).to.equal(0);
});
async function removeFriend() {
return event.invoke(ws, client, { friendId: friend.id });
}
});

View File

@ -2,7 +2,7 @@ import UserUpdate from '../../../src/ws/ws-events/user-update';
import { WebSocket } from '../../../src/ws/websocket';
import io from 'socket.io-client';
import{ Mock } from '../../mock/mock';
import{ SelfUserDocument, User, UserDocument } from '../../../src/data/models/user';
import{ SelfUserDocument, User } from '../../../src/data/models/user';
import { expect } from 'chai';
import Deps from '../../../src/utils/deps';
import Users from '../../../src/data/users';

View File

@ -1,7 +1,7 @@
import { Channel, ChannelDocument } from '../../src/data/models/channel';
import { Guild, GuildDocument } from '../../src/data/models/guild';
import { GuildMember, GuildMemberDocument } from '../../src/data/models/guild-member';
import { User, SelfUserDocument, UserDocument } from '../../src/data/models/user';
import { User, SelfUserDocument } from '../../src/data/models/user';
import { generateSnowflake } from '../../src/data/snowflake-entity';
import { Role, RoleDocument } from '../../src/data/models/role';
import { Message } from '../../src/data/models/message';
@ -17,15 +17,17 @@ import GuildMembers from '../../src/data/guild-members';
import Channels from '../../src/data/channels';
import { PermissionTypes } from '../../src/types/permission-types';
import { REST } from '../../src/rest/server';
import Users from '../../src/data/users';
// TODO: mostly replace with data wrappers
// TODO: eventually replace with data wrappers
export class Mock {
private static channels = Deps.get<Channels>(Channels);
private static guilds = Deps.get<Guilds>(Guilds);
private static guildMembers = Deps.get<GuildMembers>(GuildMembers);
private static messages = Deps.get<Messages>(Messages);
private static invites = Deps.get<Invites>(Invites);
private static roles = Deps.get<Roles>(Roles);
public static channels = Deps.get<Channels>(Channels);
public static guilds = Deps.get<Guilds>(Guilds);
public static guildMembers = Deps.get<GuildMembers>(GuildMembers);
public static messages = Deps.get<Messages>(Messages);
public static invites = Deps.get<Invites>(Invites);
public static roles = Deps.get<Roles>(Roles);
public static users = Deps.get<Users>(Users);
public static async defaultSetup(client: any, eventType: any = function() {}) {
Deps.get<REST>(REST);
@ -46,6 +48,11 @@ export class Mock {
Mock.ioClient(client);
ws.sessions.set(client.id, user.id);
// TODO:
// rename 'user' -> ownerUser
// rename 'member' -> ownerMember
// add 'ownerUser'
// add 'ownerMember'
return { event, guild, user, member, ws, role, channel };
}
public static async afterEach(ws) {
@ -56,7 +63,7 @@ export class Mock {
client.disconnect();
}
// FIXME: garbage coding
// FIXME: less maintainable than daily YouTube uploads
public static ioClient(client: any) {
client.rooms = [];
client.sockets = {
@ -100,24 +107,15 @@ export class Mock {
username: `mock-user-${generateSnowflake()}`,
discriminator: 1,
...options,
} as any);
} as any) as any;
}
public static async self(guildIds: string[] = []) {
return await this.user({ guildIds }) as any as SelfUserDocument;
}
public static async bot(guildIds: string[] = []): Promise<SelfUserDocument> {
return await User.create({
bot: true,
email: `${generateSnowflake()}@gmail.com`,
guildIds,
status: 'ONLINE',
discriminator: 1,
username: `mock-bot-${generateSnowflake()}`,
} as any) as any as SelfUserDocument;
return await Mock.user({ bot: true, guildIds });
}
public static guildMember(user: SelfUserDocument, guild: GuildDocument): Promise<GuildMemberDocument> {
return this.guildMembers.create(guild.id, user);
}
@ -130,21 +128,15 @@ export class Mock {
public static invite(guildId: string, options?: InviteTypes.Options) {
return this.invites.create({ options, guildId }, generateSnowflake());
}
public static everyoneRole(guildId: string, permissions = PermissionTypes.defaultPermissions) {
return this.roles.create(guildId, { name: '@everyone', permissions });
}
public static async clearRolePerms(guild: Entity.Guild) {
await Role.updateMany(
{ guildId: guild.id },
{ permissions: 0 },
);
await Role.updateMany({ guildId: guild.id }, { permissions: 0 });
}
public static async giveRolePerms(role: RoleDocument, permissions: PermissionTypes.Permission) {
role.permissions |= permissions;
await role.save();
await role.updateOne({ permissions: role.permissions | permissions });
}
public static async giveEveryoneAdmin(guild: Entity.Guild) {

View File

@ -46,19 +46,19 @@ console.log(`${space(48 * 3)}TESTS${space(54 * 2)}`.bgWhite.black);
// TODO: import('./integration/ws/channel-delete.tests');
// TODO: import('./integration/ws/channel-update.tests');
import('./integration/ws/guild-member-add.tests'); //fail
// import('./integration/ws/guild-member-remove.tests'); //fail
// import('./integration/ws/guild-member-update.tests'); // fail
// import('./integration/ws/guild-create.tests'); // fail
// import('./integration/ws/guild-delete.tests'); // fail
// import('./integration/ws/guild-update.tests'); // fail
// import('./integration/ws/invite-create.tests'); //fail
// import('./integration/ws/invite-delete.tests'); //fail
// import('./integration/ws/message-create.tests'); //fail
// import('./integration/ws/message-update.tests'); // fail
// import('./integration/ws/message-delete.tests'); // fail
// import('./integration/ws/ready.tests');
import('./integration/ws/guild-member-remove.tests'); //fail
import('./integration/ws/guild-member-update.tests'); // fail
import('./integration/ws/guild-create.tests'); // fail
import('./integration/ws/guild-delete.tests'); // fail
import('./integration/ws/guild-update.tests'); // fail
import('./integration/ws/invite-create.tests'); //fail
import('./integration/ws/invite-delete.tests'); //fail
import('./integration/ws/message-create.tests'); //fail
import('./integration/ws/message-update.tests'); // fail
import('./integration/ws/message-delete.tests'); // fail
import('./integration/ws/ready.tests');
import('./integration/ws/user-update.tests');
// import('./integration/ws/ws-guard.tests'); // fail
import('./integration/ws/ws-guard.tests'); // fail
import('./unit/models/app.tests');
import('./unit/models/channel.tests');

View File

@ -21,16 +21,14 @@ describe('ws-cooldowns', () => {
});
it('handle, exceeds max cooldowns, throws error', () => {
for (let i = 0; i < 60; i++)
handle();
for (let i = 0; i < 60; i++) handle();
expect(handle).to.throw('You are doing too many things at once!');
});
it('handle, prunes old cooldowns', () => {
cooldowns.active.set(clientId, [
{ eventName: 'ADD_FRIEND', timestamp: new Date(0).getTime() },
{ eventName: 'ADD_FRIEND', timestamp: new Date(0).getTime() },
{ eventName: 'MESSAGE_CREATE', timestamp: new Date(0).getTime() },
{ eventName: 'MESSAGE_CREATE', timestamp: new Date(0).getTime() },
]);
handle();
@ -39,6 +37,6 @@ describe('ws-cooldowns', () => {
});
function handle() {
return cooldowns.handle(clientId, 'ADD_FRIEND');
return cooldowns.handle(clientId, 'MESSAGE_CREATE');
}
});

4
types/ws.d.ts vendored
View File

@ -163,8 +163,8 @@ declare namespace WS {
}
export interface GuildUpdate {
guildId: string;
name: string;
iconURL: string;
name?: string;
iconURL?: string;
}
export interface InviteCreate {
guildId: string;