File size: 3,366 Bytes
0ed8124
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import { chromium } from '@playwright/test';
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { createServer as createViteServer } from 'vite';

const root = resolve(dirname(fileURLToPath(import.meta.url)), '..');
const memoryKey = '__bonsai_persistence_probe__';
const conversationId = '__bonsai_conversation_probe__';
let browser;
let page;
let vite;

try {
  vite = await createViteServer({
    root,
    logLevel: 'error',
    server: { host: '127.0.0.1', port: 0 },
    plugins: [{
      name: 'persistence-probe-page',
      configureServer(server) {
        server.middlewares.use('/persistence-probe.html', (_request, response) => {
          response.setHeader('Content-Type', 'text/html; charset=utf-8');
          response.end('<!doctype html><title>Persistence probe</title>');
        });
      },
    }],
  });
  await vite.listen();
  const address = vite.httpServer?.address();
  if (!address || typeof address === 'string') throw new Error('Vite persistence probe did not expose a TCP port');

  browser = await chromium.launch({ headless: true });
  page = await browser.newPage();
  await page.goto(`http://127.0.0.1:${address.port}/persistence-probe.html`);
  await page.evaluate(async ({ memoryKey, conversationId }) => {
    const idb = await import('/src/lib/idb.ts');
    await idb.deleteMemory(memoryKey);
    await idb.putMemory(memoryKey, 'survives reload');
    await idb.saveConversation({
      id: conversationId,
      title: 'Persistence probe',
      modelId: 'bonsai-1.7b',
      updatedAt: 0,
      messages: [{ role: 'user', content: 'keep locally' }],
    });
  }, { memoryKey, conversationId });

  await page.reload({ waitUntil: 'domcontentloaded' });
  const persisted = await page.evaluate(async ({ memoryKey, conversationId }) => {
    const idb = await import('/src/lib/idb.ts');
    const memory = await idb.getMemory(memoryKey);
    const conversation = await idb.loadConversation(conversationId);
    return { memory, conversation };
  }, { memoryKey, conversationId });

  if (persisted.memory?.value !== 'survives reload') {
    throw new Error(`Memory did not survive reload: ${JSON.stringify(persisted.memory)}`);
  }
  if (persisted.conversation?.messages?.[0]?.content !== 'keep locally') {
    throw new Error(`Conversation did not survive reload: ${JSON.stringify(persisted.conversation)}`);
  }
  console.log('IndexedDB persistence passed: memory and conversation survived a page reload');
} finally {
  if (page) {
    await page.evaluate(async ({ memoryKey, conversationId }) => {
      const idb = await import('/src/lib/idb.ts');
      await idb.deleteMemory(memoryKey).catch(() => undefined);
      const request = indexedDB.open('bonsai-webgpu-chat', 1);
      await new Promise((resolve) => {
        request.onsuccess = () => {
          const database = request.result;
          const transaction = database.transaction('conversations', 'readwrite');
          transaction.objectStore('conversations').delete(conversationId);
          transaction.oncomplete = () => { database.close(); resolve(); };
          transaction.onerror = () => { database.close(); resolve(); };
        };
        request.onerror = () => resolve();
      });
    }, { memoryKey, conversationId }).catch(() => undefined);
  }
  await browser?.close();
  await vite?.close();
}