mirror of https://github.com/ghostfolio/ghostfolio
committed by
GitHub
4 changed files with 123 additions and 1 deletions
@ -0,0 +1,30 @@ |
|||||
|
const NodeEnvironment = require('jest-environment-node').TestEnvironment; |
||||
|
|
||||
|
// Jest gives each test file a copy of `process`, so a test cannot change the
|
||||
|
// time zone at run time. This environment sets `TZ` on the real process of the
|
||||
|
// worker, which resets the internal date cache of Node.
|
||||
|
class TimeZoneEnvironment extends NodeEnvironment { |
||||
|
constructor(config, context) { |
||||
|
super(config, context); |
||||
|
|
||||
|
this.timeZone = config.projectConfig.testEnvironmentOptions?.timeZone; |
||||
|
} |
||||
|
|
||||
|
async setup() { |
||||
|
this.previousTimeZone = process.env.TZ; |
||||
|
|
||||
|
if (this.timeZone) { |
||||
|
process.env.TZ = this.timeZone; |
||||
|
} |
||||
|
|
||||
|
await super.setup(); |
||||
|
} |
||||
|
|
||||
|
async teardown() { |
||||
|
await super.teardown(); |
||||
|
|
||||
|
process.env.TZ = this.previousTimeZone; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
module.exports = TimeZoneEnvironment; |
||||
@ -0,0 +1,83 @@ |
|||||
|
/** |
||||
|
* @jest-environment <rootDir>/jest-environment-tz.js |
||||
|
* @jest-environment-options {"timeZone": "Europe/Berlin"} |
||||
|
*/ |
||||
|
import { DataGatheringService } from './data-gathering.service'; |
||||
|
|
||||
|
describe('DataGatheringService in a time zone with daylight saving time', () => { |
||||
|
let dataGatheringQueue: { addBulk: jest.Mock; clean: jest.Mock }; |
||||
|
let dataGatheringService: DataGatheringService; |
||||
|
let prismaService: { marketData: { groupBy: jest.Mock; upsert: jest.Mock } }; |
||||
|
|
||||
|
beforeEach(() => { |
||||
|
dataGatheringQueue = { |
||||
|
addBulk: jest.fn().mockResolvedValue([]), |
||||
|
clean: jest.fn().mockResolvedValue([]) |
||||
|
}; |
||||
|
prismaService = { |
||||
|
marketData: { |
||||
|
groupBy: jest.fn().mockResolvedValue([]), |
||||
|
upsert: jest.fn().mockResolvedValue({}) |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
dataGatheringService = new DataGatheringService( |
||||
|
null, |
||||
|
dataGatheringQueue as any, |
||||
|
{ getHistoricalRaw: jest.fn() } as any, |
||||
|
null, |
||||
|
null, |
||||
|
prismaService as any, |
||||
|
null, |
||||
|
null |
||||
|
); |
||||
|
}); |
||||
|
|
||||
|
afterEach(() => { |
||||
|
jest.useRealTimers(); |
||||
|
}); |
||||
|
|
||||
|
describe('getAssetProfileIdentifiersWithRecentMarketData', () => { |
||||
|
it('keeps a market price of yesterday when the clocks go forward', async () => { |
||||
|
// The clocks in Berlin go forward on 2026-03-29, so a local day is 23 hours
|
||||
|
jest.useFakeTimers().setSystemTime(new Date('2026-03-29T23:00:00.000Z')); |
||||
|
|
||||
|
prismaService.marketData.groupBy.mockResolvedValue([ |
||||
|
{ |
||||
|
_max: { date: new Date('2026-03-28T00:00:00.000Z') }, |
||||
|
dataSource: 'COINGECKO', |
||||
|
symbol: 'bitcoin' |
||||
|
} |
||||
|
]); |
||||
|
|
||||
|
const assetProfileIdentifiers = |
||||
|
await dataGatheringService[ |
||||
|
'getAssetProfileIdentifiersWithRecentMarketData' |
||||
|
](); |
||||
|
|
||||
|
expect(assetProfileIdentifiers).toEqual([ |
||||
|
{ dataSource: 'COINGECKO', symbol: 'bitcoin' } |
||||
|
]); |
||||
|
}); |
||||
|
|
||||
|
it('drops a market price of two days ago when the clocks go back', async () => { |
||||
|
// The clocks in Berlin go back on 2026-10-25, so a local day is 25 hours
|
||||
|
jest.useFakeTimers().setSystemTime(new Date('2026-10-26T00:00:00.000Z')); |
||||
|
|
||||
|
prismaService.marketData.groupBy.mockResolvedValue([ |
||||
|
{ |
||||
|
_max: { date: new Date('2026-10-24T00:00:00.000Z') }, |
||||
|
dataSource: 'COINGECKO', |
||||
|
symbol: 'bitcoin' |
||||
|
} |
||||
|
]); |
||||
|
|
||||
|
const assetProfileIdentifiers = |
||||
|
await dataGatheringService[ |
||||
|
'getAssetProfileIdentifiersWithRecentMarketData' |
||||
|
](); |
||||
|
|
||||
|
expect(assetProfileIdentifiers).toEqual([]); |
||||
|
}); |
||||
|
}); |
||||
|
}); |
||||
Loading…
Reference in new issue