diff --git a/apps/backend/src/tests/e2e/access-control.test.ts b/apps/backend/src/tests/e2e/access-control.test.ts new file mode 100644 index 0000000..0fe2c01 --- /dev/null +++ b/apps/backend/src/tests/e2e/access-control.test.ts @@ -0,0 +1,145 @@ +// apps/backend/src/tests/e2e/access-control.test.ts +// Path: apps/backend/src/tests/e2e/access-control.test.ts + +import { describe, expect, test, beforeAll } from 'bun:test'; + +describe('E2E: Access Control', () => { + let user1Cookie: string; + let user1Id: string; + let user2Cookie: string; + let user2Id: string; + let linkSaveId: number; + let linkShareUrl: string; + let publicSaveId: number; + + beforeAll(async () => { + // Создаем первого пользователя + const user1SignUp = await fetch('http://localhost:3000/auth/api/sign-up/email', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + name: 'User 1', + email: `user1-${Date.now()}@example.com`, + password: 'Password123!', + }), + }); + + const user1Data = await user1SignUp.json(); + user1Id = user1Data.user.id; + user1Cookie = user1SignUp.headers.get('set-cookie') || ''; + + // Создаем второго пользователя + const user2SignUp = await fetch('http://localhost:3000/auth/api/sign-up/email', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + name: 'User 2', + email: `user2-${Date.now()}@example.com`, + password: 'Password123!', + }), + }); + + const user2Data = await user2SignUp.json(); + user2Id = user2Data.user.id; + user2Cookie = user2SignUp.headers.get('set-cookie') || ''; + + // User 1 создает сейв с visibility: link + const linkSaveResponse = await fetch('http://localhost:3000/saves/external', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Cookie': user1Cookie, + }, + body: JSON.stringify({ + url: 'https://httpbin.org/image/png', + name: 'Link Save', + visibility: 'link', + }), + }); + + const linkSaveData = await linkSaveResponse.json(); + linkSaveId = linkSaveData.id; + linkShareUrl = linkSaveData.shareUrl; + + // User 1 создает публичный сейв + const publicSaveResponse = await fetch('http://localhost:3000/saves/external', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Cookie': user1Cookie, + }, + body: JSON.stringify({ + url: 'https://httpbin.org/image/jpeg', + name: 'Public Save', + visibility: 'public', + }), + }); + + const publicSaveData = await publicSaveResponse.json(); + publicSaveId = publicSaveData.id; + }); + + test('owner should access link save', async () => { + const response = await fetch(`http://localhost:3000/saves/${linkSaveId}`, { + headers: { 'Cookie': user1Cookie }, + }); + + expect(response.status).toBe(200); + }); + + test('non-owner should NOT access link save without share token', async () => { + const response = await fetch(`http://localhost:3000/saves/${linkSaveId}`, { + headers: { 'Cookie': user2Cookie }, + }); + + expect(response.status).toBe(404); + }); + + test('non-owner should access link save WITH share token', async () => { + const response = await fetch( + `http://localhost:3000/saves/${linkSaveId}?share=${linkShareUrl}`, + { + headers: { 'Cookie': user2Cookie }, + } + ); + + expect(response.status).toBe(200); + + const data = await response.json(); + expect(data.id).toBe(linkSaveId); + }); + + test('anyone should access public save', async () => { + const response = await fetch(`http://localhost:3000/saves/${publicSaveId}`); + + expect(response.status).toBe(200); + + const data = await response.json(); + expect(data.id).toBe(publicSaveId); + expect(data.visibility).toBe('public'); + }); + + test('non-owner should NOT be able to update save', async () => { + const response = await fetch(`http://localhost:3000/saves/${publicSaveId}`, { + method: 'PATCH', + headers: { + 'Content-Type': 'application/json', + 'Cookie': user2Cookie, + }, + body: JSON.stringify({ + name: 'Hacked Name', + }), + }); + + expect(response.status).toBeGreaterThanOrEqual(400); + }); + + test('non-owner should NOT be able to delete save', async () => { + const response = await fetch(`http://localhost:3000/saves/${publicSaveId}`, { + method: 'DELETE', + headers: { 'Cookie': user2Cookie }, + }); + + expect(response.status).toBeGreaterThanOrEqual(400); + }); +});