meta-log-db
Version:
Native database package for Meta-Log (ProLog, DataLog, R5RS)
256 lines (203 loc) • 6.78 kB
text/typescript
/**
* IndexedDB Storage Tests
*
* Tests for IndexedDB persistence layer implementation.
*
* @see {@link https://github.com/automaton-system/meta-log-db/blob/main/docs/27-Meta-Log-Browser-Db/INDEXEDDB-PERSISTENCE.md IndexedDB Persistence Guide}
* @see {@link https://github.com/automaton-system/meta-log-db/blob/main/docs/27-Meta-Log-Browser-Db/README.md Meta-Log Browser Database Documentation}
*
* Related Documentation:
* - meta-log-browser-db-indexeddb-guide: Complete IndexedDB guide
* - meta-log-browser-db-readme: Browser database overview
* - meta-log-browser-db-api-reference: IndexedDBStorage API reference
*
* Test Coverage:
* - IndexedDB initialization
* - Object store operations (get, set, delete, clear)
* - Key management
* - Error handling
* - Database lifecycle
*/
import { IndexedDBStorage } from '../indexeddb-storage';
// Mock IndexedDB
const mockIndexedDB = {
open: jest.fn(),
deleteDatabase: jest.fn()
};
// @ts-ignore
global.indexedDB = mockIndexedDB;
describe('IndexedDBStorage', () => {
let storage: IndexedDBStorage;
let mockDb: any;
beforeEach(() => {
storage = new IndexedDBStorage({ dbName: 'test-db' });
mockDb = {
objectStoreNames: {
contains: jest.fn().mockReturnValue(false)
},
createObjectStore: jest.fn(),
transaction: jest.fn(),
close: jest.fn()
};
mockIndexedDB.open.mockReturnValue({
onsuccess: null,
onerror: null,
onupgradeneeded: null,
result: mockDb
});
});
describe('init', () => {
it('should initialize IndexedDB connection', async () => {
const request = mockIndexedDB.open();
request.onsuccess({ target: { result: mockDb } });
await storage.init();
expect(mockIndexedDB.open).toHaveBeenCalledWith('test-db', 1);
});
it('should create object stores on upgrade', async () => {
const request = mockIndexedDB.open();
request.onupgradeneeded({
target: {
result: mockDb
}
});
await storage.init();
expect(mockDb.createObjectStore).toHaveBeenCalledWith('files', { keyPath: 'key' });
expect(mockDb.createObjectStore).toHaveBeenCalledWith('triples', { keyPath: 'id', autoIncrement: true });
expect(mockDb.createObjectStore).toHaveBeenCalledWith('facts', { keyPath: 'id', autoIncrement: true });
});
});
describe('get', () => {
it('should get value from object store', async () => {
const mockStore = {
get: jest.fn().mockReturnValue({
onsuccess: null,
onerror: null,
result: { key: 'test-key', value: 'test-value' }
})
};
mockDb.transaction.mockReturnValue({
objectStore: jest.fn().mockReturnValue(mockStore)
});
await storage.init();
const request = mockStore.get();
request.onsuccess({ target: { result: { value: 'test-value' } } });
const value = await storage.get('files', 'test-key');
expect(value).toBe('test-value');
});
it('should return null if key not found', async () => {
const mockStore = {
get: jest.fn().mockReturnValue({
onsuccess: null,
onerror: null,
result: null
})
};
mockDb.transaction.mockReturnValue({
objectStore: jest.fn().mockReturnValue(mockStore)
});
await storage.init();
const request = mockStore.get();
request.onsuccess({ target: { result: null } });
const value = await storage.get('files', 'non-existent');
expect(value).toBeNull();
});
});
describe('set', () => {
it('should set value in object store', async () => {
const mockStore = {
put: jest.fn().mockReturnValue({
onsuccess: null,
onerror: null
})
};
mockDb.transaction.mockReturnValue({
objectStore: jest.fn().mockReturnValue(mockStore)
});
await storage.init();
const request = mockStore.put();
request.onsuccess({});
await storage.set('files', 'test-key', 'test-value');
expect(mockStore.put).toHaveBeenCalledWith({ key: 'test-key', value: 'test-value' });
});
});
describe('delete', () => {
it('should delete value from object store', async () => {
const mockStore = {
delete: jest.fn().mockReturnValue({
onsuccess: null,
onerror: null
})
};
mockDb.transaction.mockReturnValue({
objectStore: jest.fn().mockReturnValue(mockStore)
});
await storage.init();
const request = mockStore.delete();
request.onsuccess({});
await storage.delete('files', 'test-key');
expect(mockStore.delete).toHaveBeenCalledWith('test-key');
});
});
describe('clear', () => {
it('should clear object store', async () => {
const mockStore = {
clear: jest.fn().mockReturnValue({
onsuccess: null,
onerror: null
})
};
mockDb.transaction.mockReturnValue({
objectStore: jest.fn().mockReturnValue(mockStore)
});
await storage.init();
const request = mockStore.clear();
request.onsuccess({});
await storage.clear('files');
expect(mockStore.clear).toHaveBeenCalled();
});
});
describe('keys', () => {
it('should get all keys from object store', async () => {
const mockStore = {
getAllKeys: jest.fn().mockReturnValue({
onsuccess: null,
onerror: null,
result: ['key1', 'key2', 'key3']
})
};
mockDb.transaction.mockReturnValue({
objectStore: jest.fn().mockReturnValue(mockStore)
});
await storage.init();
const request = mockStore.getAllKeys();
request.onsuccess({ target: { result: ['key1', 'key2', 'key3'] } });
const keys = await storage.keys('files');
expect(keys).toEqual(['key1', 'key2', 'key3']);
});
});
describe('has', () => {
it('should check if key exists', async () => {
const mockStore = {
get: jest.fn().mockReturnValue({
onsuccess: null,
onerror: null,
result: { key: 'test-key', value: 'test-value' }
})
};
mockDb.transaction.mockReturnValue({
objectStore: jest.fn().mockReturnValue(mockStore)
});
await storage.init();
const request = mockStore.get();
request.onsuccess({ target: { result: { value: 'test-value' } } });
const exists = await storage.has('files', 'test-key');
expect(exists).toBe(true);
});
});
describe('close', () => {
it('should close database connection', () => {
storage.close();
expect(mockDb.close).toHaveBeenCalled();
});
});
});