Browse Source

Feature/improve portfolio calculator unit tests by loading currency from user settings (#5765)

* Use currency from user settings

* Update changelog
pull/5570/head^2
Thomas Kaul 4 days ago
committed by GitHub
parent
commit
103c15ca31
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 1
      CHANGELOG.md
  2. 6
      apps/api/src/app/portfolio/calculator/portfolio-calculator-test-utils.ts
  3. 20
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btceur.spec.ts
  4. 20
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-short.spec.ts
  5. 20
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd.spec.ts
  6. 20
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell-partially.spec.ts
  7. 20
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell.spec.ts
  8. 7
      test/import/ok/novn-buy-and-sell-partially.json
  9. 7
      test/import/ok/novn-buy-and-sell.json

1
CHANGELOG.md

@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Improved the currency validation in the search functionality of the data provider service - Improved the currency validation in the search functionality of the data provider service
- Optimized the get quotes functionality by utilizing the asset profile resolutions in the _Financial Modeling Prep_ service - Optimized the get quotes functionality by utilizing the asset profile resolutions in the _Financial Modeling Prep_ service
- Extracted the footer to a component - Extracted the footer to a component
- Improved the portfolio calculator unit tests to load the user currency from the exported file
### Fixed ### Fixed

6
apps/api/src/app/portfolio/calculator/portfolio-calculator-test-utils.ts

@ -1,3 +1,5 @@
import { Export } from '@ghostfolio/common/interfaces';
import { readFileSync } from 'node:fs'; import { readFileSync } from 'node:fs';
export const activityDummyData = { export const activityDummyData = {
@ -37,6 +39,6 @@ export const userDummyData = {
id: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' id: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
}; };
export function loadActivityExportFile(filePath: string) { export function loadExportFile(filePath: string): Export {
return JSON.parse(readFileSync(filePath, 'utf8')).activities; return JSON.parse(readFileSync(filePath, 'utf8'));
} }

20
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btceur.spec.ts

@ -1,8 +1,7 @@
import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto';
import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface';
import { import {
activityDummyData, activityDummyData,
loadActivityExportFile, loadExportFile,
symbolProfileDummyData, symbolProfileDummyData,
userDummyData userDummyData
} from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils'; } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils';
@ -16,9 +15,9 @@ import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-
import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service';
import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock';
import { parseDate } from '@ghostfolio/common/helper'; import { parseDate } from '@ghostfolio/common/helper';
import { Export } from '@ghostfolio/common/interfaces';
import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type';
import { Tag } from '@prisma/client';
import { Big } from 'big.js'; import { Big } from 'big.js';
import { join } from 'node:path'; import { join } from 'node:path';
@ -53,7 +52,7 @@ jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => {
}); });
describe('PortfolioCalculator', () => { describe('PortfolioCalculator', () => {
let activityDtos: CreateOrderDto[]; let exportResponse: Export;
let configurationService: ConfigurationService; let configurationService: ConfigurationService;
let currentRateService: CurrentRateService; let currentRateService: CurrentRateService;
@ -63,7 +62,7 @@ describe('PortfolioCalculator', () => {
let redisCacheService: RedisCacheService; let redisCacheService: RedisCacheService;
beforeAll(() => { beforeAll(() => {
activityDtos = loadActivityExportFile( exportResponse = loadExportFile(
join(__dirname, '../../../../../../../test/import/ok/btceur.json') join(__dirname, '../../../../../../../test/import/ok/btceur.json')
); );
}); });
@ -97,7 +96,8 @@ describe('PortfolioCalculator', () => {
it.only('with BTCUSD buy (in EUR)', async () => { it.only('with BTCUSD buy (in EUR)', async () => {
jest.useFakeTimers().setSystemTime(parseDate('2022-01-14').getTime()); jest.useFakeTimers().setSystemTime(parseDate('2022-01-14').getTime());
const activities: Activity[] = activityDtos.map((activity) => ({ const activities: Activity[] = exportResponse.activities.map(
(activity) => ({
...activityDummyData, ...activityDummyData,
...activity, ...activity,
date: parseDate(activity.date), date: parseDate(activity.date),
@ -109,16 +109,14 @@ describe('PortfolioCalculator', () => {
name: 'Bitcoin', name: 'Bitcoin',
symbol: activity.symbol symbol: activity.symbol
}, },
tags: activity.tags?.map((id) => {
return { id } as Tag;
}),
unitPriceInAssetProfileCurrency: 44558.42 unitPriceInAssetProfileCurrency: 44558.42
})); })
);
const portfolioCalculator = portfolioCalculatorFactory.createCalculator({ const portfolioCalculator = portfolioCalculatorFactory.createCalculator({
activities, activities,
calculationType: PerformanceCalculationType.ROAI, calculationType: PerformanceCalculationType.ROAI,
currency: 'USD', currency: exportResponse.user.settings.currency,
userId: userDummyData.id userId: userDummyData.id
}); });

20
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-short.spec.ts

@ -1,8 +1,7 @@
import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto';
import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface';
import { import {
activityDummyData, activityDummyData,
loadActivityExportFile, loadExportFile,
symbolProfileDummyData, symbolProfileDummyData,
userDummyData userDummyData
} from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils'; } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils';
@ -16,9 +15,9 @@ import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-
import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service';
import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock';
import { parseDate } from '@ghostfolio/common/helper'; import { parseDate } from '@ghostfolio/common/helper';
import { Export } from '@ghostfolio/common/interfaces';
import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type';
import { Tag } from '@prisma/client';
import { Big } from 'big.js'; import { Big } from 'big.js';
import { join } from 'node:path'; import { join } from 'node:path';
@ -53,7 +52,7 @@ jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => {
}); });
describe('PortfolioCalculator', () => { describe('PortfolioCalculator', () => {
let activityDtos: CreateOrderDto[]; let exportResponse: Export;
let configurationService: ConfigurationService; let configurationService: ConfigurationService;
let currentRateService: CurrentRateService; let currentRateService: CurrentRateService;
@ -63,7 +62,7 @@ describe('PortfolioCalculator', () => {
let redisCacheService: RedisCacheService; let redisCacheService: RedisCacheService;
beforeAll(() => { beforeAll(() => {
activityDtos = loadActivityExportFile( exportResponse = loadExportFile(
join(__dirname, '../../../../../../../test/import/ok/btcusd-short.json') join(__dirname, '../../../../../../../test/import/ok/btcusd-short.json')
); );
}); });
@ -97,7 +96,8 @@ describe('PortfolioCalculator', () => {
it.only('with BTCUSD short sell (in USD)', async () => { it.only('with BTCUSD short sell (in USD)', async () => {
jest.useFakeTimers().setSystemTime(parseDate('2022-01-14').getTime()); jest.useFakeTimers().setSystemTime(parseDate('2022-01-14').getTime());
const activities: Activity[] = activityDtos.map((activity) => ({ const activities: Activity[] = exportResponse.activities.map(
(activity) => ({
...activityDummyData, ...activityDummyData,
...activity, ...activity,
date: parseDate(activity.date), date: parseDate(activity.date),
@ -109,16 +109,14 @@ describe('PortfolioCalculator', () => {
name: 'Bitcoin', name: 'Bitcoin',
symbol: activity.symbol symbol: activity.symbol
}, },
tags: activity.tags?.map((id) => {
return { id } as Tag;
}),
unitPriceInAssetProfileCurrency: activity.unitPrice unitPriceInAssetProfileCurrency: activity.unitPrice
})); })
);
const portfolioCalculator = portfolioCalculatorFactory.createCalculator({ const portfolioCalculator = portfolioCalculatorFactory.createCalculator({
activities, activities,
calculationType: PerformanceCalculationType.ROAI, calculationType: PerformanceCalculationType.ROAI,
currency: 'USD', currency: exportResponse.user.settings.currency,
userId: userDummyData.id userId: userDummyData.id
}); });

20
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd.spec.ts

@ -1,8 +1,7 @@
import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto';
import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface';
import { import {
activityDummyData, activityDummyData,
loadActivityExportFile, loadExportFile,
symbolProfileDummyData, symbolProfileDummyData,
userDummyData userDummyData
} from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils'; } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils';
@ -16,9 +15,9 @@ import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-
import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service';
import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock';
import { parseDate } from '@ghostfolio/common/helper'; import { parseDate } from '@ghostfolio/common/helper';
import { Export } from '@ghostfolio/common/interfaces';
import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type';
import { Tag } from '@prisma/client';
import { Big } from 'big.js'; import { Big } from 'big.js';
import { join } from 'node:path'; import { join } from 'node:path';
@ -53,7 +52,7 @@ jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => {
}); });
describe('PortfolioCalculator', () => { describe('PortfolioCalculator', () => {
let activityDtos: CreateOrderDto[]; let exportResponse: Export;
let configurationService: ConfigurationService; let configurationService: ConfigurationService;
let currentRateService: CurrentRateService; let currentRateService: CurrentRateService;
@ -63,7 +62,7 @@ describe('PortfolioCalculator', () => {
let redisCacheService: RedisCacheService; let redisCacheService: RedisCacheService;
beforeAll(() => { beforeAll(() => {
activityDtos = loadActivityExportFile( exportResponse = loadExportFile(
join(__dirname, '../../../../../../../test/import/ok/btcusd.json') join(__dirname, '../../../../../../../test/import/ok/btcusd.json')
); );
}); });
@ -97,7 +96,8 @@ describe('PortfolioCalculator', () => {
it.only('with BTCUSD buy (in USD)', async () => { it.only('with BTCUSD buy (in USD)', async () => {
jest.useFakeTimers().setSystemTime(parseDate('2022-01-14').getTime()); jest.useFakeTimers().setSystemTime(parseDate('2022-01-14').getTime());
const activities: Activity[] = activityDtos.map((activity) => ({ const activities: Activity[] = exportResponse.activities.map(
(activity) => ({
...activityDummyData, ...activityDummyData,
...activity, ...activity,
date: parseDate(activity.date), date: parseDate(activity.date),
@ -109,16 +109,14 @@ describe('PortfolioCalculator', () => {
name: 'Bitcoin', name: 'Bitcoin',
symbol: activity.symbol symbol: activity.symbol
}, },
tags: activity.tags?.map((id) => {
return { id } as Tag;
}),
unitPriceInAssetProfileCurrency: 44558.42 unitPriceInAssetProfileCurrency: 44558.42
})); })
);
const portfolioCalculator = portfolioCalculatorFactory.createCalculator({ const portfolioCalculator = portfolioCalculatorFactory.createCalculator({
activities, activities,
calculationType: PerformanceCalculationType.ROAI, calculationType: PerformanceCalculationType.ROAI,
currency: 'USD', currency: exportResponse.user.settings.currency,
userId: userDummyData.id userId: userDummyData.id
}); });

20
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell-partially.spec.ts

@ -1,8 +1,7 @@
import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto';
import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface';
import { import {
activityDummyData, activityDummyData,
loadActivityExportFile, loadExportFile,
symbolProfileDummyData, symbolProfileDummyData,
userDummyData userDummyData
} from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils'; } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils';
@ -16,9 +15,9 @@ import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-
import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service';
import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock';
import { parseDate } from '@ghostfolio/common/helper'; import { parseDate } from '@ghostfolio/common/helper';
import { Export } from '@ghostfolio/common/interfaces';
import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type';
import { Tag } from '@prisma/client';
import { Big } from 'big.js'; import { Big } from 'big.js';
import { join } from 'node:path'; import { join } from 'node:path';
@ -53,7 +52,7 @@ jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => {
}); });
describe('PortfolioCalculator', () => { describe('PortfolioCalculator', () => {
let activityDtos: CreateOrderDto[]; let exportResponse: Export;
let configurationService: ConfigurationService; let configurationService: ConfigurationService;
let currentRateService: CurrentRateService; let currentRateService: CurrentRateService;
@ -63,7 +62,7 @@ describe('PortfolioCalculator', () => {
let redisCacheService: RedisCacheService; let redisCacheService: RedisCacheService;
beforeAll(() => { beforeAll(() => {
activityDtos = loadActivityExportFile( exportResponse = loadExportFile(
join( join(
__dirname, __dirname,
'../../../../../../../test/import/ok/novn-buy-and-sell-partially.json' '../../../../../../../test/import/ok/novn-buy-and-sell-partially.json'
@ -100,7 +99,8 @@ describe('PortfolioCalculator', () => {
it.only('with NOVN.SW buy and sell partially', async () => { it.only('with NOVN.SW buy and sell partially', async () => {
jest.useFakeTimers().setSystemTime(parseDate('2022-04-11').getTime()); jest.useFakeTimers().setSystemTime(parseDate('2022-04-11').getTime());
const activities: Activity[] = activityDtos.map((activity) => ({ const activities: Activity[] = exportResponse.activities.map(
(activity) => ({
...activityDummyData, ...activityDummyData,
...activity, ...activity,
date: parseDate(activity.date), date: parseDate(activity.date),
@ -112,16 +112,14 @@ describe('PortfolioCalculator', () => {
name: 'Novartis AG', name: 'Novartis AG',
symbol: activity.symbol symbol: activity.symbol
}, },
tags: activity.tags?.map((id) => {
return { id } as Tag;
}),
unitPriceInAssetProfileCurrency: activity.unitPrice unitPriceInAssetProfileCurrency: activity.unitPrice
})); })
);
const portfolioCalculator = portfolioCalculatorFactory.createCalculator({ const portfolioCalculator = portfolioCalculatorFactory.createCalculator({
activities, activities,
calculationType: PerformanceCalculationType.ROAI, calculationType: PerformanceCalculationType.ROAI,
currency: 'CHF', currency: exportResponse.user.settings.currency,
userId: userDummyData.id userId: userDummyData.id
}); });

20
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell.spec.ts

@ -1,8 +1,7 @@
import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto';
import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface';
import { import {
activityDummyData, activityDummyData,
loadActivityExportFile, loadExportFile,
symbolProfileDummyData, symbolProfileDummyData,
userDummyData userDummyData
} from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils'; } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils';
@ -16,9 +15,9 @@ import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-
import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service';
import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock';
import { parseDate } from '@ghostfolio/common/helper'; import { parseDate } from '@ghostfolio/common/helper';
import { Export } from '@ghostfolio/common/interfaces';
import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type';
import { Tag } from '@prisma/client';
import { Big } from 'big.js'; import { Big } from 'big.js';
import { join } from 'node:path'; import { join } from 'node:path';
@ -53,7 +52,7 @@ jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => {
}); });
describe('PortfolioCalculator', () => { describe('PortfolioCalculator', () => {
let activityDtos: CreateOrderDto[]; let exportResponse: Export;
let configurationService: ConfigurationService; let configurationService: ConfigurationService;
let currentRateService: CurrentRateService; let currentRateService: CurrentRateService;
@ -63,7 +62,7 @@ describe('PortfolioCalculator', () => {
let redisCacheService: RedisCacheService; let redisCacheService: RedisCacheService;
beforeAll(() => { beforeAll(() => {
activityDtos = loadActivityExportFile( exportResponse = loadExportFile(
join( join(
__dirname, __dirname,
'../../../../../../../test/import/ok/novn-buy-and-sell.json' '../../../../../../../test/import/ok/novn-buy-and-sell.json'
@ -100,7 +99,8 @@ describe('PortfolioCalculator', () => {
it.only('with NOVN.SW buy and sell', async () => { it.only('with NOVN.SW buy and sell', async () => {
jest.useFakeTimers().setSystemTime(parseDate('2022-04-11').getTime()); jest.useFakeTimers().setSystemTime(parseDate('2022-04-11').getTime());
const activities: Activity[] = activityDtos.map((activity) => ({ const activities: Activity[] = exportResponse.activities.map(
(activity) => ({
...activityDummyData, ...activityDummyData,
...activity, ...activity,
date: parseDate(activity.date), date: parseDate(activity.date),
@ -112,16 +112,14 @@ describe('PortfolioCalculator', () => {
name: 'Novartis AG', name: 'Novartis AG',
symbol: activity.symbol symbol: activity.symbol
}, },
tags: activity.tags?.map((id) => {
return { id } as Tag;
}),
unitPriceInAssetProfileCurrency: activity.unitPrice unitPriceInAssetProfileCurrency: activity.unitPrice
})); })
);
const portfolioCalculator = portfolioCalculatorFactory.createCalculator({ const portfolioCalculator = portfolioCalculatorFactory.createCalculator({
activities, activities,
calculationType: PerformanceCalculationType.ROAI, calculationType: PerformanceCalculationType.ROAI,
currency: 'CHF', currency: exportResponse.user.settings.currency,
userId: userDummyData.id userId: userDummyData.id
}); });

7
test/import/ok/novn-buy-and-sell-partially.json

@ -24,5 +24,10 @@
"date": "2022-03-07T00:00:00.000Z", "date": "2022-03-07T00:00:00.000Z",
"symbol": "NOVN.SW" "symbol": "NOVN.SW"
} }
] ],
"user": {
"settings": {
"currency": "CHF"
}
}
} }

7
test/import/ok/novn-buy-and-sell.json

@ -24,5 +24,10 @@
"date": "2022-03-07T00:00:00.000Z", "date": "2022-03-07T00:00:00.000Z",
"symbol": "NOVN.SW" "symbol": "NOVN.SW"
} }
] ],
"user": {
"settings": {
"currency": "CHF"
}
}
} }

Loading…
Cancel
Save