mirror of
https://github.com/johndoe6345789/low-code-react-app-b.git
synced 2026-04-24 13:44:54 +00:00
Add tests for unified storage initialization
This commit is contained in:
145
src/lib/unified-storage.test.ts
Normal file
145
src/lib/unified-storage.test.ts
Normal file
@@ -0,0 +1,145 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
const {
|
||||
callOrder,
|
||||
mockFlaskGet,
|
||||
mockIndexedGet,
|
||||
mockSQLiteGet,
|
||||
mockSparkGet,
|
||||
MockFlaskBackendAdapter,
|
||||
MockIndexedDBAdapter,
|
||||
MockSQLiteAdapter,
|
||||
MockSparkKVAdapter
|
||||
} = vi.hoisted(() => {
|
||||
const callOrder: string[] = []
|
||||
const mockFlaskGet = vi.fn<[], Promise<unknown>>()
|
||||
const mockIndexedGet = vi.fn<[], Promise<unknown>>()
|
||||
const mockSQLiteGet = vi.fn<[], Promise<unknown>>()
|
||||
const mockSparkGet = vi.fn<[], Promise<unknown>>()
|
||||
|
||||
class MockFlaskBackendAdapter {
|
||||
constructor() {
|
||||
callOrder.push('flask')
|
||||
}
|
||||
|
||||
get = mockFlaskGet
|
||||
}
|
||||
|
||||
class MockIndexedDBAdapter {
|
||||
constructor() {
|
||||
callOrder.push('indexeddb')
|
||||
}
|
||||
|
||||
get = mockIndexedGet
|
||||
}
|
||||
|
||||
class MockSQLiteAdapter {
|
||||
constructor() {
|
||||
callOrder.push('sqlite')
|
||||
}
|
||||
|
||||
get = mockSQLiteGet
|
||||
}
|
||||
|
||||
class MockSparkKVAdapter {
|
||||
constructor() {
|
||||
callOrder.push('sparkkv')
|
||||
}
|
||||
|
||||
get = mockSparkGet
|
||||
}
|
||||
|
||||
return {
|
||||
callOrder,
|
||||
mockFlaskGet,
|
||||
mockIndexedGet,
|
||||
mockSQLiteGet,
|
||||
mockSparkGet,
|
||||
MockFlaskBackendAdapter,
|
||||
MockIndexedDBAdapter,
|
||||
MockSQLiteAdapter,
|
||||
MockSparkKVAdapter
|
||||
}
|
||||
})
|
||||
|
||||
vi.mock('./unified-storage-adapters', () => ({
|
||||
FlaskBackendAdapter: MockFlaskBackendAdapter,
|
||||
IndexedDBAdapter: MockIndexedDBAdapter,
|
||||
SQLiteAdapter: MockSQLiteAdapter,
|
||||
SparkKVAdapter: MockSparkKVAdapter
|
||||
}))
|
||||
|
||||
const createLocalStorageMock = () => {
|
||||
const store = new Map<string, string>()
|
||||
|
||||
return {
|
||||
getItem: vi.fn((key: string) => store.get(key) ?? null),
|
||||
setItem: vi.fn((key: string, value: string) => {
|
||||
store.set(key, value)
|
||||
}),
|
||||
removeItem: vi.fn((key: string) => {
|
||||
store.delete(key)
|
||||
}),
|
||||
clear: vi.fn(() => {
|
||||
store.clear()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
describe('UnifiedStorage.detectAndInitialize', () => {
|
||||
let localStorageMock: ReturnType<typeof createLocalStorageMock>
|
||||
|
||||
beforeEach(() => {
|
||||
vi.resetModules()
|
||||
callOrder.length = 0
|
||||
mockFlaskGet.mockReset()
|
||||
mockIndexedGet.mockReset()
|
||||
mockSQLiteGet.mockReset()
|
||||
mockSparkGet.mockReset()
|
||||
|
||||
localStorageMock = createLocalStorageMock()
|
||||
vi.stubGlobal('localStorage', localStorageMock)
|
||||
vi.stubGlobal('window', { spark: undefined })
|
||||
|
||||
if (!(import.meta as { env?: Record<string, string | undefined> }).env) {
|
||||
;(import.meta as { env?: Record<string, string | undefined> }).env = {}
|
||||
}
|
||||
})
|
||||
|
||||
it('tries Flask before IndexedDB when prefer-flask is set', async () => {
|
||||
localStorageMock.setItem('codeforge-prefer-flask', 'true')
|
||||
mockFlaskGet.mockRejectedValue(new Error('flask down'))
|
||||
mockIndexedGet.mockResolvedValue(undefined)
|
||||
vi.stubGlobal('indexedDB', {})
|
||||
|
||||
const { unifiedStorage } = await import('./unified-storage')
|
||||
await unifiedStorage.getBackend()
|
||||
|
||||
expect(callOrder[0]).toBe('flask')
|
||||
expect(callOrder).toContain('indexeddb')
|
||||
})
|
||||
|
||||
it('falls back to IndexedDB when Flask initialization fails', async () => {
|
||||
localStorageMock.setItem('codeforge-prefer-flask', 'true')
|
||||
mockFlaskGet.mockRejectedValue(new Error('flask down'))
|
||||
mockIndexedGet.mockResolvedValue(undefined)
|
||||
vi.stubGlobal('indexedDB', {})
|
||||
|
||||
const { unifiedStorage } = await import('./unified-storage')
|
||||
const backend = await unifiedStorage.getBackend()
|
||||
|
||||
expect(backend).toBe('indexeddb')
|
||||
})
|
||||
|
||||
it('honors prefer-sqlite when configured', async () => {
|
||||
localStorageMock.setItem('codeforge-prefer-sqlite', 'true')
|
||||
mockSQLiteGet.mockResolvedValue(undefined)
|
||||
delete (globalThis as { indexedDB?: unknown }).indexedDB
|
||||
|
||||
const { unifiedStorage } = await import('./unified-storage')
|
||||
const backend = await unifiedStorage.getBackend()
|
||||
|
||||
expect(backend).toBe('sqlite')
|
||||
expect(callOrder).toContain('sqlite')
|
||||
})
|
||||
})
|
||||
@@ -19,6 +19,23 @@ class UnifiedStorage {
|
||||
const flaskEnvUrl = import.meta.env.VITE_FLASK_BACKEND_URL
|
||||
const preferSQLite = localStorage.getItem('codeforge-prefer-sqlite') === 'true'
|
||||
|
||||
if (preferFlask || flaskEnvUrl) {
|
||||
try {
|
||||
console.log('[Storage] Flask backend explicitly configured, attempting to initialize...')
|
||||
const flaskAdapter = new FlaskBackendAdapter(flaskEnvUrl)
|
||||
await Promise.race([
|
||||
flaskAdapter.get('_health_check'),
|
||||
new Promise((_, reject) => setTimeout(() => reject(new Error('Flask connection timeout')), 2000))
|
||||
])
|
||||
this.adapter = flaskAdapter
|
||||
this.backend = 'flask'
|
||||
console.log('[Storage] ✓ Using Flask backend')
|
||||
return
|
||||
} catch (error) {
|
||||
console.warn('[Storage] Flask backend not available, falling back to IndexedDB:', error)
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof indexedDB !== 'undefined') {
|
||||
try {
|
||||
console.log('[Storage] Initializing default IndexedDB backend...')
|
||||
@@ -33,26 +50,6 @@ class UnifiedStorage {
|
||||
}
|
||||
}
|
||||
|
||||
if (preferFlask || flaskEnvUrl) {
|
||||
try {
|
||||
console.log('[Storage] Flask backend explicitly configured, attempting to initialize...')
|
||||
const flaskAdapter = new FlaskBackendAdapter(flaskEnvUrl)
|
||||
const testResponse = await Promise.race([
|
||||
flaskAdapter.get('_health_check'),
|
||||
new Promise((_, reject) => setTimeout(() => reject(new Error('Flask connection timeout')), 2000))
|
||||
])
|
||||
this.adapter = flaskAdapter
|
||||
this.backend = 'flask'
|
||||
console.log('[Storage] ✓ Using Flask backend')
|
||||
return
|
||||
} catch (error) {
|
||||
console.warn('[Storage] Flask backend not available, already using IndexedDB:', error)
|
||||
if (this.adapter && this.backend === 'indexeddb') {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (preferSQLite) {
|
||||
try {
|
||||
console.log('[Storage] SQLite fallback, attempting to initialize...')
|
||||
|
||||
Reference in New Issue
Block a user