Skip to content

Commit

Permalink
#58 make sure to check whether given userId or storyId belong to room…
Browse files Browse the repository at this point in the history
… (preconditions)
  • Loading branch information
xeronimus@gmail.com authored and xeronimus@gmail.com committed Jun 18, 2020
1 parent 226a672 commit 1a8df29
Show file tree
Hide file tree
Showing 15 changed files with 162 additions and 22 deletions.
6 changes: 3 additions & 3 deletions server/src/commandHandlers/changeStory.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/**
* A user changes the title and/or description of a story
*/
import {throwIfStoryIdNotFoundInRoom} from './commonPreconditions';

const changeStoryCommandHandler = {
preCondition: (room, command) => {
if (!room.getIn(['stories', command.payload.storyId])) {
throw new Error('Cannot change unknown story ' + command.payload.storyId);
}
throwIfStoryIdNotFoundInRoom(room, command.payload.storyId);
},
fn: (room, command) => {
room.applyEvent('storyChanged', command.payload);
Expand Down
11 changes: 11 additions & 0 deletions server/src/commandHandlers/commonPreconditions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export function throwIfUserIdNotFoundInRoom(room, userId) {
if (!room.getIn(['users', userId])) {
throw new Error(`Given user ${userId} does not belong to room ${room.get('id')}`);
}
}

export function throwIfStoryIdNotFoundInRoom(room, storyId) {
if (!room.getIn(['stories', storyId])) {
throw new Error(`Given story ${storyId} does not belong to room ${room.get('id')}`);
}
}
6 changes: 3 additions & 3 deletions server/src/commandHandlers/deleteStory.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import {throwIfStoryIdNotFoundInRoom} from './commonPreconditions';

/**
* A user deletes a story
*/
const deleteStoryCommandHandler = {
preCondition: (room, command) => {
if (!room.getIn(['stories', command.payload.storyId])) {
throw new Error('Cannot delete unknown story ' + command.payload.storyId);
}
throwIfStoryIdNotFoundInRoom(room, command.payload.storyId);
},
fn: (room, command) => {
room.applyEvent('storyDeleted', command.payload);
Expand Down
10 changes: 8 additions & 2 deletions server/src/commandHandlers/giveStoryEstimate.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,19 @@
* A user that is marked as excluded (see toggleExclude/excludedFromEstimations) cannot give estimations
* As soon as all users (that can estimate) estimated the story, a "revealed" event is produced
*/
import {throwIfStoryIdNotFoundInRoom} from './commonPreconditions';

const giveStoryEstimateCommandHandler = {
preCondition: (room, command, userId) => {
if (room.get('selectedStory') !== command.payload.storyId) {
const storyId = command.payload.storyId;

throwIfStoryIdNotFoundInRoom(room, storyId);

if (room.get('selectedStory') !== storyId) {
throw new Error('Can only give estimation for currently selected story!');
}

if (room.getIn(['stories', command.payload.storyId, 'revealed'])) {
if (room.getIn(['stories', storyId, 'revealed'])) {
throw new Error('You cannot give an estimate for a story that was revealed!');
}

Expand Down
11 changes: 3 additions & 8 deletions server/src/commandHandlers/selectStory.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
/**
* A user selected a story (marked it as the "current" story to estimate)
*/
import {throwIfStoryIdNotFoundInRoom} from './commonPreconditions';

const selectStoryCommandHandler = {
preCondition: (room, command) => {
// check that id in payload is one of the stories in room
if (!room.getIn(['stories', command.payload.storyId])) {
throw new Error(
`Story ${command.payload.storyId} cannot be selected. It is not part of room ${room.get(
'id'
)}`
);
}
throwIfStoryIdNotFoundInRoom(room, command.payload.storyId);
},
fn: (room, command) => {
room.applyEvent('storySelected', {storyId: command.payload.storyId});
Expand Down
5 changes: 5 additions & 0 deletions server/src/commandHandlers/setEmail.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import {throwIfUserIdNotFoundInRoom} from './commonPreconditions';

/**
* A user sets his email address (used for Gravatar image fetching. See https://en.gravatar.com/ ).
*/
module.exports = {
preCondition: (room, command, userId) => {
throwIfUserIdNotFoundInRoom(room, userId);
},
fn: (room, command) => {
room.applyEvent('emailSet', command.payload);
}
Expand Down
5 changes: 5 additions & 0 deletions server/src/commandHandlers/setUsername.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import {throwIfUserIdNotFoundInRoom} from './commonPreconditions';

/**
* A user sets his username.
*/
const setUsernameCommandHandler = {
preCondition: (room, command, userId) => {
throwIfUserIdNotFoundInRoom(room, userId);
},
fn: (room, command) => {
room.applyEvent('usernameSet', command.payload);
}
Expand Down
8 changes: 6 additions & 2 deletions server/src/commandHandlers/toggleExclude.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
/**
* A user can exclude himself from estimations.
* Emits event "excludedFromEstimations" or "includedInEstimations" on toggle
*
* If user is marked as excluded, he cannot estimate stories.
* (If user is marked as excluded, he cannot estimate stories.)
*
*/
import {throwIfUserIdNotFoundInRoom} from './commonPreconditions';

const toggleExcludeCommandHandler = {
preCondition: (room, command, userId) => {
throwIfUserIdNotFoundInRoom(room, userId);
},
fn: (room, command, userId) => {
if (room.getIn(['users', userId, 'excluded'])) {
room.applyEvent('includedInEstimations', {});
Expand Down
4 changes: 3 additions & 1 deletion server/test/unit/commands/changeStoryTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ describe('preconditions', () => {
},
userIdOne
)
).rejects.toThrow('Cannot change unknown story some-unknown-story');
).rejects.toThrow(
/Precondition Error during "changeStory": Given story some-unknown-story does not belong to room .*/
);
});
});
4 changes: 3 additions & 1 deletion server/test/unit/commands/deleteStoryTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ describe('preconditions', () => {
},
userId
)
).rejects.toThrow('Cannot delete unknown story some-unknown-story');
).rejects.toThrow(
/Precondition Error during "deleteStory": Given story some-unknown-story does not belong to room .*/
);
});
});
44 changes: 43 additions & 1 deletion server/test/unit/commands/giveStoryEstimateTest.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import {v4 as uuid} from 'uuid';
import Immutable from 'immutable';

import {prepTwoUsersInOneRoomWithOneStory} from '../testUtils';

test('Should produce storyEstimateGiven event', async () => {
Expand Down Expand Up @@ -268,6 +270,44 @@ test('Should produce additional "revealed" and "consensusAchieved" events if all

describe('preconditions', () => {
test('Should throw if storyId does not match "selectedStory"', async () => {
const {
roomId,
userIdOne: userId,
processor,
mockRoomsStore
} = await prepTwoUsersInOneRoomWithOneStory();

const secondStoryId = uuid();

mockRoomsStore.manipulate((room) =>
room.setIn(
['stories', secondStoryId],
new Immutable.Map({
id: secondStoryId,
title: 'second story'
})
)
);

return expect(
processor(
{
id: uuid(),
roomId: roomId,
name: 'giveStoryEstimate',
payload: {
storyId: secondStoryId,
value: 2
}
},
userId
)
).rejects.toThrow(
/Precondition Error during "giveStoryEstimate": Can only give estimation for currently selected story/
);
});

test('Should throw if storyId does not belong to room', async () => {
const {roomId, userIdOne: userId, processor} = await prepTwoUsersInOneRoomWithOneStory();

return expect(
Expand All @@ -283,7 +323,9 @@ describe('preconditions', () => {
},
userId
)
).rejects.toThrow('Can only give estimation for currently selected story!');
).rejects.toThrow(
/Precondition Error during "giveStoryEstimate": Given story unknown does not belong to room .*/
);
});

test('Should throw if story already revealed', async () => {
Expand Down
2 changes: 1 addition & 1 deletion server/test/unit/commands/selectStoryTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ describe('preconditions', () => {
userId
)
).rejects.toThrow(
'Precondition Error during "selectStory": Story story-not-in-room cannot be selected. It is not part of room'
/Precondition Error during "selectStory": Given story story-not-in-room does not belong to room .*/
);
});
});
22 changes: 22 additions & 0 deletions server/test/unit/commands/setEmailTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,26 @@ describe('preconditions', () => {
)
).rejects.toThrow('Format validation failed (must be a valid email-address) in /payload/email');
});

test('Should fail, if userId does not match user in room', async () => {
const {processor, roomId} = await prepOneUserInOneRoom();
const commandId = uuid();
const nonMatchingUserId = uuid();

return expect(
processor(
{
id: commandId,
roomId: roomId,
name: 'setEmail',
payload: {
email: 'test@test.com'
}
},
nonMatchingUserId
)
).rejects.toThrow(
/Precondition Error during "setEmail": Given user .* does not belong to room .*/
);
});
});
24 changes: 24 additions & 0 deletions server/test/unit/commands/setUsernameTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,27 @@ test('Should produce usernameSet event', async () => {
expect(room.users[userId].username).toEqual('John Doe');
});
});

describe('preconditions', () => {
test('Should fail, if userId does not match user in room', async () => {
const {processor, roomId} = await prepOneUserInOneRoom();
const commandId = uuid();
const nonMatchingUserId = uuid();

return expect(
processor(
{
id: commandId,
roomId: roomId,
name: 'setUsername',
payload: {
username: 'John Doe'
}
},
nonMatchingUserId
)
).rejects.toThrow(
/Precondition Error during "setUsername": Given user .* does not belong to room .*/
);
});
});
22 changes: 22 additions & 0 deletions server/test/unit/commands/toggleExcludeTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,25 @@ test('toggleExclude -> included', async () => {
expect(room.users[userId].excluded).toBe(false);
});
});

describe('precondition', () => {
test('Should fail, if userId does not match user in room', async () => {
const {processor, roomId} = await prepOneUserInOneRoom();
const commandId = uuid();
const nonMatchingUserId = uuid();

return expect(
processor(
{
id: commandId,
roomId: roomId,
name: 'toggleExclude',
payload: {}
},
nonMatchingUserId
)
).rejects.toThrow(
/Precondition Error during "toggleExclude": Given user .* does not belong to room .*/
);
});
});

0 comments on commit 1a8df29

Please sign in to comment.