From 7b762b52cff8cf1cd1c1b8bb0a2fdbbe0e57dc26 Mon Sep 17 00:00:00 2001 From: yulia-bel Date: Wed, 4 Oct 2023 15:39:31 +0200 Subject: [PATCH] Add geo test for Cost Surface Piece Exporter --- .../test/integration/cloning/fixtures.ts | 51 ++++++ ...ct-cost-surface.piece-exporter.e2e-spec.ts | 160 ++++++++++++++++++ 2 files changed, 211 insertions(+) create mode 100644 api/apps/geoprocessing/test/integration/cloning/piece-exporters/project-cost-surface.piece-exporter.e2e-spec.ts diff --git a/api/apps/geoprocessing/test/integration/cloning/fixtures.ts b/api/apps/geoprocessing/test/integration/cloning/fixtures.ts index 236747b047..603bea1377 100644 --- a/api/apps/geoprocessing/test/integration/cloning/fixtures.ts +++ b/api/apps/geoprocessing/test/integration/cloning/fixtures.ts @@ -22,6 +22,7 @@ import { Readable, Transform } from 'stream'; import { DeepPartial, EntityManager, In } from 'typeorm'; import { v4 } from 'uuid'; import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity'; +import { CostSurfacePuDataEntity } from "@marxan/cost-surfaces"; export type TestSpecification = { id: string; @@ -595,6 +596,56 @@ export async function GivenOutputScenarioFeaturesData( .execute(); } +export async function GivenCostSurfaces( + em: EntityManager, + min: number, + max: number, + name: string, + projectId: string, +) { + const costSurface = Array({ + id: v4(), min, max, name, project_id: projectId, + }) + + await Promise.all( + costSurface.map((values) => + em + .createQueryBuilder() + .insert() + .into('cost_surfaces') + .values(values) + .execute(), + ), + ); + + return costSurface[0]; +} + +export async function GivenCostSurfaceData( + em: EntityManager, + projectId: string, + costSurfaceId: string, +): Promise<{ id: string; hash: string; feature_id: string }[]> { + + const projectPus = await GivenProjectPus(em, projectId, 10); + const insertValues = projectPus.map((pu, index) => ({ + id: v4(), + costSurfaceId: costSurfaceId, + projectsPuId: pu.id, + cost: index + 1, + + })); + + const result = await em + .createQueryBuilder() + .insert() + .into(CostSurfacePuDataEntity) + .values(insertValues) + .returning(['id', 'cost', 'costSurfaceId']) + .execute(); + + return result.raw; +} export async function GivenSpecifications( em: EntityManager, featuresIds: string[], diff --git a/api/apps/geoprocessing/test/integration/cloning/piece-exporters/project-cost-surface.piece-exporter.e2e-spec.ts b/api/apps/geoprocessing/test/integration/cloning/piece-exporters/project-cost-surface.piece-exporter.e2e-spec.ts new file mode 100644 index 0000000000..2ee1755bcf --- /dev/null +++ b/api/apps/geoprocessing/test/integration/cloning/piece-exporters/project-cost-surface.piece-exporter.e2e-spec.ts @@ -0,0 +1,160 @@ +import { geoprocessingConnections } from '@marxan-geoprocessing/ormconfig'; +import { ClonePiece, ExportJobInput } from '@marxan/cloning'; +import { ResourceKind } from '@marxan/cloning/domain'; +import { ProjectCustomFeaturesContent } from '@marxan/cloning/infrastructure/clone-piece-data/project-custom-features'; +import { CloningFilesRepository } from '@marxan/cloning-files-repository'; +import { GeoFeatureGeometry } from '@marxan/geofeatures'; +import { FixtureType } from '@marxan/utils/tests/fixture-type'; +import { Test } from '@nestjs/testing'; +import { getEntityManagerToken, TypeOrmModule } from '@nestjs/typeorm'; +import { isLeft, Right } from 'fp-ts/lib/Either'; +import { Readable } from 'stream'; +import { EntityManager, In } from 'typeorm'; +import { v4 } from 'uuid'; +import { + DeleteProjectAndOrganization, GivenCostSurfaceData, GivenCostSurfaces, + GivenFeatures, + GivenFeaturesData, + GivenProjectExists, + readSavedFile +} from "../fixtures"; +import { GeoCloningFilesRepositoryModule } from '@marxan-geoprocessing/modules/cloning-files-repository'; +import { FakeLogger } from '@marxan-geoprocessing/utils/__mocks__/fake-logger'; +import { + ProjectCostSurfacesPieceExporter +} from "@marxan-geoprocessing/export/pieces-exporters/project-cost-surfaces.piece-exporter"; +import { CostSurfacePuDataEntity } from "@marxan/cost-surfaces"; +import { ProjectCostSurfacesContent } from "@marxan/cloning/infrastructure/clone-piece-data/project-cost-surfaces"; + +let fixtures: FixtureType; + +describe(ProjectCostSurfacesPieceExporter, () => { + beforeEach(async () => { + fixtures = await getFixtures(); + }, 10_000); + + afterEach(async () => { + await fixtures?.cleanUp(); + }); + + it('saves successfully cost surfaces data', async () => { + const input = fixtures.GivenAProjectCostSurfacesExportJob(); + await fixtures.GivenProjectExist(); + await fixtures.GivenCostSurfacesForProject(); + await fixtures + .WhenPieceExporterIsInvoked(input) + .ThenAProjectCostSurfacesFileIsSaved(); + }); +}); + +const getFixtures = async () => { + const sandbox = await Test.createTestingModule({ + imports: [ + TypeOrmModule.forRoot({ + ...geoprocessingConnections.apiDB, + keepConnectionAlive: true, + logging: false, + }), + TypeOrmModule.forRoot({ + ...geoprocessingConnections.default, + keepConnectionAlive: true, + logging: false, + }), + TypeOrmModule.forFeature([GeoFeatureGeometry]), + GeoCloningFilesRepositoryModule, + ], + providers: [ProjectCostSurfacesPieceExporter], + }).compile(); + + await sandbox.init(); + sandbox.useLogger(new FakeLogger()); + + const projectId = v4(); + const organizationId = v4(); + const sut = sandbox.get(ProjectCostSurfacesPieceExporter); + const apiEntityManager: EntityManager = sandbox.get( + getEntityManagerToken(geoprocessingConnections.apiDB), + ); + const geoEntityManager: EntityManager = sandbox.get( + getEntityManagerToken(geoprocessingConnections.default), + ); + const costSurfacesDataRepo = geoEntityManager.getRepository(CostSurfacePuDataEntity); + const fileRepository = sandbox.get(CloningFilesRepository); + + + return { + cleanUp: async () => { + await DeleteProjectAndOrganization( + apiEntityManager, + projectId, + organizationId, + ); + await costSurfacesDataRepo.delete({}); + + }, + GivenAProjectCostSurfacesExportJob: (): ExportJobInput => { + return { + allPieces: [ + { resourceId: projectId, piece: ClonePiece.ProjectMetadata }, + { + resourceId: projectId, + piece: ClonePiece.ProjectCostSurfaces, + }, + ], + componentId: v4(), + exportId: v4(), + piece: ClonePiece.ProjectCostSurfaces, + resourceId: projectId, + resourceKind: ResourceKind.Project, + }; + }, + GivenProjectExist: async () => { + return GivenProjectExists(apiEntityManager, projectId, organizationId); + }, + GivenCostSurfacesForProject: async () => { + const costSurface = await GivenCostSurfaces( + apiEntityManager, + 1, 10, 'Cost Surface', projectId + ); + + await GivenCostSurfaceData( + geoEntityManager, + projectId, + costSurface.id, + ); + return costSurface.id; + }, + GivenTagOnFeature: async (featureId: string, tag: string) => + await apiEntityManager.query(`INSERT INTO project_feature_tags + (project_id, feature_id, tag) + VALUES + ('${projectId}', '${featureId}', '${tag}' ) `), + + WhenPieceExporterIsInvoked: (input: ExportJobInput) => { + return { + ThenAProjectCostSurfacesFileIsSaved: async () => { + const result = await sut.run(input); + const file = await fileRepository.get(result.uris[0].uri); + expect((file as Right).right).toBeDefined(); + if (isLeft(file)) throw new Error(); + const savedStrem = file.right; + const content = await readSavedFile( + savedStrem, + ); + expect(content.costSurfaces).toHaveLength(1); + const costSurfacesExported = content.costSurfaces; + + + + expect( + costSurfacesExported.every( + ({ name, data, min, max }) => + name === 'cost surface' && + data.length === 10 + ), + ); + }, + }; + }, + }; +};