Browse Source

test(portfolio): cover a missing exchange rate in the calculator

Builds the BALN.SW buy with both fee fields unset, which is the state the
calculator sees when no exchange rate is available for the activity's date.
Asserts the snapshot is still produced and the fees count as 0.

Fails with "[big.js] Invalid number" without the preceding fix.
pull/7384/head
Arham Amin 4 weeks ago
parent
commit
2c5f4fdca2
  1. 141
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-without-exchange-rate.spec.ts

141
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-without-exchange-rate.spec.ts

@ -0,0 +1,141 @@
import {
activityDummyData,
assetProfileDummyData,
userDummyData
} from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils';
import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory';
import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service';
import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock';
import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service';
import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock';
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.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 { parseDate } from '@ghostfolio/common/helper';
import { Activity } from '@ghostfolio/common/interfaces';
import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type';
import { Big } from 'big.js';
jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => {
return {
CurrentRateService: jest.fn().mockImplementation(() => {
return CurrentRateServiceMock;
})
};
});
jest.mock(
'@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service',
() => {
return {
PortfolioSnapshotService: jest.fn().mockImplementation(() => {
return PortfolioSnapshotServiceMock;
})
};
}
);
jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => {
return {
RedisCacheService: jest.fn().mockImplementation(() => {
return RedisCacheServiceMock;
})
};
});
describe('PortfolioCalculator', () => {
let configurationService: ConfigurationService;
let currentRateService: CurrentRateService;
let exchangeRateDataService: ExchangeRateDataService;
let portfolioCalculatorFactory: PortfolioCalculatorFactory;
let portfolioSnapshotService: PortfolioSnapshotService;
let redisCacheService: RedisCacheService;
beforeEach(() => {
configurationService = new ConfigurationService();
currentRateService = new CurrentRateService(null, null, null, null);
exchangeRateDataService = new ExchangeRateDataService(
null,
null,
null,
null
);
portfolioSnapshotService = new PortfolioSnapshotService(null, null);
redisCacheService = new RedisCacheService(null, null);
portfolioCalculatorFactory = new PortfolioCalculatorFactory(
configurationService,
currentRateService,
exchangeRateDataService,
portfolioSnapshotService,
redisCacheService
);
});
describe('get current positions', () => {
it.only('with BALN.SW buy and a missing exchange rate', async () => {
jest.useFakeTimers().setSystemTime(parseDate('2021-12-18').getTime());
// ExchangeRateDataService.toCurrencyAtDate() resolves to undefined when
// no exchange rate is available for the activity’s date, which leaves the
// converted fee fields undefined by the time they reach the calculator.
const activities: Activity[] = [
{
...activityDummyData,
assetProfile: {
...assetProfileDummyData,
currency: 'CHF',
dataSource: 'YAHOO',
name: 'Bâloise Holding AG',
symbol: 'BALN.SW'
},
date: new Date('2021-11-30'),
feeInAssetProfileCurrency: undefined,
feeInBaseCurrency: undefined,
quantity: 2,
type: 'BUY',
unitPriceInAssetProfileCurrency: 136.6
}
];
const portfolioCalculator = portfolioCalculatorFactory.createCalculator({
activities,
calculationType: PerformanceCalculationType.ROAI,
currency: 'CHF',
userId: userDummyData.id
});
const portfolioSnapshot = await portfolioCalculator.computeSnapshot();
// The snapshot is still computed; the unconvertible fees count as 0
// rather than aborting the request with “[big.js] Invalid number”.
expect(portfolioSnapshot).toMatchObject({
currentValueInBaseCurrency: new Big('297.8'),
errors: [],
hasErrors: false,
totalFeesWithCurrencyEffect: new Big('0'),
positions: [
{
activitiesCount: 1,
averagePrice: new Big('136.6'),
currency: 'CHF',
dataSource: 'YAHOO',
dateOfFirstActivity: '2021-11-30',
fee: new Big('0'),
feeInBaseCurrency: new Big('0'),
investment: new Big('273.2'),
quantity: new Big('2'),
symbol: 'BALN.SW',
valueInBaseCurrency: new Big('297.8')
}
]
});
});
});
});
Loading…
Cancel
Save