Skip to content

Commit

Permalink
conservation project validations
Browse files Browse the repository at this point in the history
  • Loading branch information
alexeh committed Nov 10, 2024
1 parent 531f884 commit ccdd4b4
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ export class ConservationProjectParamDto {
o.projectSpecificEmission ===
PROJECT_SPECIFIC_EMISSION.TWO_EMISSION_FACTORS,
)
@IsNotEmpty({
message:
'Emission Factor AGB is required when emissionFactorUsed is Tier 3 and projectSpecificEmission is Two emission factors',
})
@IsNumber()
emissionFactorAGB: number;

@ValidateIf(
Expand All @@ -52,6 +57,11 @@ export class ConservationProjectParamDto {
o.projectSpecificEmission ===
PROJECT_SPECIFIC_EMISSION.TWO_EMISSION_FACTORS,
)
@IsNotEmpty({
message:
'Emission Factor SOC is required when emissionFactorUsed is Tier 3 and projectSpecificEmission is Two emission factors',
})
@IsNumber()
emissionFactorSOC: number;

@ValidateIf((o) => o.emissionFactorUsed === PROJECT_EMISSION_FACTORS.TIER_3)
Expand All @@ -64,5 +74,10 @@ export class ConservationProjectParamDto {
o.projectSpecificEmission ===
PROJECT_SPECIFIC_EMISSION.ONE_EMISSION_FACTOR,
)
@IsNotEmpty({
message:
'Project Specific Emission Factor must be provided when emissionFactorUsed is Tier 3 and projectSpecificEmission is One emission factor',
})
@IsNumber()
projectSpecificEmissionFactor: number;
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,7 @@ export class ProjectParamsValidator implements ValidatorConstraintInterface {
const formattedErrors = [];
errors.forEach((error) => {
Object.values(error.constraints).forEach((constraint) => {
formattedErrors.push({
message: constraint,
});
formattedErrors.push(constraint);
});
});
return formattedErrors;
Expand Down
155 changes: 103 additions & 52 deletions api/test/integration/custom-projects/custom-projects-validations.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { TestManager } from '../../utils/test-manager';
import { customProjectContract } from '@shared/contracts/custom-projects.contract';
import { PROJECT_EMISSION_FACTORS } from '@api/modules/custom-projects/dto/conservation-project-params.dto';

describe('Create Custom Projects - Request Validations', () => {
let testManager: TestManager;
Expand All @@ -20,11 +21,10 @@ describe('Create Custom Projects - Request Validations', () => {
.send({});

expect(response.body.errors).toHaveLength(10);
expect(response.body.errors).toMatchObject(GENERAL_VALIDATION_ERRORS);
});
});
describe('Conservation Project Validations', () => {
test('If Loss Rate used is National Average, Project Specific Loss Rate should not be provided', async () => {
test('Loss Rate Used should be National Average or Project Specific', async () => {
const response = await testManager
.request()
.post(customProjectContract.createCustomProject.path)
Expand All @@ -37,61 +37,112 @@ describe('Create Custom Projects - Request Validations', () => {
initialCarbonPriceAssumption: 1000,
carbonRevenuesToCover: 'Opex',
parameters: {
lossRateUsed: 'National average',
lossRateUsed: 'Invalid',
emissionFactorUsed: 'Tier 1 - Global emission factor',
projectSpecificLossRate: -0.5,
},
});

expect(response.body.errors).toHaveLength(1);
expect(response.body.errors[0].title).toEqual(
'lossRateUsed must be one of the following values: National average, Project specific',
);
});
test('If Loss Rate used is Project Specific, Project Specific Loss Rate should be provided and be negative', async () => {
const response = await testManager
.request()
.post(customProjectContract.createCustomProject.path)
.send({
countryCode: 'IND',
activity: 'Conservation',
ecosystem: 'Mangrove',
projectName: 'My custom project',
projectSizeHa: 1000,
initialCarbonPriceAssumption: 1000,
carbonRevenuesToCover: 'Opex',
parameters: {
lossRateUsed: 'Project specific',
emissionFactorUsed: 'Tier 1 - Global emission factor',
projectSpecificLossRate: 0.5,
},
});
expect(response.body.errors).toHaveLength(1);
expect(response.body.errors[0].title).toEqual(
'Project Specific Loss Rate must be negative',
);
});
test('If Emission Factor Used is Tier 2, only Mangroves is accepted as ecosystem', async () => {
const response = await testManager
.request()
.post(customProjectContract.createCustomProject.path)
.send({
countryCode: 'IND',
activity: 'Conservation',
ecosystem: 'Seagrass',
projectName: 'My custom project',
projectSizeHa: 1000,
initialCarbonPriceAssumption: 1000,
carbonRevenuesToCover: 'Opex',
parameters: {
lossRateUsed: 'National average',
emissionFactorUsed: PROJECT_EMISSION_FACTORS.TIER_2,
projectSpecificEmission: 'One emission factor',
},
});
expect(response.body.errors).toHaveLength(1);
expect(response.body.errors[0].title).toEqual(
'There is only Tier 2 emission factor for Mangrove ecosystems',
);
});
test('If Emission Factor Used is Tier 3 and Project Specific Emission is Two Emission Factors, AGB and SOC should be provided', async () => {
const response = await testManager
.request()
.post(customProjectContract.createCustomProject.path)
.send({
countryCode: 'IND',
activity: 'Conservation',
ecosystem: 'Mangrove',
projectName: 'My custom project',
projectSizeHa: 1000,
initialCarbonPriceAssumption: 1000,
carbonRevenuesToCover: 'Opex',
parameters: {
lossRateUsed: 'National average',
emissionFactorUsed: PROJECT_EMISSION_FACTORS.TIER_3,
projectSpecificEmission: 'Two emission factors',
},
});
const errorTitles = response.body.errors.map((error) => error.title);
expect(errorTitles).toEqual(
expect.arrayContaining([
'Emission Factor AGB is required when emissionFactorUsed is Tier 3 and projectSpecificEmission is Two emission factors',
'Emission Factor SOC is required when emissionFactorUsed is Tier 3 and projectSpecificEmission is Two emission factors',
]),
);
});
test('If Emission Factor Used is Tier 3 and Project Specific Emission is One Emission Factor, then Project Specific Emission Factor should be provided', async () => {
const response = await testManager
.request()
.post(customProjectContract.createCustomProject.path)
.send({
countryCode: 'IND',
activity: 'Conservation',
ecosystem: 'Mangrove',
projectName: 'My custom project',
projectSizeHa: 1000,
initialCarbonPriceAssumption: 1000,
carbonRevenuesToCover: 'Opex',
parameters: {
lossRateUsed: 'National average',
emissionFactorUsed: PROJECT_EMISSION_FACTORS.TIER_3,
projectSpecificEmission: 'One emission factor',
},
});
const errorTitles = response.body.errors.map((error) => error.title);
expect(errorTitles).toEqual(
expect.arrayContaining([
'Project Specific Emission Factor must be provided when emissionFactorUsed is Tier 3 and projectSpecificEmission is One emission factor',
]),
);
});
});
});

const GENERAL_VALIDATION_ERRORS = [
{
status: '400',
title: 'countryCode must be longer than or equal to 3 characters',
},
{
status: '400',
title: 'countryCode must be a string',
},
{
status: '400',
title: 'projectName must be a string',
},
{
status: '400',
title:
'activity must be one of the following values: Restoration, Conservation',
},
{
status: '400',
title:
'ecosystem must be one of the following values: Mangrove, Seagrass, Salt marsh',
},
{
status: '400',
title:
'projectSizeHa must be a number conforming to the specified constraints',
},
{
status: '400',
title:
'initialCarbonPriceAssumption must be a number conforming to the specified constraints',
},
{
status: '400',
title:
'carbonRevenuesToCover must be one of the following values: Opex, Capex and Opex',
},
{
status: '400',
title: 'Invalid project parameters for the selected activity type.',
},
{
status: '400',
title: 'parameters should not be empty',
},
];

0 comments on commit ccdd4b4

Please sign in to comment.