📄 src/models/test/Book.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 { describe, it, expect, beforeEach } from 'vitest';
16import { Book, NewBookWithId } from '../generated-src/Book.ts';
17import { parseSidecar, colsFromSidecar } from '../enriched-index.ts';
18import { clear, configureDb } from '../db.ts';
19import { setDefaultLanguage } from '../orm.ts';
20import { QualityScore, BookMethod } from '@the_library/db_schemas/enums';
21import { PatchInstance } from '../patcher.ts';
22import { PatchOperation, PatchAction } from '../binary.ts';
23import { createBookFixture, createInstancesFixture } from './fixtures.ts';
24
25beforeEach(() => {
26    clear();
27    configureDb({ cacheLimit: 10_000 });
28    setDefaultLanguage('eng');
29    PatchInstance.clearQueue();
30});
31
32describe('Book Model - Multi-Instance and Multi-Language', () => {
33    it('should serialize and deserialize multiple instances across languages', () => {
34        const fixtureEng = createBookFixture(100);
35        const fixtureFra = createBookFixture(100);
36        fixtureFra.title = 'Titre du Livre';
37        
38        // We only put the French instance in the French sidecar for testing separation
39        const instances = createInstancesFixture();
40        fixtureFra.instances = instances.filter(i => i.language === 'fra');
41        fixtureEng.instances = instances.filter(i => i.language === 'eng');
42
43        const bufferEngSlim = Book.serializeSlim([fixtureEng]);
44        const bufferFraSlim = Book.serializeSlim([fixtureFra]);
45        const bufferEngExt = Book.serializeExtended([fixtureEng]);
46        const bufferFraExt = Book.serializeExtended([fixtureFra]);
47
48        const parsedEngSlim = parseSidecar(bufferEngSlim);
49        const parsedFraSlim = parseSidecar(bufferFraSlim);
50        const parsedEngExt = parseSidecar(bufferEngExt);
51        const parsedFraExt = parseSidecar(bufferFraExt);
52
53        const book = NewBookWithId(101);
54        book.mergeSlim(colsFromSidecar(parsedEngSlim) as any, 0, 'eng');
55        book.mergeSlim(colsFromSidecar(parsedFraSlim) as any, 0, 'fra');
56        book.mergeExtended(colsFromSidecar(parsedEngExt) as any, 0, 'eng');
57        book.mergeExtended(colsFromSidecar(parsedFraExt) as any, 0, 'fra');
58
59        // Check basic properties
60        expect(book.getTitle('eng')).toBe('Book Title 100');
61        expect(book.getTitle('fra')).toBe('Titre du Livre');
62
63        // Check instances isolation by language
64        const engInstances = book.getInstances('eng');
65        expect(engInstances.length).toBe(2);
66        expect(engInstances[0].language).toBe('eng');
67        expect(engInstances[1].language).toBe('eng');
68        
69        const defaultEng = book.getDefaultInstance('eng');
70        expect(defaultEng?.downloadTxId).toBe('tx-default-eng');
71
72        const fraInstances = book.getInstances('fra');
73        expect(fraInstances.length).toBe(1);
74        expect(fraInstances[0].language).toBe('fra');
75        expect(fraInstances[0].aiEngine).toBe('GPT-4');
76
77        const defaultFra = book.getDefaultInstance('fra');
78        expect(defaultFra?.downloadTxId).toBe('tx-default-fra');
79    });
80});
81
82describe('Book Model - Patching Scenarios', () => {
83    it('should generate patches for instance field updates', () => {
84        const fixture = createBookFixture(200);
85        const buffer = Book.serializeExtended([fixture]);
86        const cols = colsFromSidecar(parseSidecar(buffer));
87
88        const book = NewBookWithId(200);
89        book.mergeExtended(cols as any, 0, 'eng');
90        PatchInstance.clearQueue(); // Clear any patches generated during merge, though ideally merge shouldn't generate patches.
91
92        const inst = book.getDefaultInstance('eng')!;
93        
94        // Update string field
95        inst.publisher = 'Patched Press';
96        
97        // Update number field
98        inst.nbPages = 500;
99        
100        // Update boolean field (stored as number in binary)
101        inst.isAiTranslation = true;
102
103        const patches = PatchInstance.patches;
104        expect(patches.length).toBe(3);
105
106        expect(patches[0]).toMatchObject({
107            operation: PatchOperation.EditInstanceFieldString,
108            objectId: 200,
109            propertyId: 38, // publisher
110            value: 'Patched Press'
111        });
112
113        expect(patches[1]).toMatchObject({
114            operation: PatchOperation.EditInstanceFieldNumber,
115            objectId: 200,
116            propertyId: 36, // nbPages
117            value: 500
118        });
119
120        expect(patches[2]).toMatchObject({
121            operation: PatchOperation.EditInstanceFieldNumber,
122            objectId: 200,
123            propertyId: 33, // isAiTranslation
124            value: 1
125        });
126        
127        // Ensure in-memory state is updated
128        expect(inst.publisher).toBe('Patched Press');
129        expect(inst.nbPages).toBe(500);
130        expect(inst.isAiTranslation).toBe(true);
131        expect(book.getInstancesData('eng')[inst.index]['publisher']).toBe('Patched Press');
132    });
133
134    it('should handle patching of base properties', () => {
135        const book = NewBookWithId(201);
136        PatchInstance.clearQueue();
137
138        book.title = 'New Patched Title';
139        book.textQuality = QualityScore.Excellent;
140        book.safe = false;
141
142        const patches = PatchInstance.patches;
143        expect(patches.length).toBe(3);
144
145        expect(patches[0]).toMatchObject({
146            operation: PatchOperation.EditPropertyString,
147            objectId: 201,
148            propertyId: 0, // title
149            value: 'New Patched Title'
150        });
151
152        expect(patches[1]).toMatchObject({
153            operation: PatchOperation.EditPropertyNumber,
154            objectId: 201,
155            propertyId: 12, // textQuality
156            value: QualityScore.Excellent
157        });
158
159        expect(patches[2]).toMatchObject({
160            operation: PatchOperation.EditPropertyNumber,
161            objectId: 201,
162            propertyId: 5, // safe
163            value: 0
164        });
165    });
166});
167