Skip to content

Commit

Permalink
Improve testing framework, add tests for eudr admin-regions
Browse files Browse the repository at this point in the history
  • Loading branch information
alexeh committed Feb 29, 2024
1 parent be6d97f commit 11577d0
Show file tree
Hide file tree
Showing 8 changed files with 311 additions and 47 deletions.
1 change: 0 additions & 1 deletion api/src/modules/import-data/eudr/eudr.import.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,6 @@ export class EudrImportService {
const fakedCartoOutput: any[] = data.map((row: any) =>
this.generateFakeAlerts(row),
);
console.log(fakedCartoOutput);

const parsed: any = await new AsyncParser({
fields: [
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { AdminRegionTestManager } from './fixtures';

describe('Admin Regions EUDR Filters (e2e)', () => {
let testManager: AdminRegionTestManager;

beforeAll(async () => {
testManager = await AdminRegionTestManager.load();
});

beforeEach(async () => {
await testManager.refreshState();
});

afterEach(async () => {
await testManager.clearDatabase();
});

afterAll(async () => {
await testManager.close();
});
describe('EUDR Admin Regions Filters', () => {
it('should only get admin-regions that are part of EUDR data', async () => {
await testManager.GivenAdminRegionsOfSourcingLocations();
const { eudrAdminRegions } = await testManager.GivenEUDRAdminRegions();
await testManager.WhenIRequestEUDRAdminRegions();
testManager.ThenIShouldOnlyReceiveCorrespondingAdminRegions(
eudrAdminRegions,
);
});
it('should only get admin-regions that are part of EUDR data and are filtered', async () => {
const { sourcingLocations } =
await testManager.GivenAdminRegionsOfSourcingLocations();
await testManager.AndAssociatedMaterials(sourcingLocations);
await testManager.AndAssociatedSuppliers([sourcingLocations[0]]);
const { eudrAdminRegions, eudrSourcingLocations } =
await testManager.GivenEUDRAdminRegions();
const eudrMaterials = await testManager.AndAssociatedMaterials([
eudrSourcingLocations[0],
]);
const eudrSuppliers = await testManager.AndAssociatedSuppliers(
eudrSourcingLocations,
);
await testManager.WhenIRequestEUDRAdminRegions({
'materialIds[]': [eudrMaterials[0].id],
'producerIds[]': eudrSuppliers.map((s) => s.id),
});
testManager.ThenIShouldOnlyReceiveCorrespondingAdminRegions([
eudrAdminRegions[0],
]);
});
});
});
97 changes: 97 additions & 0 deletions api/test/e2e/admin-regions/fixtures.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import {
LOCATION_TYPES,
SourcingLocation,
} from 'modules/sourcing-locations/sourcing-location.entity';
import { createAdminRegion, createSourcingLocation } from '../../entity-mocks';
import { AdminRegion } from 'modules/admin-regions/admin-region.entity';
import { AndAssociatedMaterials } from '../../common-steps/and-associated-materials';
import { AndAssociatedSuppliers } from '../../common-steps/and-associated-suppliers';
import { TestManager } from '../../utils/test-manager';

export class AdminRegionTestManager extends TestManager {
url = '/api/v1/admin-regions/';

constructor(manager: TestManager) {
super(manager.testApp, manager.jwtToken, manager.dataSource);
}

static async load() {
return new AdminRegionTestManager(await this.createManager());
}

GivenAdminRegionsOfSourcingLocations = async () => {
const adminRegion = await createAdminRegion({
name: 'Regular AdminRegion',
});
const adminRegion2 = await createAdminRegion({
name: 'Regular AdminRegion 2',
});
const sourcingLocation1 = await createSourcingLocation({
adminRegionId: adminRegion.id,
});
const sourcingLocation2 = await createSourcingLocation({
adminRegionId: adminRegion2.id,
});
return {
adminRegions: [adminRegion, adminRegion2],
sourcingLocations: [sourcingLocation1, sourcingLocation2],
};
};
AndAssociatedMaterials = async (
sourcingLocations: SourcingLocation[],
materialNames?: string[],
) => {
const materials = await this.createMaterials(materialNames);
await AndAssociatedMaterials(materials, sourcingLocations);
return materials;
};
AndAssociatedSuppliers = async (
sourcingLocations: SourcingLocation[],
supplierNames?: string[],
) => {
const suppliers = await this.createSuppliers(supplierNames);
await AndAssociatedSuppliers(suppliers, sourcingLocations);
return suppliers;
};
GivenEUDRAdminRegions = async () => {
const adminRegion = await createAdminRegion({
name: 'EUDR AdminRegion',
});
const adminRegion2 = await createAdminRegion({
name: 'EUDR AdminRegion 2',
});
const eudrSourcingLocation1 = await createSourcingLocation({
adminRegionId: adminRegion.id,
locationType: LOCATION_TYPES.EUDR,
});
const eudrSourcingLocation2 = await createSourcingLocation({
adminRegionId: adminRegion2.id,
locationType: LOCATION_TYPES.EUDR,
});
return {
eudrAdminRegions: [adminRegion, adminRegion2],
eudrSourcingLocations: [eudrSourcingLocation1, eudrSourcingLocation2],
};
};
WhenIRequestEUDRAdminRegions = async (filters?: {
'producerIds[]'?: string[];
'materialIds[]'?: string[];
}) => {
return this.GET({ url: `${this.url}trees/eudr`, query: filters });
};

ThenIShouldOnlyReceiveCorrespondingAdminRegions = (
eudrAdminRegions: AdminRegion[],
) => {
expect(this.response!.status).toBe(200);
expect(this.response!.body.data.length).toBe(eudrAdminRegions.length);
for (const adminRegion of eudrAdminRegions) {
expect(
this.response!.body.data.find(
(adminRegionResponse: AdminRegion) =>
adminRegionResponse.id === adminRegion.id,
),
).toBeDefined();
}
};
}
4 changes: 2 additions & 2 deletions api/test/utils/application-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ export default class ApplicationManager {

const testingModuleBuilder: TestingModuleBuilder =
initTestingModuleBuilder ||
(await Test.createTestingModule({
Test.createTestingModule({
imports: [AppModule],
})
.overrideProvider('IEmailService')
.useClass(MockEmailService));
.useClass(MockEmailService);

ApplicationManager.testApplication.moduleFixture =
await testingModuleBuilder.compile();
Expand Down
8 changes: 8 additions & 0 deletions api/test/utils/random-name.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export const randomName = (length = 10): string => {
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
let result = '';
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * characters.length));
}
return result;
};
99 changes: 99 additions & 0 deletions api/test/utils/test-manager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { DataSource } from 'typeorm';
import ApplicationManager, { TestApplication } from './application-manager';
import { clearTestDataFromDatabase } from './database-test-helper';
import { setupTestUser } from './userAuth';
import * as request from 'supertest';
import {
createGeoRegion,
createMaterial,
createSupplier,
} from '../entity-mocks';
import { Material } from 'modules/materials/material.entity';
import { Supplier } from 'modules/suppliers/supplier.entity';
import { GeoRegion } from 'modules/geo-regions/geo-region.entity';
import { randomName } from './random-name';

export class TestManager {
testApp: TestApplication;
jwtToken: string;
dataSource: DataSource;
response?: request.Response;
materials?: Material[];
suppliers?: Supplier[];
geoRegions?: GeoRegion[];

constructor(app: TestApplication, jwtToken: string, dataSource: DataSource) {
this.testApp = app;
this.jwtToken = jwtToken;
this.dataSource = dataSource;
}

static async createManager() {
const testApplication = await ApplicationManager.init();
const dataSource = testApplication.get<DataSource>(DataSource);
const { jwtToken } = await setupTestUser(testApplication);
return new TestManager(testApplication, jwtToken, dataSource);
}

async refreshState() {
const { jwtToken } = await setupTestUser(this.testApp);
this.jwtToken = jwtToken;
this.materials = undefined;
this.suppliers = undefined;
this.geoRegions = undefined;
this.response = undefined;
}

async clearDatabase() {
await clearTestDataFromDatabase(this.dataSource);
}

async GET(options: { url: string; query?: object | string }) {
this.response = await request(this.testApp.getHttpServer())
.get(options.url)
.query(options?.query || '')
.set('Authorization', `Bearer ${this.token}`);
return this.response;
}

get token() {
if (!this.jwtToken) {
throw new Error('TestManager has no token available!');
}
return this.jwtToken;
}

async close() {
await this.testApp.close();
}

async createMaterials(names?: string[]) {
const namesToCreate = names || [randomName()];
const createdMaterials: Material[] = [];
for (let i = 0; i < namesToCreate.length; i++) {
createdMaterials.push(await createMaterial({ name: namesToCreate[i] }));
}
this.materials?.push(...createdMaterials);
return createdMaterials;
}

async createSuppliers(names?: string[]) {
const namesToCreate = names || [randomName()];
const createdSuppliers: Supplier[] = [];
for (let i = 0; i < namesToCreate.length; i++) {
createdSuppliers.push(await createSupplier({ name: namesToCreate[i] }));
}
this.suppliers?.push(...createdSuppliers);
return createdSuppliers;
}

async createGeoRegion(names?: string[]) {
const namesToCreate = names || [randomName()];
const createdGeoRegions: GeoRegion[] = [];
for (let i = 0; i < namesToCreate.length; i++) {
createdGeoRegions.push(await createGeoRegion({ name: namesToCreate[i] }));
}
this.geoRegions?.push(...createdGeoRegions);
return createdGeoRegions;
}
}
36 changes: 21 additions & 15 deletions api/test/utils/userAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { ROLES } from 'modules/authorization/roles/roles.enum';
import { User } from 'modules/users/user.entity';
import { EntityManager } from 'typeorm';
import { TestApplication } from './application-manager';
import { faker } from '@faker-js/faker';
import { Permission } from '../../src/modules/authorization/permissions/permissions.entity';
import { PERMISSIONS } from '../../src/modules/authorization/permissions/permissions.enum';

Expand All @@ -15,34 +14,41 @@ export type TestUser = { jwtToken: string; user: User; password: string };
export async function setupTestUser(
applicationManager: TestApplication,
roleName: ROLES = ROLES.ADMIN,
extraData: Partial<User> = {},
extraData: Partial<User> = { password: 'Password123!' },
): Promise<TestUser> {
const salt = await genSalt();
const role = new Role();
role.name = roleName;
const entityManager = applicationManager.get<EntityManager>(EntityManager);
const userRepository = entityManager.getRepository(User);

const { password: extraDataPassword, ...restOfExtraData } = extraData;

const password = extraDataPassword ?? faker.internet.password();
const { password, ...restOfExtraData } = extraData;

await setUpRolesAndPermissions(entityManager);

const user = await userRepository.save({
...E2E_CONFIG.users.signUp,
salt,
password: await hash(password, salt),
isActive: true,
isDeleted: false,
roles: [role],
...restOfExtraData,
let existingUser = await userRepository.findOne({
where: { email: E2E_CONFIG.users.signUp.email },
});
if (!existingUser) {
existingUser = await userRepository.save({
...E2E_CONFIG.users.signUp,
salt,
password: await hash(password!, salt),
isActive: true,
isDeleted: false,
roles: [role],
});
}

const response = await request(applicationManager.application.getHttpServer())
.post('/auth/sign-in')
.send({ username: user.email, password: password });
.send({ username: existingUser.email, password: password });

return { jwtToken: response.body.accessToken, user, password };
return {
jwtToken: response.body.accessToken,
user: existingUser,
password: password!,
};
}

async function setUpRolesAndPermissions(
Expand Down
Loading

0 comments on commit 11577d0

Please sign in to comment.