From eed01925ae73e1b45f05d2555e289440178c1faf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 19 Jul 2025 21:30:03 +0000 Subject: [PATCH 1/3] Initial plan From feca65f512b09b7f0114ba6a0da52f42aa08386e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 19 Jul 2025 21:39:47 +0000 Subject: [PATCH 2/3] Add admin access control tests for POST /api/country endpoints Co-authored-by: gulfaraz <3880591+gulfaraz@users.noreply.github.com> --- .../dto/upload-typhoon-track.dto.ts | 3 +- tests/integration/helpers/utility.helper.ts | 33 ++++++ tests/integration/tests/all.test.ts | 2 + .../country/country-admin-access.test.ts | 107 ++++++++++++++++++ 4 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 tests/integration/tests/country/country-admin-access.test.ts diff --git a/tests/integration/helpers/API-service/dto/upload-typhoon-track.dto.ts b/tests/integration/helpers/API-service/dto/upload-typhoon-track.dto.ts index cfde082bd..d440b5811 100644 --- a/tests/integration/helpers/API-service/dto/upload-typhoon-track.dto.ts +++ b/tests/integration/helpers/API-service/dto/upload-typhoon-track.dto.ts @@ -8,7 +8,8 @@ export interface UploadTyphoonTrackDto { readonly date: Date; } -export enum TyphoonCategory { // https://www.pagasa.dost.gov.ph/information/about-tropical-cyclone +export enum TyphoonCategory { + // https://www.pagasa.dost.gov.ph/information/about-tropical-cyclone TropicalDepression = 'TD', // a tropical cyclone with maximum sustained winds of up to 62 kilometers per hour (kph) or less than 34 nautical miles per hour (knots) TropicalStorm = 'TS', // a tropical cyclone with maximum wind speed of 62 to 88 kph or 34 - 47 knots // eslint-disable-next-line perfectionist/sort-enums diff --git a/tests/integration/helpers/utility.helper.ts b/tests/integration/helpers/utility.helper.ts index 026d6af1b..1ba241954 100644 --- a/tests/integration/helpers/utility.helper.ts +++ b/tests/integration/helpers/utility.helper.ts @@ -31,6 +31,39 @@ export async function getToken() { return token; } +export async function getNonAdminToken() { + // First get admin token to create a non-admin user + const adminToken = await getToken(); + + const nonAdminUser = { + email: 'operator@redcross.nl', + firstName: 'Test', + lastName: 'Operator', + userRole: 'operator', + countryCodesISO3: ['UGA'], + disasterTypes: ['floods'], + password: 'password', + }; + + // Try to create the user (will fail if it already exists, which is fine) + try { + await api(adminToken).post('/user').send(nonAdminUser); + } catch (error) { + // User already exists, continue + } + + // Login with the non-admin user + const { + body: { + user: { token }, + }, + } = await api() + .post(`/user/login`) + .send({ email: nonAdminUser.email, password: nonAdminUser.password }); + + return token; +} + export async function reset() { const token = await getToken(); diff --git a/tests/integration/tests/all.test.ts b/tests/integration/tests/all.test.ts index 5d307d508..9a3f40b23 100644 --- a/tests/integration/tests/all.test.ts +++ b/tests/integration/tests/all.test.ts @@ -5,6 +5,7 @@ import { adminAreaDeleteTests } from './admin-areas/admin-areas-delete.test'; import adminAreaAggregatesTests from './admin-areas/aggregates.test'; import eventAdminAreaTests from './admin-areas/event-admin-areas.test'; import communityNotificationTests from './community-notification/community-notification.test'; +import countryAdminAccessTests from './country/country-admin-access.test'; import createCountryTests from './country/create-country.test'; import emailTests from './email/emails.test'; import getEventsTests from './events/get-events.test'; @@ -22,6 +23,7 @@ describe('integration tests', () => { communityNotificationTests(); createCountryTests(); + countryAdminAccessTests(); emailTests(); diff --git a/tests/integration/tests/country/country-admin-access.test.ts b/tests/integration/tests/country/country-admin-access.test.ts new file mode 100644 index 000000000..7a7b6c033 --- /dev/null +++ b/tests/integration/tests/country/country-admin-access.test.ts @@ -0,0 +1,107 @@ +import { countryData } from '../../fixtures/country.const'; +import { notificationInfoData } from '../../fixtures/notification-info.const'; +import { getNonAdminToken, getToken } from '../../helpers/utility.helper'; +import { + addOrUpdateCountries, + addOrUpdateNotificationInfo, +} from './country.api'; + +export default function countryAdminAccessTests() { + describe('country admin access control', () => { + let adminToken: string; + let nonAdminToken: string; + + beforeAll(async () => { + adminToken = await getToken(); + nonAdminToken = await getNonAdminToken(); + }); + + describe('POST /country - admin access required', () => { + it('should allow admin users to create/update countries', async () => { + // Arrange + const testCountryData = structuredClone(countryData); + + // Act + const result = await addOrUpdateCountries( + { countries: testCountryData }, + adminToken, + ); + + // Assert + expect(result.status).toBe(201); + }); + + it('should deny non-admin users access to create/update countries', async () => { + // Arrange + const testCountryData = structuredClone(countryData); + + // Act + const result = await addOrUpdateCountries( + { countries: testCountryData }, + nonAdminToken, + ); + + // Assert + expect(result.status).toBe(403); + }); + + it('should deny unauthenticated users access to create/update countries', async () => { + // Arrange + const testCountryData = structuredClone(countryData); + + // Act + const result = await addOrUpdateCountries( + { countries: testCountryData }, + '', // empty token + ); + + // Assert + expect(result.status).toBe(401); + }); + }); + + describe('POST /country/notification-info - admin access required', () => { + it('should allow admin users to create/update notification info', async () => { + // Arrange + const testNotificationData = structuredClone(notificationInfoData); + + // Act + const result = await addOrUpdateNotificationInfo( + testNotificationData, + adminToken, + ); + + // Assert + expect(result.status).toBe(201); + }); + + it('should deny non-admin users access to create/update notification info', async () => { + // Arrange + const testNotificationData = structuredClone(notificationInfoData); + + // Act + const result = await addOrUpdateNotificationInfo( + testNotificationData, + nonAdminToken, + ); + + // Assert + expect(result.status).toBe(403); + }); + + it('should deny unauthenticated users access to create/update notification info', async () => { + // Arrange + const testNotificationData = structuredClone(notificationInfoData); + + // Act + const result = await addOrUpdateNotificationInfo( + testNotificationData, + '', // empty token + ); + + // Assert + expect(result.status).toBe(401); + }); + }); + }); +} From ba797c6f78ab688e3cb797b895e61402b6579bcb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 19 Jul 2025 21:42:44 +0000 Subject: [PATCH 3/3] Fix imports and improve user role handling in admin access tests Co-authored-by: gulfaraz <3880591+gulfaraz@users.noreply.github.com> --- tests/integration/helpers/utility.helper.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/integration/helpers/utility.helper.ts b/tests/integration/helpers/utility.helper.ts index 1ba241954..05a1da72a 100644 --- a/tests/integration/helpers/utility.helper.ts +++ b/tests/integration/helpers/utility.helper.ts @@ -8,6 +8,7 @@ import { MalariaScenario, TyphoonScenario, } from './API-service/enum/mock-scenario.enum'; +import { UserRole } from './API-service/enum/user-role.enum'; export function api(token?: string) { const request = agent(process.env.API_SERVICE_URL); @@ -39,7 +40,7 @@ export async function getNonAdminToken() { email: 'operator@redcross.nl', firstName: 'Test', lastName: 'Operator', - userRole: 'operator', + userRole: UserRole.Operator, countryCodesISO3: ['UGA'], disasterTypes: ['floods'], password: 'password',