📄 src/models/test/session.test.ts
D-OPEN SOVEREIGN
1/*
2 * Copyright (c) 2026 DataPond D-Library Pty Ltd. ("The Sanctuary")
3 * Non-Profit Public Library — The World Library
4 * 
5 * D-SAFE Certification issued by POND Enterprise.
6 * Published under the D-Open Code Sovereign Licence v1.0 framework
7 * DataPond D-Library All exclusive Right reserved on modifiying the code - CC Attribution to data pond, Non modifiable, Non Commercial.
8 * https://registry.world.bibliotech.com/licence
9 * Source code donated to DataPond D-Library by it's author: data pond.
10 * 
11 * Technical Guardian ("The Shield"): Pond Enterprise Pty Ltd. (ACN 694 747 987)
12 * All liability claims about D-SAFE certification issues coming from the published D-Safe direction must be addressed with Pond Enterprise Pty Ltd.
13 * Code Modification automatically voids the certified liability protection provided by Pond Enterprise.
14 */
15import 'fake-indexeddb/auto';
16import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
17import { bootModelsV2, loadLanguage, hasLang, bootstrapSession, resetModelsV2Session, localSidecarUrls, registrySidecarUrls } from '../session.ts';
18import { Book, NewBookWithId } from '../generated-src/Book.ts';
19import * as workerPatchMain from '../worker-patch-main.ts';
20import { clear, configureDb } from '../db.ts';
21
22// Mock the worker methods to avoid spinning up actual workers
23vi.mock('../worker-patch-main.ts', () => {
24    return {
25        InitPatchWorker: async () => ({
26            sidecars: [],
27            patches: { mine: [] }
28        }),
29        LoadLanguageWorkerCmd: async () => ({
30            sidecars: []
31        })
32    };
33});
34
35describe('session.ts', () => {
36    beforeEach(() => {
37        resetModelsV2Session();
38        clear();
39        configureDb({ cacheLimit: 2 });
40        // @ts-ignore
41        globalThis.fetch = vi.fn().mockResolvedValue({
42            ok: true,
43            json: async () => ({
44                objects: {
45                    [Book.type]: {
46                        tiers: {
47                            slim: [{ file: 'eng_slim.bin' }]
48                        }
49                    }
50                }
51            })
52        });
53    });
54
55    afterEach(() => {
56        vi.restoreAllMocks();
57    });
58
59    it('localSidecarUrls generates correct URLs', () => {
60        const manifest = {
61            objects: {
62                '0': {
63                    tiers: {
64                        slim: [{ file: 'eng_slim.bin' }],
65                        extended: [{ file: 'eng_extended.bin' }],
66                        prov: [{ file: 'eng_prov.bin' }]
67                    }
68                }
69            }
70        };
71        const urls = localSidecarUrls('/base', manifest, ['eng']);
72        expect(urls.length).toBe(1);
73        expect(urls[0].slimUrl).toBe('/base/eng_slim.bin');
74        expect(urls[0].extendedUrl).toBe('/base/eng_extended.bin');
75        expect(urls[0].provUrl).toBe('/base/eng_prov.bin');
76    });
77
78    it('registrySidecarUrls generates correct URLs', () => {
79        const dataBlock = {
80            objects: {
81                '0': {
82                    'eng': {
83                        slim: ['tx-slim'],
84                        extended: ['tx-ext'],
85                        prov: ['tx-prov']
86                    }
87                }
88            }
89        };
90        const urls = registrySidecarUrls(dataBlock, ['eng'], 'https://arweave.net');
91        expect(urls.length).toBe(1);
92        expect(urls[0].slimUrl).toBe('https://arweave.net/tx-slim');
93        expect(urls[0].extendedUrl).toBe('https://arweave.net/tx-ext');
94        expect(urls[0].provUrl).toBe('https://arweave.net/tx-prov');
95    });
96
97    it('bootModelsV2 boots correctly in localMode', async () => {
98        const data = await bootModelsV2({
99            userLang: 'eng',
100            localMode: '/corpus_v2',
101            models: [{ type: Book.type, create: NewBookWithId }]
102        });
103        
104        expect(data.patches.mine).toBeDefined();
105        expect(hasLang('eng')).toBe(true);
106    });
107
108    it('bootModelsV2 throws if no localMode or registry provided', async () => {
109        await expect(bootModelsV2({
110            userLang: 'eng',
111            models: []
112        } as any)).rejects.toThrow(/provide `localMode`/);
113    });
114
115    it('loadLanguage dynamically loads a new language', async () => {
116        // First boot
117        await bootModelsV2({
118            userLang: 'eng',
119            localMode: '/corpus_v2',
120            models: [{ type: Book.type, create: NewBookWithId }]
121        });
122
123        // Setup mock for fra
124        // @ts-ignore
125        globalThis.fetch.mockResolvedValueOnce({
126            ok: true,
127            json: async () => ({
128                objects: {
129                    [Book.type]: {
130                        tiers: {
131                            slim: [{ file: 'fra_slim.bin' }]
132                        }
133                    }
134                }
135            })
136        });
137
138        expect(hasLang('fra')).toBe(false);
139        await loadLanguage('fra');
140        expect(hasLang('fra')).toBe(true);
141    });
142
143    it('loadLanguage throws if called before boot', async () => {
144        await expect(loadLanguage('eng')).rejects.toThrow(/before bootModelsV2/);
145    });
146
147    it('bootstrapSession installs reactivity and boots', async () => {
148        const reactivitySpy = vi.fn();
149        const initSpy = vi.fn().mockResolvedValue(undefined);
150
151        await bootstrapSession({
152            userLang: 'eng',
153            models: [],
154            init: initSpy
155        }, reactivitySpy);
156
157        expect(reactivitySpy).toHaveBeenCalled();
158        expect(initSpy).toHaveBeenCalled();
159    });
160});
161