import { describe, expect, it, jest, mock, beforeEach, beforeAll } from "bun:test"; import { prepareExchangeRates, wipeExchangeRates } from '../src/content/exchangeRateService.ts' import { getStorageValue, setStorageValue } from '../src/content/storage' import { storage_exchangerates, storage_exchangerates_next, storage_exchangerates_update } from "../src/content/config"; import { logDebug, logError } from '../src/content/utils/log'; declare function GM_fetch(input: string | URL | globalThis.Request, init?: RequestInit): Promise // Mock the storage functions mock.module('../src/content/storage', () => ({ setStorageValue: mock(async (key: string, value: any) => {}), getStorageValue: mock(async (key: string, defaultValue: any) => defaultValue) })); // Mock the log functions mock.module('../src/content/utils/log', () => ({ logDebug: mock(() => {}), log: mock(() => {}), logError: mock(() => {}) })); describe('wipeExchangeRates', () => { it('should clear exchange rate storage and set update timestamps', async () => { await wipeExchangeRates(); expect(setStorageValue).toHaveBeenCalledWith(storage_exchangerates, null); expect(setStorageValue).toHaveBeenCalledWith(storage_exchangerates_update, expect.any(String)); expect(setStorageValue).toHaveBeenCalledWith(storage_exchangerates_next, expect.any(String)); expect(logDebug).toHaveBeenCalled(); }); }); describe('prepareExchangeRates', () => { const mockRates = { USD: 1, EUR: 0.85, GBP: 0.73 }; beforeAll(() => { jest .spyOn(global, 'GM_fetch') .mockImplementation(() => { return Promise.resolve({ ok: false, status: 500 } as Response); }); }); beforeEach(() => { jest.clearAllMocks(); }); it('should return stored rates if they are up to date', async () => { (getStorageValue as jest.Mock).mockImplementation(async (key) => { if (key === storage_exchangerates) return mockRates; if (key === storage_exchangerates_update) return new Date().toISOString(); if (key === storage_exchangerates_next) return new Date(Date.now() + 100000).toISOString(); return null; }); const rates = await prepareExchangeRates(); expect(rates).toEqual(mockRates); expect(GM_fetch).not.toHaveBeenCalled(); }); it('should fetch new rates when they are expired', async () => { (getStorageValue as jest.Mock).mockImplementation(async (key) => { if (key === storage_exchangerates_update) return new Date(Date.now() - 100000).toISOString(); if (key === storage_exchangerates_next) return new Date(Date.now() - 50000).toISOString(); return null; }); (GM_fetch as jest.Mock).mockResolvedValue({ ok: true, json: async () => ({ rates: mockRates, time_next_update_utc: new Date(Date.now() + 100000).toISOString() }) }); const rates = await prepareExchangeRates(); expect(rates).toEqual(mockRates); expect(GM_fetch).toHaveBeenCalled(); expect(setStorageValue).toHaveBeenCalledWith(storage_exchangerates, mockRates); }); it('should handle fetch errors gracefully', async () => { (getStorageValue as jest.Mock).mockResolvedValue(null); (GM_fetch as jest.Mock).mockResolvedValueOnce({ ok: false, status: 500 } as Response); const rates = await prepareExchangeRates(); expect(rates).toBeNull(); expect(logError).toHaveBeenCalled(); }); });