UNPKG

@lobehub/chat

Version:

Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.

342 lines (273 loc) • 12.2 kB
import { afterEach, describe, expect, it, vi } from 'vitest'; import { createHyperStorage } from './index'; import { createIndexedDB } from './indexedDB'; import { createKeyMapper } from './keyMapper'; import { createLocalStorage } from './localStorage'; import { creatUrlStorage } from './urlStorage'; // Mock the dependent modules vi.mock('./indexedDB', () => { return { createIndexedDB: vi.fn(), }; }); vi.mock('./localStorage', () => ({ createLocalStorage: vi.fn(), })); vi.mock('./urlStorage', () => ({ creatUrlStorage: vi.fn(), })); vi.mock('./keyMapper', () => ({ createKeyMapper: vi.fn().mockReturnValue({ mapStateKeyToStorageKey: vi.fn((k) => k), getStateKeyFromStorageKey: vi.fn((k) => k), }), })); afterEach(() => { vi.mocked(createKeyMapper).mockClear(); }); describe('createHyperStorage', () => { it('should create storage with default configuration if no options provided', async () => { const storage = createHyperStorage({}); expect(storage).toBeDefined(); // Add more assertions to verify default behavior }); it('should skip local storage if explicitly disabled', async () => { const storage = createHyperStorage({ localStorage: false }); await storage.setItem('key', { state: {}, version: 1 }); // The setItem should not call the local storage functions const indexDBSetItemMock = vi.fn(); vi.mocked(createIndexedDB).mockImplementation(() => ({ getItem: vi.fn(), removeItem: vi.fn(), setItem: indexDBSetItemMock, })); const localStorageSetItemMock = vi.fn(); vi.mocked(createLocalStorage).mockImplementation(() => ({ getItem: vi.fn(), removeItem: vi.fn(), setItem: localStorageSetItemMock, })); expect(indexDBSetItemMock).not.toHaveBeenCalled(); expect(localStorageSetItemMock).not.toHaveBeenCalled(); }); it('should use indexedDB mode if set in local storage options', async () => { // The setItem should call the indexedDB functions // The setItem should not call the local storage functions const indexDBSetItemMock = vi.fn(); vi.mocked(createIndexedDB).mockImplementation(() => ({ getItem: vi.fn(), removeItem: vi.fn(), setItem: indexDBSetItemMock, })); const storage = createHyperStorage({ localStorage: { mode: 'indexedDB', dbName: 'testDB', selectors: [] }, }); await storage.setItem('key', { state: {}, version: 1 }); expect(indexDBSetItemMock).toHaveBeenCalled(); }); it('should use the provided dbName for indexedDB', async () => { const dbName = 'customDB'; createHyperStorage({ localStorage: { mode: 'indexedDB', dbName, selectors: [] } }); expect(vi.mocked(createIndexedDB)).toHaveBeenCalledWith(dbName); }); it('should handle URL storage if URL options are provided', async () => { const urlMode = 'hash'; const setItemMock = vi.fn(); vi.mocked(creatUrlStorage).mockImplementation(() => ({ getItem: vi.fn(), removeItem: vi.fn(), setItem: setItemMock, })); const storage = createHyperStorage({ url: { mode: urlMode, selectors: [] } }); await storage.setItem('key', { state: {}, version: 1 }); expect(creatUrlStorage).toHaveBeenCalledWith(urlMode); // The setItem should call the urlStorage functions expect(setItemMock).toHaveBeenCalled(); }); describe('getItem method', () => { it('should retrieve item from indexedDB when useIndexedDB is true', async () => { const indexedDBGetItemMock = vi .fn() .mockResolvedValue({ state: { key: 'value' }, version: 1 }); vi.mocked(createIndexedDB).mockImplementation(() => ({ getItem: indexedDBGetItemMock, removeItem: vi.fn(), setItem: vi.fn(), })); const storage = createHyperStorage({ localStorage: { mode: 'indexedDB', selectors: [] } }); const item = await storage.getItem('key'); expect(indexedDBGetItemMock).toHaveBeenCalledWith('key'); expect(item).toEqual({ state: { key: 'value' }, version: 1 }); }); it('should fallback to localStorage if item not found in indexedDB', async () => { const indexedDBGetItemMock = vi.fn().mockResolvedValue(undefined); const localStorageGetItemMock = vi .fn() .mockReturnValue({ state: { key: 'value' }, version: 1 }); vi.mocked(createIndexedDB).mockImplementation(() => ({ getItem: indexedDBGetItemMock, removeItem: vi.fn(), setItem: vi.fn(), })); vi.mocked(createLocalStorage).mockImplementation(() => ({ getItem: localStorageGetItemMock, removeItem: vi.fn(), setItem: vi.fn(), })); const storage = createHyperStorage({ localStorage: { mode: 'indexedDB', selectors: [] } }); const item = await storage.getItem('key'); expect(indexedDBGetItemMock).toHaveBeenCalledWith('key'); expect(localStorageGetItemMock).toHaveBeenCalledWith('key'); expect(item).toEqual({ state: { key: 'value' }, version: 1 }); }); it('should not attempt to retrieve from any storage if skipLocalStorage is true', async () => { const storage = createHyperStorage({ localStorage: false }); const item = await storage.getItem('key'); const indexedDBGetItemMock = vi.fn().mockResolvedValue(undefined); const localStorageGetItemMock = vi .fn() .mockReturnValue({ state: { key: 'value' }, version: 1 }); vi.mocked(createIndexedDB).mockImplementation(() => ({ getItem: indexedDBGetItemMock, removeItem: vi.fn(), setItem: vi.fn(), })); vi.mocked(createLocalStorage).mockImplementation(() => ({ getItem: localStorageGetItemMock, removeItem: vi.fn(), setItem: vi.fn(), })); expect(indexedDBGetItemMock).not.toHaveBeenCalled(); expect(localStorageGetItemMock).not.toHaveBeenCalled(); expect(item).toEqual({ state: {}, version: undefined }); }); describe('getItem method with URL storage', () => { it('should override state from URL storage if hasUrl is true', async () => { const urlStorageGetItemMock = vi.fn().mockReturnValue({ state: { urlKey: 'urlValue' } }); vi.mocked(creatUrlStorage).mockImplementation(() => ({ getItem: urlStorageGetItemMock, removeItem: vi.fn(), setItem: vi.fn(), })); // Mock createKeyMapper to simulate state key mapping from URL storage keys vi.mocked(createKeyMapper).mockReturnValue({ mapStateKeyToStorageKey: vi.fn((k) => k), getStateKeyFromStorageKey: vi.fn((k) => (k === 'urlKey' ? 'mappedKey' : undefined)), }); const storage = createHyperStorage({ url: { mode: 'hash', selectors: [] }, localStorage: false, }); const item = await storage.getItem('key'); expect(urlStorageGetItemMock).toHaveBeenCalled(); expect(item?.state).toEqual({ mappedKey: 'urlValue' }); }); it('should not include URL storage state if key mapping is undefined', async () => { const urlStorageGetItemMock = vi.fn().mockReturnValue({ state: { urlKey: 'urlValue' } }); vi.mocked(creatUrlStorage).mockImplementation(() => ({ getItem: urlStorageGetItemMock, removeItem: vi.fn(), setItem: vi.fn(), })); // Mock createKeyMapper to simulate state key mapping from URL storage keys vi.mocked(createKeyMapper).mockReturnValue({ mapStateKeyToStorageKey: vi.fn((k) => k), getStateKeyFromStorageKey: vi.fn(() => undefined), // No key will be mapped }); const storage = createHyperStorage({ url: { mode: 'hash', selectors: [] }, localStorage: false, }); const item = await storage.getItem('key'); expect(urlStorageGetItemMock).toHaveBeenCalled(); expect(item?.state).toEqual({}); // No state from URL storage should be included }); it('should not attempt to retrieve from URL storage if hasUrl is false', async () => { const urlStorageGetItemMock = vi.fn(); vi.mocked(creatUrlStorage).mockImplementation(() => ({ getItem: urlStorageGetItemMock, removeItem: vi.fn(), setItem: vi.fn(), })); const storage = createHyperStorage({ localStorage: false }); // No URL options provided await storage.getItem('key'); expect(urlStorageGetItemMock).not.toHaveBeenCalled(); }); }); }); describe('removeItem method', () => { it('should remove item from indexedDB when useIndexedDB is true', async () => { const indexedDBRemoveItemMock = vi.fn().mockResolvedValue(undefined); vi.mocked(createIndexedDB).mockImplementation(() => ({ getItem: vi.fn(), removeItem: indexedDBRemoveItemMock, setItem: vi.fn(), })); const storage = createHyperStorage({ localStorage: { mode: 'indexedDB', selectors: [] } }); await storage.removeItem('key'); expect(indexedDBRemoveItemMock).toHaveBeenCalledWith('key'); }); it('should remove item from localStorage when useIndexedDB is false', async () => { const localStorageRemoveItemMock = vi.fn().mockResolvedValue(undefined); vi.mocked(createLocalStorage).mockImplementation(() => ({ getItem: vi.fn(), removeItem: localStorageRemoveItemMock, setItem: vi.fn(), })); const storage = createHyperStorage({}); await storage.removeItem('key'); expect(localStorageRemoveItemMock).toHaveBeenCalledWith('key'); }); it('should remove item from URL storage if URL options provided', async () => { const urlStorageRemoveItemMock = vi.fn(); vi.mocked(creatUrlStorage).mockImplementation(() => ({ getItem: vi.fn(), removeItem: urlStorageRemoveItemMock, setItem: vi.fn(), })); // Mock createKeyMapper to simulate state key mapping from URL storage keys vi.mocked(createKeyMapper).mockReturnValue({ mapStateKeyToStorageKey: vi.fn((k) => k), getStateKeyFromStorageKey: vi.fn((k) => k), // No key will be mapped }); const storage = createHyperStorage({ url: { mode: 'hash', selectors: ['key'] } }); await storage.removeItem('key'); expect(urlStorageRemoveItemMock).toHaveBeenCalledWith('key'); }); }); describe('setItem method', () => { it('should save item to indexedDB when useIndexedDB is true', async () => { const indexedDBSetItemMock = vi.fn().mockResolvedValue(undefined); vi.mocked(createIndexedDB).mockImplementation(() => ({ getItem: vi.fn(), removeItem: vi.fn(), setItem: indexedDBSetItemMock, })); const storage = createHyperStorage({ localStorage: { mode: 'indexedDB', selectors: [] } }); await storage.setItem('key', { state: { key: 'value' }, version: 1 }); expect(indexedDBSetItemMock).toHaveBeenCalledWith('key', { key: 'value' }, 1); }); it('should save item to localStorage when useIndexedDB is false', async () => { const localStorageSetItemMock = vi.fn().mockResolvedValue(undefined); vi.mocked(createLocalStorage).mockImplementation(() => ({ getItem: vi.fn(), removeItem: vi.fn(), setItem: localStorageSetItemMock, })); const storage = createHyperStorage({}); await storage.setItem('key', { state: { key: 'value' }, version: 1 }); expect(localStorageSetItemMock).toHaveBeenCalledWith('key', { key: 'value' }, 1); }); it('should save state to URL storage if URL options provided', async () => { const urlStorageSetItemMock = vi.fn().mockResolvedValue(undefined); vi.mocked(creatUrlStorage).mockImplementation(() => ({ getItem: vi.fn(), removeItem: vi.fn(), setItem: urlStorageSetItemMock, })); const storage = createHyperStorage({ url: { mode: 'hash', selectors: [] } }); await storage.setItem('key', { state: { key: 'value' }, version: 1 }); expect(urlStorageSetItemMock).toHaveBeenCalled(); }); }); });