chore: api tests (#1856)

This commit is contained in:
Catalin Pit
2025-07-10 05:56:46 +03:00
committed by GitHub
parent 106f796fea
commit cb73d21e05
4 changed files with 2008 additions and 0 deletions

View File

@ -0,0 +1,540 @@
import { expect, test } from '@playwright/test';
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
import { createApiToken } from '@documenso/lib/server-only/public-api/create-api-token';
import { prisma } from '@documenso/prisma';
import { FieldType } from '@documenso/prisma/client';
import {
seedBlankDocument,
seedCompletedDocument,
seedDraftDocument,
seedPendingDocumentWithFullFields,
} from '@documenso/prisma/seed/documents';
import { seedBlankTemplate } from '@documenso/prisma/seed/templates';
import { seedUser } from '@documenso/prisma/seed/users';
const WEBAPP_BASE_URL = NEXT_PUBLIC_WEBAPP_URL();
test.describe.configure({
mode: 'parallel',
});
test.describe('Document Access API V1', () => {
test('should block unauthorized access to documents not owned by the user', async ({
request,
}) => {
const { user: userA, team: teamA } = await seedUser();
const { user: userB, team: teamB } = await seedUser();
const { token: tokenB } = await createApiToken({
userId: userB.id,
teamId: teamB.id,
tokenName: 'userB',
expiresIn: null,
});
const documentA = await seedBlankDocument(userA, teamA.id);
// User B cannot access User A's document
const resB = await request.get(`${WEBAPP_BASE_URL}/api/v1/documents/${documentA.id}`, {
headers: { Authorization: `Bearer ${tokenB}` },
});
expect(resB.ok()).toBeFalsy();
expect(resB.status()).toBe(404);
});
test('should block unauthorized access to document download endpoint', async ({ request }) => {
const { user: userA, team: teamA } = await seedUser();
const { user: userB, team: teamB } = await seedUser();
const { token: tokenB } = await createApiToken({
userId: userB.id,
teamId: teamB.id,
tokenName: 'userB',
expiresIn: null,
});
const documentA = await seedCompletedDocument(userA, teamA.id, ['test@example.com'], {
createDocumentOptions: { title: 'Document 1 - Completed' },
});
const resB = await request.get(`${WEBAPP_BASE_URL}/api/v1/documents/${documentA.id}/download`, {
headers: { Authorization: `Bearer ${tokenB}` },
});
expect(resB.ok()).toBeFalsy();
expect(resB.status()).toBe(500);
});
test('should block unauthorized access to document delete endpoint', async ({ request }) => {
const { user: userA, team: teamA } = await seedUser();
const { user: userB, team: teamB } = await seedUser();
const { token: tokenB } = await createApiToken({
userId: userB.id,
teamId: teamB.id,
tokenName: 'userB',
expiresIn: null,
});
const documentA = await seedBlankDocument(userA, teamA.id);
const resB = await request.delete(`${WEBAPP_BASE_URL}/api/v1/documents/${documentA.id}`, {
headers: { Authorization: `Bearer ${tokenB}` },
});
expect(resB.ok()).toBeFalsy();
expect(resB.status()).toBe(404);
});
test('should block unauthorized access to document send endpoint', async ({ request }) => {
const { user: userA, team: teamA } = await seedUser();
const { user: userB, team: teamB } = await seedUser();
const { token: tokenB } = await createApiToken({
userId: userB.id,
teamId: teamB.id,
tokenName: 'userB',
expiresIn: null,
});
const { document: documentA } = await seedPendingDocumentWithFullFields({
owner: userA,
recipients: ['test@example.com'],
teamId: teamA.id,
});
const resB = await request.post(`${WEBAPP_BASE_URL}/api/v1/documents/${documentA.id}/send`, {
headers: { Authorization: `Bearer ${tokenB}` },
data: {},
});
expect(resB.ok()).toBeFalsy();
expect(resB.status()).toBe(500);
});
test('should block unauthorized access to document resend endpoint', async ({ request }) => {
const { user: userA, team: teamA } = await seedUser();
const { user: userB, team: teamB } = await seedUser();
const { token: tokenB } = await createApiToken({
userId: userB.id,
teamId: teamB.id,
tokenName: 'userB',
expiresIn: null,
});
const { user: recipientUser } = await seedUser();
const { document: documentA, recipients } = await seedPendingDocumentWithFullFields({
owner: userA,
recipients: [recipientUser.email],
teamId: teamA.id,
});
const resB = await request.post(`${WEBAPP_BASE_URL}/api/v1/documents/${documentA.id}/resend`, {
headers: { Authorization: `Bearer ${tokenB}` },
data: {
recipients: [recipients[0].id],
},
});
expect(resB.ok()).toBeFalsy();
expect(resB.status()).toBe(500);
});
test('should block unauthorized access to document recipients endpoint', async ({ request }) => {
const { user: userA, team: teamA } = await seedUser();
const { user: userB, team: teamB } = await seedUser();
const { token: tokenB } = await createApiToken({
userId: userB.id,
teamId: teamB.id,
tokenName: 'userB',
expiresIn: null,
});
const documentA = await seedBlankDocument(userA, teamA.id);
const resB = await request.post(
`${WEBAPP_BASE_URL}/api/v1/documents/${documentA.id}/recipients`,
{
headers: { Authorization: `Bearer ${tokenB}` },
data: { name: 'Test', email: 'test@example.com' },
},
);
expect(resB.ok()).toBeFalsy();
expect(resB.status()).toBe(401);
});
test('should block unauthorized access to PATCH on recipients endpoint', async ({ request }) => {
const { user: userA, team: teamA } = await seedUser();
const { user: userB, team: teamB } = await seedUser();
const { token: tokenB } = await createApiToken({
userId: userB.id,
teamId: teamB.id,
tokenName: 'userB',
expiresIn: null,
});
const { user: userRecipient } = await seedUser();
const documentA = await seedDraftDocument(userA, teamA.id, [userRecipient.email]);
const recipient = await prisma.recipient.findFirst({
where: {
documentId: documentA.id,
email: userRecipient.email,
},
});
const patchRes = await request.patch(
`${WEBAPP_BASE_URL}/api/v1/documents/${documentA.id}/recipients/${recipient!.id}`,
{
headers: { Authorization: `Bearer ${tokenB}` },
data: {
name: 'New Name',
email: 'new@example.com',
role: 'SIGNER',
signingOrder: null,
authOptions: {
accessAuth: [],
actionAuth: [],
},
},
},
);
expect(patchRes.ok()).toBeFalsy();
expect(patchRes.status()).toBe(401);
});
test('should block unauthorized access to DELETE on recipients endpoint', async ({ request }) => {
const { user: userA, team: teamA } = await seedUser();
const { user: userB, team: teamB } = await seedUser();
const { token: tokenB } = await createApiToken({
userId: userB.id,
teamId: teamB.id,
tokenName: 'userB',
expiresIn: null,
});
const { user: userRecipient } = await seedUser();
const documentA = await seedDraftDocument(userA, teamA.id, [userRecipient.email]);
const recipient = await prisma.recipient.findFirst({
where: {
documentId: documentA.id,
email: userRecipient.email,
},
});
const deleteRes = await request.delete(
`${WEBAPP_BASE_URL}/api/v1/documents/${documentA.id}/recipients/${recipient!.id}`,
{
headers: { Authorization: `Bearer ${tokenB}` },
data: {},
},
);
expect(deleteRes.ok()).toBeFalsy();
expect(deleteRes.status()).toBe(401);
});
test('should block unauthorized access to document fields endpoint', async ({ request }) => {
const { user: userA, team: teamA } = await seedUser();
const { user: userB, team: teamB } = await seedUser();
const { token: tokenB } = await createApiToken({
userId: userB.id,
teamId: teamB.id,
tokenName: 'userB',
expiresIn: null,
});
const { user: recipientUser } = await seedUser();
const documentA = await seedDraftDocument(userA, teamA.id, [recipientUser.email]);
const documentRecipient = await prisma.recipient.findFirst({
where: {
documentId: documentA.id,
email: recipientUser.email,
},
});
const resB = await request.post(`${WEBAPP_BASE_URL}/api/v1/documents/${documentA.id}/fields`, {
headers: { Authorization: `Bearer ${tokenB}` },
data: {
recipientId: documentRecipient!.id,
type: 'SIGNATURE',
pageNumber: 1,
pageX: 1,
pageY: 1,
pageWidth: 1,
pageHeight: 1,
},
});
expect(resB.ok()).toBeFalsy();
expect(resB.status()).toBe(404);
});
test('should block unauthorized access to template get endpoint', async ({ request }) => {
const { user: userA, team: teamA } = await seedUser();
const { user: userB, team: teamB } = await seedUser();
const { token: tokenB } = await createApiToken({
userId: userB.id,
teamId: teamB.id,
tokenName: 'userB',
expiresIn: null,
});
const templateA = await seedBlankTemplate(userA, teamA.id);
const resB = await request.get(`${WEBAPP_BASE_URL}/api/v1/templates/${templateA.id}`, {
headers: { Authorization: `Bearer ${tokenB}` },
});
expect(resB.ok()).toBeFalsy();
expect(resB.status()).toBe(404);
});
test('should block unauthorized access to template delete endpoint', async ({ request }) => {
const { user: userA, team: teamA } = await seedUser();
const { user: userB, team: teamB } = await seedUser();
const { token: tokenB } = await createApiToken({
userId: userB.id,
teamId: teamB.id,
tokenName: 'userB',
expiresIn: null,
});
const templateA = await seedBlankTemplate(userA, teamA.id);
const resB = await request.delete(`${WEBAPP_BASE_URL}/api/v1/templates/${templateA.id}`, {
headers: { Authorization: `Bearer ${tokenB}` },
});
expect(resB.ok()).toBeFalsy();
expect(resB.status()).toBe(404);
});
test('should block unauthorized access to PATCH on fields endpoint', async ({ request }) => {
const { user: userA, team: teamA } = await seedUser();
const { user: userB, team: teamB } = await seedUser();
const { token: tokenB } = await createApiToken({
userId: userB.id,
teamId: teamB.id,
tokenName: 'userB',
expiresIn: null,
});
const { user: userRecipient } = await seedUser();
const documentA = await seedDraftDocument(userA, teamA.id, [userRecipient.email]);
const recipient = await prisma.recipient.findFirst({
where: {
documentId: documentA.id,
email: userRecipient.email,
},
});
const field = await prisma.field.create({
data: {
documentId: documentA.id,
recipientId: recipient!.id,
type: FieldType.TEXT,
page: 1,
positionX: 5,
positionY: 5,
width: 10,
height: 5,
customText: '',
inserted: false,
fieldMeta: {
type: 'text',
label: 'Default Text Field',
},
},
});
const patchRes = await request.patch(
`${WEBAPP_BASE_URL}/api/v1/documents/${documentA.id}/fields/${field.id}`,
{
headers: { Authorization: `Bearer ${tokenB}` },
data: {
recipientId: recipient!.id,
type: FieldType.TEXT,
pageNumber: 1,
pageX: 99,
pageY: 99,
pageWidth: 99,
pageHeight: 99,
fieldMeta: {
type: 'text',
label: 'My new field',
},
},
},
);
expect(patchRes.ok()).toBeFalsy();
expect(patchRes.status()).toBe(401);
});
test('should block unauthorized access to DELETE on fields endpoint', async ({ request }) => {
const { user: userA, team: teamA } = await seedUser();
const { user: userB, team: teamB } = await seedUser();
const { token: tokenB } = await createApiToken({
userId: userB.id,
teamId: teamB.id,
tokenName: 'userB',
expiresIn: null,
});
const { user: userRecipient } = await seedUser();
const documentA = await seedDraftDocument(userA, teamA.id, [userRecipient.email]);
const recipient = await prisma.recipient.findFirst({
where: {
documentId: documentA.id,
email: userRecipient.email,
},
});
const field = await prisma.field.create({
data: {
documentId: documentA.id,
recipientId: recipient!.id,
type: FieldType.NUMBER,
page: 1,
positionX: 5,
positionY: 5,
width: 10,
height: 5,
customText: '',
inserted: false,
fieldMeta: {
type: 'number',
label: 'Default Number Field',
},
},
});
const deleteRes = await request.delete(
`${WEBAPP_BASE_URL}/api/v1/documents/${documentA.id}/fields/${field.id}`,
{
headers: { Authorization: `Bearer ${tokenB}` },
data: {},
},
);
expect(deleteRes.ok()).toBeFalsy();
expect(deleteRes.status()).toBe(401);
});
test('should block unauthorized access to documents list endpoint', async ({ request }) => {
const { user: userA, team: teamA } = await seedUser();
const { user: userB, team: teamB } = await seedUser();
const { token: tokenB } = await createApiToken({
userId: userB.id,
teamId: teamB.id,
tokenName: 'userB',
expiresIn: null,
});
await seedBlankDocument(userA, teamA.id);
const resB = await request.get(`${WEBAPP_BASE_URL}/api/v1/documents`, {
headers: { Authorization: `Bearer ${tokenB}` },
});
const reqData = await resB.json();
expect(resB.ok()).toBeTruthy();
expect(resB.status()).toBe(200);
expect(reqData.documents.every((doc: { userId: number }) => doc.userId !== userA.id)).toBe(
true,
);
expect(reqData.documents.length).toBe(0);
expect(reqData.totalPages).toBe(0);
});
test('should block unauthorized access to templates list endpoint', async ({ request }) => {
const { user: userA, team: teamA } = await seedUser();
const { user: userB, team: teamB } = await seedUser();
const { token: tokenB } = await createApiToken({
userId: userB.id,
teamId: teamB.id,
tokenName: 'userB',
expiresIn: null,
});
await seedBlankTemplate(userA, teamA.id);
const resB = await request.get(`${WEBAPP_BASE_URL}/api/v1/templates`, {
headers: { Authorization: `Bearer ${tokenB}` },
});
const reqData = await resB.json();
expect(resB.ok()).toBeTruthy();
expect(resB.status()).toBe(200);
expect(reqData.templates.every((tpl: { userId: number }) => tpl.userId !== userA.id)).toBe(
true,
);
expect(reqData.templates.length).toBe(0);
expect(reqData.totalPages).toBe(0);
});
test('should block unauthorized access to create-document-from-template endpoint', async ({
request,
}) => {
const { user: userA, team: teamA } = await seedUser();
const { user: userB, team: teamB } = await seedUser();
const { token: tokenB } = await createApiToken({
userId: userB.id,
teamId: teamB.id,
tokenName: 'userB',
expiresIn: null,
});
const templateA = await seedBlankTemplate(userA, teamA.id);
const resB = await request.post(
`${WEBAPP_BASE_URL}/api/v1/templates/${templateA.id}/create-document`,
{
headers: {
Authorization: `Bearer ${tokenB}`,
'Content-Type': 'application/json',
},
data: {
title: 'Should not work',
recipients: [{ name: 'Test user', email: 'test@example.com' }],
meta: {
subject: 'Test',
message: 'Test',
timezone: 'UTC',
dateFormat: 'yyyy-MM-dd',
redirectUrl: 'https://example.com',
},
},
},
);
expect(resB.ok()).toBeFalsy();
expect(resB.status()).toBe(401);
});
});

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,100 @@
import { expect, test } from '@playwright/test';
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
import {
seedBlankDocument,
seedCompletedDocument,
seedPendingDocument,
} from '@documenso/prisma/seed/documents';
import { seedUser } from '@documenso/prisma/seed/users';
import { apiSignin } from '../fixtures/authentication';
test.describe.configure({
mode: 'parallel',
});
test.describe('Unauthorized Access to Documents', () => {
test('should block unauthorized access to the draft document page', async ({ page }) => {
const { user, team } = await seedUser();
const document = await seedBlankDocument(user, team.id);
const { user: unauthorizedUser } = await seedUser();
await apiSignin({
page,
email: unauthorizedUser.email,
redirectPath: `/t/${team.url}/documents`,
});
await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}/t/${team.url}/documents/${document.id}`);
await expect(page.getByRole('heading', { name: 'Oops! Something went wrong.' })).toBeVisible();
});
test('should block unauthorized access to the draft document edit page', async ({ page }) => {
const { user, team } = await seedUser();
const document = await seedBlankDocument(user, team.id);
const { user: unauthorizedUser } = await seedUser();
await apiSignin({
page,
email: unauthorizedUser.email,
redirectPath: `/t/${team.url}/documents/${document.id}/edit`,
});
await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}/t/${team.url}/documents/${document.id}/edit`);
await expect(page.getByRole('heading', { name: 'Oops! Something went wrong.' })).toBeVisible();
});
test('should block unauthorized access to the pending document page', async ({ page }) => {
const { user, team } = await seedUser();
const { user: recipient } = await seedUser();
const document = await seedPendingDocument(user, team.id, [recipient]);
const { user: unauthorizedUser } = await seedUser();
await apiSignin({
page,
email: unauthorizedUser.email,
redirectPath: `/t/${team.url}/documents/${document.id}`,
});
await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}/t/${team.url}/documents/${document.id}`);
await expect(page.getByRole('heading', { name: 'Oops! Something went wrong.' })).toBeVisible();
});
test('should block unauthorized access to pending document edit page', async ({ page }) => {
const { user, team } = await seedUser();
const { user: recipient } = await seedUser();
const document = await seedPendingDocument(user, team.id, [recipient]);
const { user: unauthorizedUser } = await seedUser();
await apiSignin({
page,
email: unauthorizedUser.email,
redirectPath: `/t/${team.url}/documents/${document.id}/edit`,
});
await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}/t/${team.url}/documents/${document.id}/edit`);
await expect(page.getByRole('heading', { name: 'Oops! Something went wrong.' })).toBeVisible();
});
test('should block unauthorized access to completed document page', async ({ page }) => {
const { user, team } = await seedUser();
const { user: recipient } = await seedUser();
const document = await seedCompletedDocument(user, team.id, [recipient]);
const { user: unauthorizedUser } = await seedUser();
await apiSignin({
page,
email: unauthorizedUser.email,
redirectPath: `/t/${team.url}/documents/${document.id}`,
});
await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}/t/${team.url}/documents/${document.id}`);
await expect(page.getByRole('heading', { name: 'Oops! Something went wrong.' })).toBeVisible();
});
});

View File

@ -0,0 +1,45 @@
import { expect, test } from '@playwright/test';
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
import { seedBlankTemplate } from '@documenso/prisma/seed/templates';
import { seedUser } from '@documenso/prisma/seed/users';
import { apiSignin } from '../fixtures/authentication';
test.describe.configure({
mode: 'parallel',
});
test.describe('Unauthorized Access to Templates', () => {
test('should block unauthorized access to the template page', async ({ page }) => {
const { user, team } = await seedUser();
const template = await seedBlankTemplate(user, team.id);
const { user: unauthorizedUser } = await seedUser();
await apiSignin({
page,
email: unauthorizedUser.email,
redirectPath: `/t/${team.url}/templates/${template.id}`,
});
await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}/t/${team.url}/templates/${template.id}`);
await expect(page.getByRole('heading', { name: 'Oops! Something went wrong.' })).toBeVisible();
});
test('should block unauthorized access to the template edit page', async ({ page }) => {
const { user, team } = await seedUser();
const template = await seedBlankTemplate(user, team.id);
const { user: unauthorizedUser } = await seedUser();
await apiSignin({
page,
email: unauthorizedUser.email,
redirectPath: `/t/${team.url}/templates/${template.id}/edit`,
});
await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}/t/${team.url}/templates/${template.id}/edit`);
await expect(page.getByRole('heading', { name: 'Oops! Something went wrong.' })).toBeVisible();
});
});