mirror of https://github.com/ghostfolio/ghostfolio
committed by
GitHub
15 changed files with 263 additions and 136 deletions
@ -0,0 +1,35 @@ |
|||||
|
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.previousTimeZone = process.env.TZ; |
||||
|
this.timeZone = config.projectConfig.testEnvironmentOptions?.timeZone; |
||||
|
} |
||||
|
|
||||
|
async setup() { |
||||
|
if (this.timeZone) { |
||||
|
process.env.TZ = this.timeZone; |
||||
|
} |
||||
|
|
||||
|
await super.setup(); |
||||
|
} |
||||
|
|
||||
|
async teardown() { |
||||
|
try { |
||||
|
await super.teardown(); |
||||
|
} finally { |
||||
|
if (this.previousTimeZone === undefined) { |
||||
|
delete process.env.TZ; |
||||
|
} else { |
||||
|
process.env.TZ = this.previousTimeZone; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
module.exports = TimeZoneEnvironment; |
||||
@ -0,0 +1,70 @@ |
|||||
|
/** |
||||
|
* @jest-environment <rootDir>/jest-environment-tz.js |
||||
|
* @jest-environment-options {"timeZone": "America/New_York"} |
||||
|
*/ |
||||
|
import { DataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces'; |
||||
|
|
||||
|
import { Job } from 'bull'; |
||||
|
|
||||
|
import { DataGatheringProcessor } from './data-gathering.processor'; |
||||
|
|
||||
|
describe('DataGatheringProcessor in a time zone behind UTC', () => { |
||||
|
let dataGatheringProcessor: DataGatheringProcessor; |
||||
|
let dataProviderService: { getHistoricalRaw: jest.Mock }; |
||||
|
let marketDataService: { replaceForSymbol: jest.Mock; updateMany: jest.Mock }; |
||||
|
|
||||
|
beforeAll(() => { |
||||
|
// 2026-08-23 21:30 in New York, but already 2026-08-24 in UTC
|
||||
|
jest.useFakeTimers().setSystemTime(new Date('2026-08-24T01:30:00.000Z')); |
||||
|
}); |
||||
|
|
||||
|
beforeEach(() => { |
||||
|
dataProviderService = { |
||||
|
getHistoricalRaw: jest.fn().mockResolvedValue({ |
||||
|
'COINGECKO-bitcoin': { |
||||
|
'2026-08-21': { marketPrice: 5 }, |
||||
|
'2026-08-22': { marketPrice: 6 }, |
||||
|
'2026-08-23': { marketPrice: 7 } |
||||
|
} |
||||
|
}) |
||||
|
}; |
||||
|
marketDataService = { |
||||
|
replaceForSymbol: jest.fn(), |
||||
|
updateMany: jest.fn() |
||||
|
}; |
||||
|
|
||||
|
dataGatheringProcessor = new DataGatheringProcessor( |
||||
|
null, |
||||
|
dataProviderService as any, |
||||
|
marketDataService as any, |
||||
|
null |
||||
|
); |
||||
|
}); |
||||
|
|
||||
|
afterAll(() => { |
||||
|
jest.useRealTimers(); |
||||
|
}); |
||||
|
|
||||
|
it('gathers up to the last complete UTC day and does not shift the dates', async () => { |
||||
|
await dataGatheringProcessor.gatherHistoricalMarketData({ |
||||
|
data: { |
||||
|
dataSource: 'COINGECKO', |
||||
|
// The queue enqueues dates at midnight (UTC), not at local midnight
|
||||
|
date: new Date('2026-08-21T00:00:00.000Z').toISOString(), |
||||
|
symbol: 'bitcoin' |
||||
|
} |
||||
|
} as unknown as Job<DataGatheringItem>); |
||||
|
|
||||
|
const { data } = marketDataService.updateMany.mock.calls[0][0]; |
||||
|
|
||||
|
expect( |
||||
|
data.map(({ date, marketPrice }) => { |
||||
|
return { date, marketPrice }; |
||||
|
}) |
||||
|
).toEqual([ |
||||
|
{ date: new Date('2026-08-21T00:00:00.000Z'), marketPrice: 5 }, |
||||
|
{ date: new Date('2026-08-22T00:00:00.000Z'), marketPrice: 6 }, |
||||
|
{ date: new Date('2026-08-23T00:00:00.000Z'), marketPrice: 7 } |
||||
|
]); |
||||
|
}); |
||||
|
}); |
||||
Loading…
Reference in new issue