📄 src/downloader/test/meta-db.test.ts
D-OPEN SOVEREIGN
1import 'fake-indexeddb/auto';
2import { describe, it, beforeEach, expect } from 'vitest';
3import {
4 openMetaDb, closeMetaDb, _resetDbForTest,
5 getBookData, storeBookData, getAllBookData,
6 getDownloadRecord, putDownloadRecord, getAllDownloadRecords, deleteDownloadRecord,
7 getMigrationCheckpoint, saveMigrationCheckpoint, clearMigrationCheckpoint,
8 type BookData, type DownloadRecord,
9} from '../meta-db';
10
11beforeEach(async () => {
12 closeMetaDb();
13 _resetDbForTest();
14 // Fresh database per test — delete then let openMetaDb() recreate the stores.
15 await new Promise<void>((resolve) => {
16 const req = indexedDB.deleteDatabase('dl2_meta');
17 req.onsuccess = () => resolve();
18 req.onerror = () => resolve();
19 req.onblocked = () => resolve();
20 });
21});
22
23describe('meta-db', () => {
24 it('openMetaDb creates book_data, downloads and migration_checkpoints stores', async () => {
25 const db = await openMetaDb();
26 expect(db.objectStoreNames.contains('book_data')).toBe(true);
27 expect(db.objectStoreNames.contains('downloads')).toBe(true);
28 expect(db.objectStoreNames.contains('migration_checkpoints')).toBe(true);
29 });
30
31 it('openMetaDb returns the cached singleton on repeat calls', async () => {
32 const a = await openMetaDb();
33 const b = await openMetaDb();
34 expect(a).toBe(b);
35 });
36
37 it('storeBookData/getBookData round-trip', async () => {
38 const data: BookData = { txId: 'book1', dimensions: [{ width: 100, height: 200 }], parsed: true };
39 await storeBookData(data);
40 expect(await getBookData('book1')).toEqual(data);
41 });
42
43 it('getBookData returns undefined for a miss', async () => {
44 expect(await getBookData('nope')).toBeUndefined();
45 });
46
47 it('getAllBookData returns every stored record', async () => {
48 await storeBookData({ txId: 'b1', dimensions: [], parsed: false });
49 await storeBookData({ txId: 'b2', dimensions: [], parsed: true });
50 const all = await getAllBookData();
51 expect(all.map(b => b.txId).sort()).toEqual(['b1', 'b2']);
52 });
53
54 it('putDownloadRecord/getDownloadRecord round-trip', async () => {
55 const rec: DownloadRecord = {
56 txId: 'dl1', fileType: 'pdf', sizeBytes: 1234,
57 downloadedAt: 1000, lastAccessedAt: 2000,
58 };
59 await putDownloadRecord(rec);
60 expect(await getDownloadRecord('dl1')).toEqual(rec);
61 });
62
63 it('putDownloadRecord overwrites an existing record with the same txId', async () => {
64 await putDownloadRecord({ txId: 'dl2', fileType: 'pdf', sizeBytes: 1, downloadedAt: 0, lastAccessedAt: 0 });
65 await putDownloadRecord({ txId: 'dl2', fileType: 'pdf', sizeBytes: 999, downloadedAt: 5, lastAccessedAt: 5 });
66 const rec = await getDownloadRecord('dl2');
67 expect(rec?.sizeBytes).toBe(999);
68 });
69
70 it('getAllDownloadRecords returns every record', async () => {
71 await putDownloadRecord({ txId: 'a', fileType: 'pdf', sizeBytes: 1, downloadedAt: 0, lastAccessedAt: 0 });
72 await putDownloadRecord({ txId: 'b', fileType: 'preview', sizeBytes: 2, downloadedAt: 0, lastAccessedAt: 0 });
73 const all = await getAllDownloadRecords();
74 expect(all.length).toBe(2);
75 });
76
77 it('deleteDownloadRecord removes the entry', async () => {
78 await putDownloadRecord({ txId: 'to-delete', fileType: 'pdf', sizeBytes: 1, downloadedAt: 0, lastAccessedAt: 0 });
79 await deleteDownloadRecord('to-delete');
80 expect(await getDownloadRecord('to-delete')).toBeUndefined();
81 });
82
83 it('getMigrationCheckpoint defaults to 0 when unset', async () => {
84 expect(await getMigrationCheckpoint('cache-x')).toBe(0);
85 });
86
87 it('saveMigrationCheckpoint/getMigrationCheckpoint round-trip', async () => {
88 await saveMigrationCheckpoint('cache-x', 42);
89 expect(await getMigrationCheckpoint('cache-x')).toBe(42);
90 });
91
92 it('clearMigrationCheckpoint resets to the default', async () => {
93 await saveMigrationCheckpoint('cache-y', 7);
94 await clearMigrationCheckpoint('cache-y');
95 expect(await getMigrationCheckpoint('cache-y')).toBe(0);
96 });
97
98 it('closeMetaDb + _resetDbForTest allow a subsequent openMetaDb to reconnect', async () => {
99 const first = await openMetaDb();
100 closeMetaDb();
101 _resetDbForTest();
102 const second = await openMetaDb();
103 expect(second).not.toBe(first);
104 });
105});
106