Skip to content

Commit

Permalink
[cleanup] feat: Delete user cleanup api
Browse files Browse the repository at this point in the history
  • Loading branch information
helloitsdave committed May 18, 2024
1 parent f960cc5 commit 90caef3
Show file tree
Hide file tree
Showing 9 changed files with 113 additions and 9 deletions.
5 changes: 3 additions & 2 deletions backend/src/routes/userRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,9 @@ router.post('/api/users', async (req, res) => {
}
});

router.delete('/api/users/:id', authenticateToken, async (req, res) => {
const { id } = req.params;
router.delete('/api/users/', authenticateToken, async (req, res) => {

Check failure

Code scanning / CodeQL

Missing rate limiting High

This route handler performs
authorization
, but is not rate-limited.
// get id from decoded jwt token
const id = req.user.userId;

try {
await prisma.user.delete({ where: { id } });
Expand Down
4 changes: 2 additions & 2 deletions backend/tests/unit/users.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ describe('Delete User', () => {
updatedAt: new Date('2024-02-05T23:33:42.252Z'),
});
const response = await request(app).delete(
'/api/users/gcf89a7e-b941-4f17-bbe0-4e0c8b2cd272'
'/api/users/'
);
expect(response.status).toBe(204);
});
Expand All @@ -155,7 +155,7 @@ describe('Delete User', () => {
throw new Error('Test error');
});
const response = await request(app).delete(
'/api/users/gcf89a7e-b941-4f17-bbe0-4e0c8b2cd272'
'/api/users/'
);
expect(response.status).toBe(500);
expect(response.body).toStrictEqual({
Expand Down
8 changes: 6 additions & 2 deletions frontend/src/api/apiService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,11 @@ export const createUser = async ( user:
return response;
}

export const deleteUser = async (id: string) => {
const response = await api.delete(`users/${id}`);
export const deleteUser = async () => {
const response = await api.delete('users', {
headers: {
"Content-Type": "application/json",
},
});
return response;
}
31 changes: 31 additions & 0 deletions playwright/helpers/apiLogin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { request, expect, APIResponse } from '@playwright/test';
import getApiUrl from './getApiUrl';

const apiLogin = async (options: {
username: string;
password: string;
}): Promise<APIResponse> => {
if (!options.username || !options.password) {
throw new Error('Username and Password is required to login');
}

const context = await request.newContext({
baseURL: await getApiUrl(),
});

const loginResponse = await context.post('/api/login', {
headers: {
'Content-Type': 'application/json',
},
data: {
username: options.username,
password: options.password,
},
});

expect(loginResponse.ok()).toBeTruthy();
expect(loginResponse.status()).toBe(200);
return loginResponse;
};

export default apiLogin;
26 changes: 26 additions & 0 deletions playwright/helpers/deleteUser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { request, expect, APIResponse } from '@playwright/test';
import getApiUrl from './getApiUrl';

const deleteUser = async (token: string | null): Promise<APIResponse> => {
if (!token) {
throw new Error('Token is required to delete user');
}

const context = await request.newContext({
baseURL: await getApiUrl(),
});

const deleteResponse = await context.delete('/api/users', {
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
});

expect(deleteResponse.ok()).toBeTruthy();
expect(deleteResponse.status()).toBe(204);

return deleteResponse;
};

export default deleteUser;
17 changes: 17 additions & 0 deletions playwright/helpers/getApiUrl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import playwrightConfig from '../playwright.config';

const getApiUrl = async () => {
const baseURL = playwrightConfig.use?.baseURL;

const apiURL = baseURL?.includes('localhost')
? 'http://localhost:5000'
: 'https://notes-app-full-stack-be.onrender.com';

if (!baseURL) {
throw new Error('Base URL is required to make API requests');
}

return apiURL;
};

export default getApiUrl;
5 changes: 5 additions & 0 deletions playwright/pages/RegistrationPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class RegistrationPage {
accountHeader = () =>
this.page.getByRole('heading', { name: 'Register new account' });
errorMessage = () => this.page.locator('.registration-form-error');
spinnerContainer = () => this.page.getByTestId('spinner-container');

goto = async () => {
await this.page.goto('/register');
Expand Down Expand Up @@ -56,6 +57,10 @@ class RegistrationPage {
});
}
};

getLocalStorage = async () => {
return await this.page.evaluate(() => JSON.stringify(window.localStorage));
};
}

export default RegistrationPage;
24 changes: 22 additions & 2 deletions playwright/tests/registration.spec.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { test, expect, Page } from '@playwright/test';
import { test, expect, Page, APIResponse } from '@playwright/test';
import { faker } from '@faker-js/faker';
import { allure } from 'allure-playwright';
import RegistrationPage from '../pages/RegistrationPage';
import deleteUser from '../helpers/deleteUser';
import apiLogin from '../helpers/apiLogin';

let page: Page;
let registrationPage: RegistrationPage;
const timeout = 60 * 1000; // Render.com free tier may take 60 seconds to startup
let token: string;

const username = faker.internet.userName().toLowerCase();
const email = faker.internet.email();
Expand All @@ -19,7 +22,7 @@ test.beforeAll(async ({ browser }) => {
await registrationPage.goto();
});

test.describe('User Registration', async () => {
test.describe('User Registration', { tag: ['@PRODUCTION'] }, async () => {
test('Should be able to register new user', async () => {
await expect(registrationPage.accountHeader()).toBeVisible();

Expand All @@ -30,7 +33,24 @@ test.describe('User Registration', async () => {
});

await expect(registrationPage.successMessage()).toBeVisible({ timeout });

await test.step('Should be able to login with newly registered user', async () => {
const loginResponse: APIResponse = await apiLogin({ username, password });

expect(loginResponse.ok()).toBeTruthy();

const json = await loginResponse.json();
token = json.token;

expect(json.token).toBeTruthy();
});

await test.step('Cleanup: Delete User', async () => {
const deleteUserResponse = await deleteUser(token);
expect(deleteUserResponse.ok()).toBeTruthy();
});
});

test('Should not be able to register with existing username', async () => {
await registrationPage.goto();

Expand Down
2 changes: 1 addition & 1 deletion playwright/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"strict": true,
"skipLibCheck": true
},
"include": ["src", "tests", "pages", "./.eslintrc.js", "./playwright.config.ts"],
"include": ["src", "tests", "pages", "helpers", "./.eslintrc.js", "./playwright.config.ts", "cleanup/Cleanup.ts"],
"exclude": ["node_modules", "dist"],
"overrides": [
{
Expand Down

0 comments on commit 90caef3

Please sign in to comment.