diff --git a/src/presentation/http/router/note.test.ts b/src/presentation/http/router/note.test.ts index 2f475ac5..e33fd540 100644 --- a/src/presentation/http/router/note.test.ts +++ b/src/presentation/http/router/note.test.ts @@ -1787,6 +1787,83 @@ describe('Note API', () => { expect(response?.json().parentNote.id).toBe(newParentNote.publicId); }); + test.each([ + /** Returns 403 if user is not in the team of the new parent note */ + { + role: null, + }, + + /** Returns 403 if user is team member of the new parent note with a Read role */ + { + role: MemberRole.Read, + }, + ]) + ('Returns 403 when user can not edit the new parent note', async ({ role }) => { + /* create test user - creator of the new parent note */ + const owner = await global.db.insertUser(); + + /* create test child note */ + const childNote = await global.db.insertNote({ + creatorId: user.id, + }); + + /* create test parent note */ + const parentNote = await global.db.insertNote({ + creatorId: user.id, + }); + + /* create test note of another user, that will be new parent for the child note */ + const newParentNote = await global.db.insertNote({ + creatorId: owner.id, + }); + + /* create note settings for child note */ + await global.db.insertNoteSetting({ + noteId: childNote.id, + isPublic: true, + }); + + /* create test relation */ + await global.db.insertNoteRelation({ + noteId: childNote.id, + parentId: parentNote.id, + }); + + /* create test team of the new parent note */ + if (role !== null) { + await global.db.insertNoteTeam({ + noteId: newParentNote.id, + userId: user.id, + role: role, + }); + } + + let response = await global.api?.fakeRequest({ + method: 'PATCH', + headers: { + authorization: `Bearer ${accessToken}`, + }, + body: { + parentNoteId: newParentNote.publicId, + }, + url: `/note/${childNote.publicId}/relation`, + }); + + expect(response?.statusCode).toBe(403); + + expect(response?.json().message).toStrictEqual('You don\'t have permission to edit the parent note'); + + response = await global.api?.fakeRequest({ + method: 'GET', + headers: { + authorization: `Bearer ${accessToken}`, + }, + url: `/note/${childNote.publicId}`, + }); + + expect(response?.json().parentNote.id).toBe(parentNote.publicId); + }); + test('Returns 400 when parent is the same as child', async () => { /* create test child note */ const childNote = await global.db.insertNote({ @@ -1820,7 +1897,7 @@ describe('Note API', () => { expect(response?.json().message).toStrictEqual(`Forbidden relation. Note can't be a child of own child`); }); - test('Return 400 when parent note does not exist', async () => { + test('Return 406 when parent note does not exist', async () => { const nonExistentParentId = '47L43yY7dp'; const childNote = await global.db.insertNote({ @@ -1838,9 +1915,9 @@ describe('Note API', () => { url: `/note/${childNote.publicId}/relation`, }); - expect(response?.statusCode).toBe(400); + expect(response?.statusCode).toBe(406); - expect(response?.json().message).toStrictEqual('Incorrect parent note'); + expect(response?.json().message).toStrictEqual('Parent note not found'); }); test('Return 400 when circular reference occurs', async () => { @@ -1925,6 +2002,72 @@ describe('Note API', () => { expect(response?.json().parentNote.id).toBe(parentNote.publicId); }); + test.each([ + /** Returns 403 if user is not in the team of the parent note */ + { + role: null, + }, + + /** Returns 403 if user is team member of the parent note with a Read role */ + { + role: MemberRole.Read, + }, + ]) + ('Returns 403 when user can not edit the parent note', async ({ role }) => { + /* create test user - creator of the parent note */ + const owner = await global.db.insertUser(); + + /* create test child note */ + const childNote = await global.db.insertNote({ + creatorId: user.id, + }); + + /* create test note of another user, that will be parent for the child note */ + const parentNote = await global.db.insertNote({ + creatorId: owner.id, + }); + + /* create note settings for child note */ + await global.db.insertNoteSetting({ + noteId: childNote.id, + isPublic: true, + }); + + /* create test team of the parent note */ + if (role !== null) { + await global.db.insertNoteTeam({ + noteId: parentNote.id, + userId: user.id, + role: role, + }); + } + + let response = await global.api?.fakeRequest({ + method: 'POST', + headers: { + authorization: `Bearer ${accessToken}`, + }, + body: { + parentNoteId: parentNote.publicId, + }, + url: `/note/${childNote.publicId}/relation`, + }); + + expect(response?.statusCode).toBe(403); + + expect(response?.json().message).toStrictEqual('You don\'t have permission to edit the parent note'); + + response = await global.api?.fakeRequest({ + method: 'GET', + headers: { + authorization: `Bearer ${accessToken}`, + }, + url: `/note/${childNote.publicId}`, + }); + + expect(response?.json().parentNote).toBeUndefined(); + }); + test('Returns 400 when note already has parent note', async () => { /* create test child note */ const childNote = await global.db.insertNote({ @@ -1985,7 +2128,7 @@ describe('Note API', () => { expect(response?.json().message).toStrictEqual(`Forbidden relation. Note can't be a child of own child`); }); - test('Return 400 when parent note does not exist', async () => { + test('Return 406 when parent note does not exist', async () => { const nonExistentParentId = '47L43yY7dp'; const childNote = await global.db.insertNote({ @@ -2003,9 +2146,9 @@ describe('Note API', () => { url: `/note/${childNote.publicId}/relation`, }); - expect(response?.statusCode).toBe(400); + expect(response?.statusCode).toBe(406); - expect(response?.json().message).toStrictEqual('Incorrect parent note Id'); + expect(response?.json().message).toStrictEqual('Parent note not found'); }); test('Return 400 when circular reference occurs', async () => { diff --git a/src/presentation/http/router/note.ts b/src/presentation/http/router/note.ts index 383b86e3..7dfc05fe 100644 --- a/src/presentation/http/router/note.ts +++ b/src/presentation/http/router/note.ts @@ -442,10 +442,12 @@ const NoteRouter: FastifyPluginCallback = (fastify, opts, don policy: [ 'authRequired', 'userCanEdit', + 'userCanEditParentNote', ], }, preHandler: [ noteResolver, + parentNoteResolver, ], }, async (request, reply) => { const noteId = request.note?.id as number; @@ -497,10 +499,12 @@ const NoteRouter: FastifyPluginCallback = (fastify, opts, don policy: [ 'authRequired', 'userCanEdit', + 'userCanEditParentNote', ], }, preHandler: [ noteResolver, + parentNoteResolver, ], }, async (request, reply) => { const noteId = request.note?.id as number;