diff --git a/api/test/integration/custom-projects/custom-projects-save.spec.ts b/api/test/integration/custom-projects/custom-projects-save.spec.ts new file mode 100644 index 00000000..2379e672 --- /dev/null +++ b/api/test/integration/custom-projects/custom-projects-save.spec.ts @@ -0,0 +1,728 @@ +import { TestManager } from '../../utils/test-manager'; +import { customProjectContract } from '@shared/contracts/custom-projects.contract'; +import { HttpStatus } from '@nestjs/common'; +import { CustomProject } from '@shared/entities/custom-project.entity'; + +describe('Snapshot Custom Projects', () => { + let testManager: TestManager; + let token: string; + + beforeAll(async () => { + testManager = await TestManager.createTestManager(); + const { jwtToken } = await testManager.setUpTestUser(); + token = jwtToken; + await testManager.ingestCountries(); + await testManager.ingestExcel(jwtToken); + }); + + afterAll(async () => { + await testManager.clearDatabase(); + await testManager.close(); + }); + + describe('Save Custom Projects', () => { + // TODO: We need to add a createCustomProject mock function for tests that can be used across apps + test('Should save a custom project', async () => { + const response = await testManager + .request() + .post(customProjectContract.saveCustomProject.path) + .set('Authorization', `Bearer ${token}`) + .send({ + projectName: 'My custom project', + abatementPotential: null, + country: { + code: 'IND', + name: 'India', + }, + totalCostNPV: 2503854.27918858, + totalCost: 3332201.598883546, + projectSize: 1000, + projectLength: 20, + ecosystem: 'Mangrove', + activity: 'Conservation', + output: { + lossRate: -0.0016, + carbonRevenuesToCover: 'Opex', + initialCarbonPrice: 1000, + emissionFactors: { + emissionFactor: null, + emissionFactorAgb: 67.7, + emissionFactorSoc: 85.5, + }, + totalProjectCost: { + total: { + total: 3332201.598883546, + capex: 1600616.6666666667, + opex: 1731584.9322168794, + }, + npv: { + total: 2503854.27918858, + capex: 1505525.2721514185, + opex: 998329.0070371614, + }, + }, + summary: { + '$/tCO2e (total cost, NPV)': 61.470410294099835, + '$/ha': 2503.8542791885798, + 'NPV covering cost': -493830.3621939037, + 'Leftover after OpEx / total cost': null, + 'IRR when priced to cover OpEx': 0.0560813585617166, + 'IRR when priced to cover total cost': 85683156958.8259, + 'Total cost (NPV)': 2503854.27918858, + 'Capital expenditure (NPV)': 1505525.2721514185, + 'Operating expenditure (NPV)': 998329.0070371614, + 'Credits issued': 26038.815407423757, + 'Total revenue (NPV)': 504498.6448432577, + 'Total revenue (non-discounted)': 956754.3382707891, + 'Financing cost': 80030.83333333334, + 'Funding gap': 493830.3621939037, + 'Funding gap (NPV)': 493830.3621939037, + 'Funding gap per tCO2e (NPV)': 18.965162372675024, + 'Community benefit sharing fund': 0.5, + }, + costDetails: { + total: { + capitalExpenditure: 1600616.6666666667, + operationalExpenditure: 1731584.9322168794, + totalCost: 3201233.3333333335, + operationExpenditure: 1731584.9322168794, + feasibilityAnalysis: 50000, + conservationPlanningAndAdmin: 667066.6666666666, + dataCollectionAndFieldCost: 80000, + communityRepresentation: 213550, + blueCarbonProjectPlanning: 300000, + establishingCarbonRights: 140000, + validation: 50000, + implementationLabor: 100000, + monitoring: 300000, + maintenance: 0, + communityBenefitSharingFund: 478377.16913539456, + carbonStandardFees: 5207.763081484753, + baselineReassessment: 120000, + mrv: 300000, + longTermProjectOperatingCost: 528000, + }, + npv: { + capitalExpenditure: 1505525.2721514185, + operationalExpenditure: 998329.0070371614, + totalCost: 2503854.27918858, + feasibilityAnalysis: 50000, + conservationPlanningAndAdmin: 629559.3479745106, + dataCollectionAndFieldCost: 76962.52465483235, + communityRepresentation: 197540.230048551, + blueCarbonProjectPlanning: 288609.4674556213, + establishingCarbonRights: 129504.24821726595, + validation: 44449.81793354574, + implementationLabor: 88899.63586709148, + monitoring: 181226.25950738514, + maintenance: 0, + communityBenefitSharingFund: 252249.32242162884, + carbonStandardFees: 2786.931340270489, + baselineReassessment: 75811.8711249392, + mrv: 167296.40590994002, + longTermProjectOperatingCost: 318958.2167329978, + }, + }, + yearlyBreakdown: [ + { + costName: 'feasibilityAnalysis', + totalCost: -50000, + totalNPV: -50000, + costValues: { + '0': 0, + '1': 0, + '2': 0, + '3': 0, + '4': 0, + '5': 0, + '6': 0, + '7': 0, + '8': 0, + '9': 0, + '10': 0, + '11': 0, + '12': 0, + '13': 0, + '14': 0, + '15': 0, + '16': 0, + '17': 0, + '18': 0, + '19': 0, + '20': 0, + '-4': -50000, + '-3': 0, + '-2': 0, + '-1': 0, + }, + }, + { + costName: 'conservationPlanningAndAdmin', + totalCost: -667066.6666666666, + totalNPV: -629559.3479745106, + costValues: { + '0': 0, + '1': 0, + '2': 0, + '3': 0, + '4': 0, + '5': 0, + '6': 0, + '7': 0, + '8': 0, + '9': 0, + '10': 0, + '11': 0, + '12': 0, + '13': 0, + '14': 0, + '15': 0, + '16': 0, + '17': 0, + '18': 0, + '19': 0, + '20': 0, + '-4': -166766.66666666666, + '-3': -166766.66666666666, + '-2': -166766.66666666666, + '-1': -166766.66666666666, + }, + }, + { + costName: 'dataCollectionAndFieldCost', + totalCost: -80000, + totalNPV: -76962.52465483235, + costValues: { + '0': 0, + '1': 0, + '2': 0, + '3': 0, + '4': 0, + '5': 0, + '6': 0, + '7': 0, + '8': 0, + '9': 0, + '10': 0, + '11': 0, + '12': 0, + '13': 0, + '14': 0, + '15': 0, + '16': 0, + '17': 0, + '18': 0, + '19': 0, + '20': 0, + '-4': -26666.666666666668, + '-3': -26666.666666666668, + '-2': -26666.666666666668, + '-1': 0, + }, + }, + { + costName: 'blueCarbonProjectPlanning', + totalCost: -300000, + totalNPV: -288609.4674556213, + costValues: { + '0': 0, + '1': 0, + '2': 0, + '3': 0, + '4': 0, + '5': 0, + '6': 0, + '7': 0, + '8': 0, + '9': 0, + '10': 0, + '11': 0, + '12': 0, + '13': 0, + '14': 0, + '15': 0, + '16': 0, + '17': 0, + '18': 0, + '19': 0, + '20': 0, + '-4': -100000, + '-3': -100000, + '-2': -100000, + '-1': 0, + }, + }, + { + costName: 'communityRepresentation', + totalCost: -213550, + totalNPV: -197540.230048551, + costValues: { + '0': 0, + '1': 0, + '2': 0, + '3': 0, + '4': 0, + '5': 0, + '6': 0, + '7': 0, + '8': 0, + '9': 0, + '10': 0, + '11': 0, + '12': 0, + '13': 0, + '14': 0, + '15': 0, + '16': 0, + '17': 0, + '18': 0, + '19': 0, + '20': 0, + '-4': 0, + '-3': -71183.33333333333, + '-2': -71183.33333333333, + '-1': -71183.33333333333, + }, + }, + { + costName: 'establishingCarbonRights', + totalCost: -140000, + totalNPV: -129504.24821726595, + costValues: { + '0': 0, + '1': 0, + '2': 0, + '3': 0, + '4': 0, + '5': 0, + '6': 0, + '7': 0, + '8': 0, + '9': 0, + '10': 0, + '11': 0, + '12': 0, + '13': 0, + '14': 0, + '15': 0, + '16': 0, + '17': 0, + '18': 0, + '19': 0, + '20': 0, + '-4': 0, + '-3': -46666.666666666664, + '-2': -46666.666666666664, + '-1': -46666.666666666664, + }, + }, + { + costName: 'validation', + totalCost: -50000, + totalNPV: -44449.81793354574, + costValues: { + '0': 0, + '1': 0, + '2': 0, + '3': 0, + '4': 0, + '5': 0, + '6': 0, + '7': 0, + '8': 0, + '9': 0, + '10': 0, + '11': 0, + '12': 0, + '13': 0, + '14': 0, + '15': 0, + '16': 0, + '17': 0, + '18': 0, + '19': 0, + '20': 0, + '-4': 0, + '-3': 0, + '-2': 0, + '-1': -50000, + }, + }, + { + costName: 'implementationLabor', + totalCost: -100000, + totalNPV: -88899.63586709148, + costValues: { + '0': 0, + '1': 0, + '2': 0, + '3': 0, + '4': 0, + '5': 0, + '6': 0, + '7': 0, + '8': 0, + '9': 0, + '10': 0, + '11': 0, + '12': 0, + '13': 0, + '14': 0, + '15': 0, + '16': 0, + '17': 0, + '18': 0, + '19': 0, + '20': 0, + '-4': 0, + '-3': 0, + '-2': 0, + '-1': -100000, + }, + }, + { + costName: 'monitoring', + totalCost: -300000, + totalNPV: -181226.25950738514, + costValues: { + '0': 0, + '1': -15000, + '2': -15000, + '3': -15000, + '4': -15000, + '5': -15000, + '6': -15000, + '7': -15000, + '8': -15000, + '9': -15000, + '10': -15000, + '11': -15000, + '12': -15000, + '13': -15000, + '14': -15000, + '15': -15000, + '16': -15000, + '17': -15000, + '18': -15000, + '19': -15000, + '20': -15000, + '-4': 0, + '-3': 0, + '-2': 0, + '-1': 0, + }, + }, + { + costName: 'maintenance', + totalCost: 0, + totalNPV: 0, + costValues: { + '0': 0, + '1': 0, + '2': 0, + '3': 0, + '4': 0, + '5': 0, + '6': 0, + '7': 0, + '8': 0, + '9': 0, + '10': 0, + '11': 0, + '12': 0, + '13': 0, + '14': 0, + '15': 0, + '16': 0, + '17': 0, + '18': 0, + '19': 0, + '20': 0, + '-4': 0, + '-3': 0, + '-2': 0, + '-1': 0, + }, + }, + { + costName: 'communityBenefitSharingFund', + totalCost: -478377.16913539456, + totalNPV: -252249.32242162884, + costValues: { + '0': 0, + '1': -3101.320320000044, + '2': -4951.5160414005795, + '3': -6853.590667682226, + '4': -8808.645091381013, + '5': -10817.801034974973, + '6': -12882.20142107311, + '7': -15003.01074892755, + '8': -17181.41547737641, + '9': -19418.624414322498, + '10': -21715.869112862427, + '11': -24074.40427416561, + '12': -26495.508157226817, + '13': -28980.482995600523, + '14': -31530.655421230902, + '15': -34147.37689550309, + '16': -36832.024147625285, + '17': -39585.99962047368, + '18': -42410.73192401404, + '19': -45307.67629643649, + '20': -48278.31507311732, + '-4': 0, + '-3': 0, + '-2': 0, + '-1': 0, + }, + }, + { + costName: 'carbonStandardFees', + totalCost: -5207.763081484753, + totalNPV: -2786.931340270489, + costValues: { + '0': 0, + '1': -40.739840000000584, + '2': -64.08329625600338, + '3': -87.38940298199216, + '4': -110.65821993722336, + '5': -133.88980678532448, + '6': -157.08422309446732, + '7': -180.24152833751594, + '8': -203.3617818921798, + '9': -226.44504304114957, + '10': -249.49137097228606, + '11': -272.5008247787324, + '12': -295.473463459085, + '13': -318.4093459175526, + '14': -341.3085309640828, + '15': -364.17107731454234, + '16': -386.9970435908389, + '17': -409.7864883210964, + '18': -432.53946993977934, + '19': -455.25604678787926, + '20': -477.93627711301974, + '-4': 0, + '-3': 0, + '-2': 0, + '-1': 0, + }, + }, + { + costName: 'baselineReassessment', + totalCost: -120000, + totalNPV: -75811.8711249392, + costValues: { + '0': 0, + '1': 0, + '2': 0, + '3': 0, + '4': 0, + '5': 0, + '6': 0, + '7': 0, + '8': 0, + '9': 0, + '10': -40000, + '11': 0, + '12': 0, + '13': 0, + '14': 0, + '15': 0, + '16': 0, + '17': 0, + '18': 0, + '19': 0, + '20': -40000, + '-4': 0, + '-3': 0, + '-2': 0, + '-1': -40000, + }, + }, + { + costName: 'mrv', + totalCost: -300000, + totalNPV: -167296.40590994002, + costValues: { + '0': 0, + '1': 0, + '2': 0, + '3': 0, + '4': 0, + '5': -75000, + '6': 0, + '7': 0, + '8': 0, + '9': 0, + '10': -75000, + '11': 0, + '12': 0, + '13': 0, + '14': 0, + '15': -75000, + '16': 0, + '17': 0, + '18': 0, + '19': 0, + '20': -75000, + '-4': 0, + '-3': 0, + '-2': 0, + '-1': 0, + }, + }, + { + costName: 'longTermProjectOperatingCost', + totalCost: -528000, + totalNPV: -318958.2167329978, + costValues: { + '0': 0, + '1': -26400, + '2': -26400, + '3': -26400, + '4': -26400, + '5': -26400, + '6': -26400, + '7': -26400, + '8': -26400, + '9': -26400, + '10': -26400, + '11': -26400, + '12': -26400, + '13': -26400, + '14': -26400, + '15': -26400, + '16': -26400, + '17': -26400, + '18': -26400, + '19': -26400, + '20': -26400, + '-4': 0, + '-3': 0, + '-2': 0, + '-1': 0, + }, + }, + { + costName: 'opexTotalCostPlan', + totalCost: -1731584.9322168794, + totalNPV: -998329.0070371614, + costValues: { + '0': 0, + '1': -44542.060160000045, + '2': -46415.59933765658, + '3': -48340.98007066422, + '4': -50319.30331131823, + '5': -127351.6908417603, + '6': -54439.28564416758, + '7': -56583.25227726507, + '8': -58784.777259268594, + '9': -61045.06945736365, + '10': -178365.36048383472, + '11': -65746.90509894435, + '12': -68190.9816206859, + '13': -70698.89234151808, + '14': -73271.96395219499, + '15': -150911.5479728176, + '16': -78619.02119121613, + '17': -81395.78610879477, + '18': -84243.27139395382, + '19': -87162.93234322437, + '20': -205156.25135023033, + '-4': 0, + '-3': 0, + '-2': 0, + '-1': -40000, + }, + }, + { + costName: 'capexTotalCostPlan', + totalCost: -1600616.6666666667, + totalNPV: -1505525.2721514185, + costValues: { + '0': 0, + '1': 0, + '2': 0, + '3': 0, + '4': 0, + '5': 0, + '6': 0, + '7': 0, + '8': 0, + '9': 0, + '10': 0, + '11': 0, + '12': 0, + '13': 0, + '14': 0, + '15': 0, + '16': 0, + '17': 0, + '18': 0, + '19': 0, + '20': 0, + '-4': -343433.3333333333, + '-3': -411283.3333333333, + '-2': -411283.3333333333, + '-1': -434616.6666666667, + }, + }, + ], + }, + input: { + countryCode: 'IND', + activity: 'Conservation', + ecosystem: 'Mangrove', + projectName: 'My custom project', + projectSizeHa: 1000, + initialCarbonPriceAssumption: 1000, + carbonRevenuesToCover: 'Opex', + parameters: { + lossRateUsed: 'National average', + emissionFactorUsed: 'Tier 2 - Country-specific emission factor', + ecosystem: 'Mangrove', + }, + costInputs: { + feasibilityAnalysis: 50000, + conservationPlanningAndAdmin: 166766.66666666666, + dataCollectionAndFieldCost: 26666.666666666668, + communityRepresentation: 71183.33333333333, + blueCarbonProjectPlanning: 100000, + establishingCarbonRights: 46666.666666666664, + financingCost: 0.05, + validation: 50000, + implementationLaborHybrid: null, + implementationLabor: 100, + monitoring: 15000, + maintenance: 0.0833, + carbonStandardFees: 0.2, + communityBenefitSharingFund: 0.5, + baselineReassessment: 40000, + mrv: 75000, + longTermProjectOperatingCost: 26400, + otherCommunityCashFlow: 'Development', + }, + assumptions: { + verificationFrequency: 5, + baselineReassessmentFrequency: 10, + discountRate: 0.04, + restorationRate: 250, + carbonPriceIncrease: 0.015, + buffer: 0.2, + projectLength: 20, + }, + }, + }); + + const customProject = await testManager + .getDataSource() + .getRepository(CustomProject) + .find(); + + expect(response.status).toBe(HttpStatus.CREATED); + expect(customProject).toHaveLength(1); + expect(customProject[0].projectName).toBe('My custom project'); + }); + }); +}); diff --git a/api/test/integration/custom-projects/custom-projects-snapshot.spec.ts b/api/test/integration/custom-projects/custom-projects-snapshot.spec.ts deleted file mode 100644 index 71da3dab..00000000 --- a/api/test/integration/custom-projects/custom-projects-snapshot.spec.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { TestManager } from '../../utils/test-manager'; -import { customProjectContract } from '@shared/contracts/custom-projects.contract'; -import { HttpStatus } from '@nestjs/common'; - -describe('Snapshot Custom Projects', () => { - let testManager: TestManager; - - beforeAll(async () => { - testManager = await TestManager.createTestManager(); - const { jwtToken } = await testManager.setUpTestUser(); - await testManager.ingestCountries(); - await testManager.ingestExcel(jwtToken); - }); - - afterAll(async () => { - await testManager.clearDatabase(); - await testManager.close(); - }); - - describe('Persist custom project snapshot', () => { - test.skip('Should persist a custom project in the DB', async () => { - const response = await testManager - .request() - .post(customProjectContract.saveCustomProject.path) - .send({ - inputSnapshot: { - countryCode: 'IND', - activity: 'Conservation', - ecosystem: 'Mangrove', - projectName: 'My custom project', - projectSizeHa: 1000, - initialCarbonPriceAssumption: 1000, - carbonRevenuesToCover: 'Opex', - parameters: { - lossRateUsed: 'National average', - emissionFactorUsed: 'Tier 2 - Country-specific emission factor', - }, - costInputs: { - feasibilityAnalysis: 50000, - conservationPlanningAndAdmin: 166766.66666666666, - dataCollectionAndFieldCost: 26666.666666666668, - communityRepresentation: 71183.33333333333, - blueCarbonProjectPlanning: 100000, - establishingCarbonRights: 46666.666666666664, - financingCost: 0.05, - validation: 50000, - implementationLaborHybrid: null, - monitoring: 15000, - maintenance: 0.0833, - carbonStandardFees: 0.2, - communityBenefitSharingFund: 0.5, - baselineReassessment: 40000, - mrv: 75000, - longTermProjectOperatingCost: 26400, - }, - assumptions: { - verificationFrequency: 5, - baselineReassessmentFrequency: 10, - discountRate: 0.04, - restorationRate: 250, - carbonPriceIncrease: 0.015, - buffer: 0.2, - projectLength: 20, - }, - }, - outputSnapshot: { - projectLength: 20, - annualProjectCashFlow: { - feasibilityAnalysis: [1], - conservationPlanningAndAdmin: [2], - dataCollectionAndFieldCost: [3], - communityRepresentation: [4], - blueCarbonProjectPlanning: [5], - establishingCarbonRights: [6], - validation: [7], - implementationLabor: [8], - totalCapex: [9], - monitoring: [10], - maintenance: [11], - communityBenefitSharingFund: [12], - carbonStandardFees: [13], - baselineReassessment: [14], - mrv: [15], - longTermProjectOperatingCost: [16], - totalOpex: [17], - totalCost: [18], - estCreditsIssued: [19], - estRevenue: [20], - annualNetIncomeRevLessOpex: [21], - cummulativeNetIncomeRevLessOpex: [22], - fundingGap: [23], - irrOpex: [24], - irrTotalCost: [25], - irrAnnualNetIncome: [26], - annualNetCashFlow: [27], - }, - projectSummary: { - costPerTCO2e: 1000, - costPerHa: 2000, - leftoverAfterOpexTotalCost: 3000, - irrCoveringOpex: 4000, - irrCoveringTotalCost: 5000, - totalCost: 6000, - capitalExpenditure: 7000, - operatingExpenditure: 8000, - creditsIssued: 9000, - totalRevenue: 10000, - nonDiscountedTotalRevenue: 1000, - financingCost: 2000, - foundingGap: 3000, - foundingGapPerTCO2e: 4000, - communityBenefitSharingFundRevenuePc: 40, - }, - costDetails: [], - }, - }); - - expect(response.status).toBe(HttpStatus.CREATED); - }); - }); -});