mirror of https://github.com/ghostfolio/ghostfolio
committed by
GitHub
25 changed files with 873 additions and 3 deletions
@ -0,0 +1,176 @@ |
|||
import { PortfolioCalculator } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator'; |
|||
import { |
|||
activityDummyData, |
|||
symbolProfileDummyData, |
|||
userDummyData |
|||
} from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils'; |
|||
import { DEFAULT_CURRENCY } from '@ghostfolio/common/config'; |
|||
import { Activity } from '@ghostfolio/common/interfaces'; |
|||
import { RequestWithUser } from '@ghostfolio/common/types'; |
|||
|
|||
import { Type as ActivityType } from '@prisma/client'; |
|||
import { Big } from 'big.js'; |
|||
|
|||
import { PortfolioService } from './portfolio.service'; |
|||
|
|||
describe('PortfolioService', () => { |
|||
describe('getSummary', () => { |
|||
beforeEach(() => { |
|||
jest.useFakeTimers().setSystemTime(new Date('2023-07-10')); |
|||
}); |
|||
|
|||
afterEach(() => { |
|||
jest.useRealTimers(); |
|||
jest.restoreAllMocks(); |
|||
}); |
|||
|
|||
it('returns annualizedDividendYield from the calculator snapshot', async () => { |
|||
const activities: Activity[] = [ |
|||
{ |
|||
...activityDummyData, |
|||
currency: 'USD', |
|||
date: new Date('2023-06-01'), |
|||
feeInAssetProfileCurrency: 0, |
|||
feeInBaseCurrency: 0, |
|||
quantity: 2, |
|||
SymbolProfile: { |
|||
...symbolProfileDummyData, |
|||
currency: 'USD', |
|||
dataSource: 'YAHOO', |
|||
name: 'Microsoft Inc.', |
|||
symbol: 'MSFT' |
|||
}, |
|||
type: ActivityType.BUY, |
|||
unitPrice: 50, |
|||
unitPriceInAssetProfileCurrency: 50, |
|||
value: 100, |
|||
valueInBaseCurrency: 100 |
|||
}, |
|||
{ |
|||
...activityDummyData, |
|||
currency: 'USD', |
|||
date: new Date('2023-06-02'), |
|||
feeInAssetProfileCurrency: 0, |
|||
feeInBaseCurrency: 0, |
|||
quantity: 1, |
|||
SymbolProfile: { |
|||
...symbolProfileDummyData, |
|||
currency: 'USD', |
|||
dataSource: 'YAHOO', |
|||
name: 'Microsoft Inc.', |
|||
symbol: 'MSFT' |
|||
}, |
|||
type: ActivityType.SELL, |
|||
unitPrice: 40, |
|||
unitPriceInAssetProfileCurrency: 40, |
|||
value: 40, |
|||
valueInBaseCurrency: 40 |
|||
} |
|||
]; |
|||
|
|||
const exchangeRateDataService = { |
|||
toCurrency: jest.fn((value: number) => value) |
|||
}; |
|||
|
|||
const orderService = { |
|||
getOrders: jest.fn().mockResolvedValue({ activities }) |
|||
}; |
|||
|
|||
const userService = { |
|||
user: jest.fn().mockResolvedValue({ |
|||
id: userDummyData.id, |
|||
settings: { |
|||
settings: { |
|||
baseCurrency: DEFAULT_CURRENCY, |
|||
emergencyFund: 0 |
|||
} |
|||
} |
|||
}) |
|||
}; |
|||
|
|||
const accountService = { |
|||
getCashDetails: jest.fn().mockResolvedValue({ |
|||
balanceInBaseCurrency: 1000 |
|||
}) |
|||
}; |
|||
|
|||
const impersonationService = { |
|||
validateImpersonationId: jest.fn().mockResolvedValue(undefined) |
|||
}; |
|||
|
|||
const request = { |
|||
user: { |
|||
id: userDummyData.id, |
|||
settings: { settings: { baseCurrency: DEFAULT_CURRENCY } } |
|||
} |
|||
} as RequestWithUser; |
|||
|
|||
const portfolioCalculator = { |
|||
getDividendInBaseCurrency: jest.fn().mockResolvedValue(new Big(12)), |
|||
getFeesInBaseCurrency: jest.fn().mockResolvedValue(new Big(4)), |
|||
getInterestInBaseCurrency: jest.fn().mockResolvedValue(new Big(1)), |
|||
getLiabilitiesInBaseCurrency: jest.fn().mockResolvedValue(new Big(6)), |
|||
getSnapshot: jest.fn().mockResolvedValue({ |
|||
annualizedDividendYield: 0.0123, |
|||
currentValueInBaseCurrency: new Big(500), |
|||
totalInvestment: new Big(400) |
|||
}), |
|||
getStartDate: jest.fn().mockReturnValue(new Date('2023-01-01')) |
|||
} as unknown as PortfolioCalculator; |
|||
|
|||
const service = new PortfolioService( |
|||
{} as any, |
|||
accountService as any, |
|||
{} as any, |
|||
{} as any, |
|||
{} as any, |
|||
exchangeRateDataService as any, |
|||
{} as any, |
|||
impersonationService as any, |
|||
orderService as any, |
|||
request, |
|||
{} as any, |
|||
{} as any, |
|||
userService as any |
|||
); |
|||
|
|||
jest.spyOn(service, 'getPerformance').mockResolvedValue({ |
|||
performance: { |
|||
netPerformance: 20, |
|||
netPerformancePercentage: 0.05, |
|||
netPerformancePercentageWithCurrencyEffect: 0.05, |
|||
netPerformanceWithCurrencyEffect: 20 |
|||
} |
|||
} as any); |
|||
|
|||
const summary = await (service as any).getSummary({ |
|||
balanceInBaseCurrency: 1000, |
|||
emergencyFundHoldingsValueInBaseCurrency: 0, |
|||
filteredValueInBaseCurrency: new Big(200), |
|||
impersonationId: userDummyData.id, |
|||
portfolioCalculator, |
|||
userCurrency: DEFAULT_CURRENCY, |
|||
userId: userDummyData.id |
|||
}); |
|||
|
|||
expect(portfolioCalculator.getSnapshot).toHaveBeenCalledTimes(1); |
|||
expect(summary).toMatchObject({ |
|||
annualizedDividendYield: 0.0123, |
|||
cash: 1000, |
|||
committedFunds: 60, |
|||
dividendInBaseCurrency: 12, |
|||
fees: 4, |
|||
grossPerformance: 24, |
|||
grossPerformanceWithCurrencyEffect: 24, |
|||
interestInBaseCurrency: 1, |
|||
liabilitiesInBaseCurrency: 6, |
|||
totalBuy: 100, |
|||
totalInvestment: 400, |
|||
totalSell: 40, |
|||
totalValueInBaseCurrency: 1494 |
|||
}); |
|||
expect(summary.activityCount).toBe(2); |
|||
expect(summary.dateOfFirstActivity).toEqual(new Date('2023-01-01')); |
|||
}); |
|||
}); |
|||
}); |
|||
Loading…
Reference in new issue