|
|
@ -1,9 +1,18 @@ |
|
|
|
|
|
import type { ActivitiesService } from '@ghostfolio/api/app/activities/activities.service'; |
|
|
import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service'; |
|
|
import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service'; |
|
|
import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service'; |
|
|
import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service'; |
|
|
import { PropertyService } from '@ghostfolio/api/services/property/property.service'; |
|
|
import { PropertyService } from '@ghostfolio/api/services/property/property.service'; |
|
|
|
|
|
import { resetHours } from '@ghostfolio/common/helper'; |
|
|
import { AssetProfileIdentifier } from '@ghostfolio/common/interfaces'; |
|
|
import { AssetProfileIdentifier } from '@ghostfolio/common/interfaces'; |
|
|
|
|
|
|
|
|
import { DataSource, MarketData } from '@prisma/client'; |
|
|
import { utc } from '@date-fns/utc'; |
|
|
|
|
|
import { |
|
|
|
|
|
DataSource, |
|
|
|
|
|
MarketData, |
|
|
|
|
|
Order, |
|
|
|
|
|
Type as ActivityType |
|
|
|
|
|
} from '@prisma/client'; |
|
|
|
|
|
import { endOfDay, subDays } from 'date-fns'; |
|
|
|
|
|
|
|
|
import { CurrentRateService } from './current-rate.service'; |
|
|
import { CurrentRateService } from './current-rate.service'; |
|
|
import { DateQuery } from './interfaces/date-query.interface'; |
|
|
import { DateQuery } from './interfaces/date-query.interface'; |
|
|
@ -97,12 +106,17 @@ jest.mock('@ghostfolio/api/services/property/property.service', () => { |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
describe('CurrentRateService', () => { |
|
|
describe('CurrentRateService', () => { |
|
|
|
|
|
let activitiesService: ActivitiesService; |
|
|
let currentRateService: CurrentRateService; |
|
|
let currentRateService: CurrentRateService; |
|
|
let dataProviderService: DataProviderService; |
|
|
let dataProviderService: DataProviderService; |
|
|
let marketDataService: MarketDataService; |
|
|
let marketDataService: MarketDataService; |
|
|
let propertyService: PropertyService; |
|
|
let propertyService: PropertyService; |
|
|
|
|
|
|
|
|
beforeAll(async () => { |
|
|
beforeAll(async () => { |
|
|
|
|
|
activitiesService = { |
|
|
|
|
|
getLatestActivity: jest.fn() |
|
|
|
|
|
} as unknown as ActivitiesService; |
|
|
|
|
|
|
|
|
propertyService = new PropertyService(null); |
|
|
propertyService = new PropertyService(null); |
|
|
|
|
|
|
|
|
dataProviderService = new DataProviderService( |
|
|
dataProviderService = new DataProviderService( |
|
|
@ -117,13 +131,17 @@ describe('CurrentRateService', () => { |
|
|
marketDataService = new MarketDataService(null); |
|
|
marketDataService = new MarketDataService(null); |
|
|
|
|
|
|
|
|
currentRateService = new CurrentRateService( |
|
|
currentRateService = new CurrentRateService( |
|
|
null, |
|
|
activitiesService, |
|
|
dataProviderService, |
|
|
dataProviderService, |
|
|
marketDataService, |
|
|
marketDataService, |
|
|
null |
|
|
null |
|
|
); |
|
|
); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
afterEach(() => { |
|
|
|
|
|
jest.restoreAllMocks(); |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
it('getValues', async () => { |
|
|
it('getValues', async () => { |
|
|
expect( |
|
|
expect( |
|
|
await currentRateService.getValues({ |
|
|
await currentRateService.getValues({ |
|
|
@ -152,4 +170,82 @@ describe('CurrentRateService', () => { |
|
|
] |
|
|
] |
|
|
}); |
|
|
}); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
it('getValues with a missing quote falls back to the latest market price', async () => { |
|
|
|
|
|
const today = resetHours(new Date()); |
|
|
|
|
|
const yesterday = subDays(today, 1, { in: utc }); |
|
|
|
|
|
|
|
|
|
|
|
jest.spyOn(dataProviderService, 'getQuotes').mockResolvedValue({}); |
|
|
|
|
|
|
|
|
|
|
|
jest.spyOn(marketDataService, 'getRangeCount').mockResolvedValue(1); |
|
|
|
|
|
|
|
|
|
|
|
jest.spyOn(marketDataService, 'getRange').mockResolvedValue([ |
|
|
|
|
|
{ |
|
|
|
|
|
createdAt: yesterday, |
|
|
|
|
|
dataSource: DataSource.YAHOO, |
|
|
|
|
|
date: yesterday, |
|
|
|
|
|
id: '3b1a3f4c-3d2b-4a19-9f5a-5c0f5b4a2e11', |
|
|
|
|
|
isCarriedForward: false, |
|
|
|
|
|
marketPrice: 1841.823902, |
|
|
|
|
|
state: 'CLOSE', |
|
|
|
|
|
symbol: 'AMZN' |
|
|
|
|
|
} |
|
|
|
|
|
]); |
|
|
|
|
|
|
|
|
|
|
|
const getLatestActivitySpy = jest.spyOn( |
|
|
|
|
|
activitiesService, |
|
|
|
|
|
'getLatestActivity' |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
const { errors, values } = await currentRateService.getValues({ |
|
|
|
|
|
dataGatheringItems: [{ dataSource: DataSource.YAHOO, symbol: 'AMZN' }], |
|
|
|
|
|
dateQuery: { gte: yesterday, lt: endOfDay(new Date()) } |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
expect(errors).toEqual([{ dataSource: DataSource.YAHOO, symbol: 'AMZN' }]); |
|
|
|
|
|
|
|
|
|
|
|
expect(values).toContainEqual({ |
|
|
|
|
|
dataSource: DataSource.YAHOO, |
|
|
|
|
|
date: today, |
|
|
|
|
|
marketPrice: 1841.823902, |
|
|
|
|
|
symbol: 'AMZN' |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
expect(getLatestActivitySpy).not.toHaveBeenCalled(); |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
it('getValues with a missing quote and without market data falls back to the latest buy or sell activity', async () => { |
|
|
|
|
|
const today = resetHours(new Date()); |
|
|
|
|
|
|
|
|
|
|
|
jest.spyOn(dataProviderService, 'getQuotes').mockResolvedValue({}); |
|
|
|
|
|
|
|
|
|
|
|
jest.spyOn(marketDataService, 'getRangeCount').mockResolvedValue(0); |
|
|
|
|
|
|
|
|
|
|
|
const getLatestActivitySpy = jest |
|
|
|
|
|
.spyOn(activitiesService, 'getLatestActivity') |
|
|
|
|
|
.mockResolvedValue({ unitPrice: 1847.839966 } as Order); |
|
|
|
|
|
|
|
|
|
|
|
const { values } = await currentRateService.getValues({ |
|
|
|
|
|
dataGatheringItems: [{ dataSource: DataSource.YAHOO, symbol: 'AMZN' }], |
|
|
|
|
|
dateQuery: { |
|
|
|
|
|
gte: subDays(today, 1, { in: utc }), |
|
|
|
|
|
lt: endOfDay(new Date()) |
|
|
|
|
|
} |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
expect(getLatestActivitySpy).toHaveBeenCalledWith({ |
|
|
|
|
|
dataSource: DataSource.YAHOO, |
|
|
|
|
|
symbol: 'AMZN', |
|
|
|
|
|
types: [ActivityType.BUY, ActivityType.SELL] |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
expect(values).toEqual([ |
|
|
|
|
|
{ |
|
|
|
|
|
dataSource: DataSource.YAHOO, |
|
|
|
|
|
date: today, |
|
|
|
|
|
marketPrice: 1847.839966, |
|
|
|
|
|
symbol: 'AMZN' |
|
|
|
|
|
} |
|
|
|
|
|
]); |
|
|
|
|
|
}); |
|
|
}); |
|
|
}); |
|
|
|