Skip to content

Commit

Permalink
Add unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
gabriellsh committed Oct 31, 2024
1 parent 97701ee commit 897cef3
Show file tree
Hide file tree
Showing 2 changed files with 183 additions and 10 deletions.
55 changes: 46 additions & 9 deletions apps/meteor/server/routes/avatar/middlewares/auth.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const mocks = {
},
};

const { protectAvatarsWithFallback } = proxyquire.noCallThru().load('./auth.ts', {
const { protectAvatarsWithFallback, protectAvatars } = proxyquire.noCallThru().load('./auth.ts', {
'../utils': mocks.utils,
});

Expand Down Expand Up @@ -54,33 +54,70 @@ describe('#protectAvatarsWithFallback()', () => {
expect(response.end.calledOnce).to.be.true;
});

it(`should write 200 to head and write fallback to body (user avatar)`, async () => {
it(`should write 200 to head and write fallback to body (room avatar)`, async () => {
mocks.utils.renderSVGLetters.returns('fallback');

await protectAvatarsWithFallback({ url: '/jon' }, response, next);
await protectAvatarsWithFallback({ url: '/room/jon' }, response, next);
expect(next.called).to.be.false;
expect(response.setHeader.called).to.be.false;
expect(response.writeHead.calledWith(200, { 'Content-Type': 'image/svg+xml' })).to.be.true;
expect(response.write.calledWith('fallback')).to.be.true;
expect(response.end.calledOnce).to.be.true;
});

it(`should write 200 to head and write fallback to body (room avatar)`, async () => {
mocks.utils.renderSVGLetters.returns('fallback');
it(`should call next if user can access avatar`, async () => {
mocks.utils.userCanAccessAvatar.returns(true);
const request = { url: '/jon' };

await protectAvatarsWithFallback(request, response, next);
expect(mocks.utils.userCanAccessAvatar.calledWith(request)).to.be.true;
expect(next.called).to.be.true;
});
});

describe('#protectAvatars()', () => {
const response = {
setHeader: sinon.spy(),
writeHead: sinon.spy(),
write: sinon.spy(),
end: sinon.spy(),
};
const next = sinon.spy();

afterEach(() => {
response.setHeader.resetHistory();
response.writeHead.resetHistory();
response.end.resetHistory();
next.resetHistory();

Object.values(mocks.utils).forEach((mock) => mock.reset());
});

it(`should write 404 to head if no url provided`, async () => {
await protectAvatars({}, response, next);

await protectAvatarsWithFallback({ url: '/room/jon' }, response, next);
expect(next.called).to.be.false;
expect(response.setHeader.called).to.be.false;
expect(response.writeHead.calledWith(200, { 'Content-Type': 'image/svg+xml' })).to.be.true;
expect(response.write.calledWith('fallback')).to.be.true;
expect(response.writeHead.calledWith(404)).to.be.true;
expect(response.end.calledOnce).to.be.true;
});

it(`should write 404 to head if access is denied`, async () => {
mocks.utils.userCanAccessAvatar.returns(false);

await protectAvatars({ url: '/room/jon' }, response, next);

expect(next.called).to.be.false;
expect(response.setHeader.called).to.be.false;
expect(response.writeHead.calledWith(404)).to.be.true;
expect(response.end.calledOnce).to.be.true;
});

it(`should call next if user can access avatar`, async () => {
mocks.utils.userCanAccessAvatar.returns(true);
const request = { url: '/jon' };

await protectAvatarsWithFallback(request, response, next);
await protectAvatars(request, response, next);
expect(mocks.utils.userCanAccessAvatar.calledWith(request)).to.be.true;
expect(next.called).to.be.true;
});
Expand Down
138 changes: 137 additions & 1 deletion apps/meteor/server/routes/avatar/user.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import sinon from 'sinon';
const mocks = {
settingsGet: sinon.stub(),
findOneByUsernameIgnoringCase: sinon.stub(),
findOneById: sinon.stub(),
utils: {
serveSvgAvatarInRequestedFormat: sinon.spy(),
wasFallbackModified: sinon.stub(),
Expand All @@ -15,15 +16,18 @@ const mocks = {
},
serverFetch: sinon.stub(),
avatarFindOneByName: sinon.stub(),
avatarFindOneByUserId: sinon.stub(),
};

const { userAvatarByUsername } = proxyquire.noCallThru().load('./user', {
const { userAvatarById, userAvatarByUsername } = proxyquire.noCallThru().load('./user', {
'@rocket.chat/models': {
Users: {
findOneByUsernameIgnoringCase: mocks.findOneByUsernameIgnoringCase,
findOneById: mocks.findOneById,
},
Avatars: {
findOneByName: mocks.avatarFindOneByName,
findOneByUserId: mocks.avatarFindOneByUserId,
},
},
'../../../app/settings/server': {
Expand All @@ -37,6 +41,138 @@ const { userAvatarByUsername } = proxyquire.noCallThru().load('./user', {
},
});

describe('#userAvatarById()', () => {
const response = {
setHeader: sinon.spy(),
writeHead: sinon.spy(),
end: sinon.spy(),
};
const next = sinon.spy();

afterEach(() => {
mocks.settingsGet.reset();
mocks.avatarFindOneByUserId.reset();

response.setHeader.resetHistory();
response.writeHead.resetHistory();
response.end.resetHistory();
next.resetHistory();

Object.values(mocks.utils).forEach((mock) => ('reset' in mock ? mock.reset() : mock.resetHistory()));
});

it(`should do nothing if url is not in request object`, async () => {
await userAvatarById({}, response, next);
expect(next.called).to.be.false;
expect(response.setHeader.called).to.be.false;
expect(response.writeHead.called).to.be.false;
expect(response.end.called).to.be.false;
});

it(`should write 404 if Id is not provided`, async () => {
await userAvatarById({ url: '/' }, response, next);
expect(next.called).to.be.false;
expect(response.setHeader.called).to.be.false;
expect(response.writeHead.calledWith(404)).to.be.true;
expect(response.end.calledOnce).to.be.true;
});

it(`should call external provider`, async () => {
const userId = 'xvf5Tr34';
const request = { url: '/' + userId };

const pipe = sinon.spy();
const mockResponseHeaders = new Headers();
mockResponseHeaders.set('header1', 'true');
mockResponseHeaders.set('header2', 'false');

mocks.serverFetch.returns({
headers: mockResponseHeaders,
body: { pipe },
});

mocks.settingsGet.returns('test123/{username}');

mocks.findOneById.returns({ username: 'jon' });

await userAvatarById(request, response, next);

expect(mocks.utils.setCacheAndDispositionHeaders.calledWith(request, response)).to.be.true;
expect(mocks.findOneById.calledWith(userId)).to.be.true;
expect(mocks.serverFetch.calledWith('test123/jon')).to.be.true;
expect(response.setHeader.calledTwice).to.be.true;
expect(response.setHeader.getCall(0).calledWith('header1', 'true')).to.be.true;
expect(response.setHeader.getCall(1).calledWith('header2', 'false')).to.be.true;
expect(pipe.calledWith(response)).to.be.true;
});

it(`should serve avatar file if found`, async () => {
const request = { url: '/jon' };

const file = { uploadedAt: new Date(0), type: 'image/png', size: 100 };
mocks.avatarFindOneByUserId.returns(file);

await userAvatarById(request, response, next);

expect(mocks.utils.setCacheAndDispositionHeaders.calledWith(request, response)).to.be.true;
expect(mocks.utils.serveAvatarFile.calledWith(file, request, response, next)).to.be.true;
});

it(`should write 304 to head if content is not modified`, async () => {
const request = { url: '/xyzabc', headers: {} };

mocks.utils.wasFallbackModified.returns(false);

await userAvatarById(request, response, next);

expect(mocks.utils.setCacheAndDispositionHeaders.calledWith(request, response)).to.be.true;
expect(response.writeHead.calledWith(304)).to.be.true;
expect(response.end.calledOnce).to.be.true;
});

it(`should write 404 if username is not found`, async () => {
mocks.utils.wasFallbackModified.returns(true);
mocks.findOneById.returns(null);

const userId = 'awdasdaw';
const request = { url: '/' + userId, headers: {} };

await userAvatarById(request, response, next);
expect(mocks.utils.setCacheAndDispositionHeaders.calledWith(request, response)).to.be.true;

expect(response.writeHead.calledWith(404)).to.be.true;
expect(response.end.calledOnce).to.be.true;
});

it(`should fallback to SVG if no avatar found`, async () => {
const userId = '2apso9283';
const request = { url: '/' + userId, headers: {} };

mocks.findOneById.returns({ username: 'jon' });
mocks.utils.wasFallbackModified.returns(true);

await userAvatarById(request, response, next);

expect(mocks.findOneById.calledWith(userId)).to.be.true;
expect(mocks.utils.setCacheAndDispositionHeaders.calledWith(request, response)).to.be.true;
expect(mocks.utils.serveSvgAvatarInRequestedFormat.calledWith({ nameOrUsername: 'jon', req: request, res: response })).to.be.true;
});

it(`should fallback to SVG with user name if UI_Use_Name_Avatar is true`, async () => {
const userId = '2apso9283';
const request = { url: '/' + userId, headers: {} };

mocks.findOneById.returns({ username: 'jon', name: 'Doe' });
mocks.utils.wasFallbackModified.returns(true);
mocks.settingsGet.withArgs('UI_Use_Name_Avatar').returns(true);

await userAvatarById(request, response, next);

expect(mocks.utils.setCacheAndDispositionHeaders.calledWith(request, response)).to.be.true;
expect(mocks.utils.serveSvgAvatarInRequestedFormat.calledWith({ nameOrUsername: 'Doe', req: request, res: response })).to.be.true;
});
});

describe('#userAvatarByUsername()', () => {
const response = {
setHeader: sinon.spy(),
Expand Down

0 comments on commit 897cef3

Please sign in to comment.