@objectql/driver-excel 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +70 -0
- package/EXAMPLE.md +198 -0
- package/LICENSE +21 -0
- package/README.md +653 -0
- package/dist/index.d.ts +215 -0
- package/dist/index.js +856 -0
- package/dist/index.js.map +1 -0
- package/jest.config.js +9 -0
- package/package.json +37 -0
- package/src/index.ts +960 -0
- package/test/index.test.ts +566 -0
- package/tsconfig.json +12 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,566 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Excel Driver Tests
|
|
3
|
+
*
|
|
4
|
+
* Comprehensive test suite for the Excel ObjectQL driver.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { ExcelDriver } from '../src';
|
|
8
|
+
import * as fs from 'fs';
|
|
9
|
+
import * as path from 'path';
|
|
10
|
+
|
|
11
|
+
describe('ExcelDriver', () => {
|
|
12
|
+
const TEST_DIR = path.join(__dirname, 'test-files');
|
|
13
|
+
const TEST_FILE = path.join(TEST_DIR, 'test-data.xlsx');
|
|
14
|
+
const TEST_OBJECT = 'test_users';
|
|
15
|
+
let driver: ExcelDriver;
|
|
16
|
+
|
|
17
|
+
beforeAll(() => {
|
|
18
|
+
// Create test directory
|
|
19
|
+
if (!fs.existsSync(TEST_DIR)) {
|
|
20
|
+
fs.mkdirSync(TEST_DIR, { recursive: true });
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
beforeEach(async () => {
|
|
25
|
+
// Clean up test file
|
|
26
|
+
if (fs.existsSync(TEST_FILE)) {
|
|
27
|
+
fs.unlinkSync(TEST_FILE);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Create new driver using factory method
|
|
31
|
+
driver = await ExcelDriver.create({
|
|
32
|
+
filePath: TEST_FILE,
|
|
33
|
+
createIfMissing: true,
|
|
34
|
+
autoSave: true
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
afterEach(async () => {
|
|
39
|
+
await driver.disconnect();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
afterAll(() => {
|
|
43
|
+
// Clean up test directory
|
|
44
|
+
if (fs.existsSync(TEST_DIR)) {
|
|
45
|
+
fs.rmSync(TEST_DIR, { recursive: true, force: true });
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
describe('Initialization', () => {
|
|
50
|
+
it('should create a new Excel file if it does not exist', () => {
|
|
51
|
+
expect(fs.existsSync(TEST_FILE)).toBe(true);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('should load an existing Excel file', async () => {
|
|
55
|
+
// Create some data
|
|
56
|
+
await driver.create(TEST_OBJECT, {
|
|
57
|
+
name: 'Alice',
|
|
58
|
+
email: 'alice@example.com'
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Create new driver instance with same file
|
|
62
|
+
const driver2 = await ExcelDriver.create({
|
|
63
|
+
filePath: TEST_FILE,
|
|
64
|
+
createIfMissing: false
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const results = await driver2.find(TEST_OBJECT);
|
|
68
|
+
expect(results.length).toBe(1);
|
|
69
|
+
expect(results[0].name).toBe('Alice');
|
|
70
|
+
|
|
71
|
+
await driver2.disconnect();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('should throw error if file does not exist and createIfMissing is false', async () => {
|
|
75
|
+
const nonExistentFile = path.join(TEST_DIR, 'non-existent.xlsx');
|
|
76
|
+
|
|
77
|
+
await expect(
|
|
78
|
+
ExcelDriver.create({
|
|
79
|
+
filePath: nonExistentFile,
|
|
80
|
+
createIfMissing: false
|
|
81
|
+
})
|
|
82
|
+
).rejects.toThrow('Excel file not found');
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('should support strict mode', async () => {
|
|
86
|
+
const strictDriver = await ExcelDriver.create({
|
|
87
|
+
filePath: path.join(TEST_DIR, 'strict-test.xlsx'),
|
|
88
|
+
strictMode: true,
|
|
89
|
+
createIfMissing: true
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
await expect(
|
|
93
|
+
strictDriver.update(TEST_OBJECT, 'non-existent', { name: 'Test' })
|
|
94
|
+
).rejects.toThrow('Record with id \'non-existent\' not found');
|
|
95
|
+
|
|
96
|
+
await strictDriver.disconnect();
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
describe('CRUD Operations', () => {
|
|
101
|
+
it('should create a record', async () => {
|
|
102
|
+
const result = await driver.create(TEST_OBJECT, {
|
|
103
|
+
name: 'Alice',
|
|
104
|
+
email: 'alice@example.com',
|
|
105
|
+
role: 'admin'
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
expect(result).toHaveProperty('id');
|
|
109
|
+
expect(result.name).toBe('Alice');
|
|
110
|
+
expect(result.email).toBe('alice@example.com');
|
|
111
|
+
expect(result).toHaveProperty('created_at');
|
|
112
|
+
expect(result).toHaveProperty('updated_at');
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('should create a record with custom ID', async () => {
|
|
116
|
+
const result = await driver.create(TEST_OBJECT, {
|
|
117
|
+
id: 'custom-123',
|
|
118
|
+
name: 'Bob',
|
|
119
|
+
email: 'bob@example.com'
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
expect(result.id).toBe('custom-123');
|
|
123
|
+
expect(result.name).toBe('Bob');
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('should throw error on duplicate ID', async () => {
|
|
127
|
+
await driver.create(TEST_OBJECT, {
|
|
128
|
+
id: 'test-1',
|
|
129
|
+
name: 'Alice'
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
await expect(
|
|
133
|
+
driver.create(TEST_OBJECT, {
|
|
134
|
+
id: 'test-1',
|
|
135
|
+
name: 'Bob'
|
|
136
|
+
})
|
|
137
|
+
).rejects.toThrow('Record with id \'test-1\' already exists');
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('should find a record by ID', async () => {
|
|
141
|
+
const created = await driver.create(TEST_OBJECT, {
|
|
142
|
+
name: 'Alice',
|
|
143
|
+
email: 'alice@example.com'
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
const found = await driver.findOne(TEST_OBJECT, created.id);
|
|
147
|
+
expect(found).toBeDefined();
|
|
148
|
+
expect(found.name).toBe('Alice');
|
|
149
|
+
expect(found.email).toBe('alice@example.com');
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('should return null for non-existent ID', async () => {
|
|
153
|
+
const result = await driver.findOne(TEST_OBJECT, 'non-existent-id');
|
|
154
|
+
expect(result).toBeNull();
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('should update a record', async () => {
|
|
158
|
+
const created = await driver.create(TEST_OBJECT, {
|
|
159
|
+
name: 'Alice',
|
|
160
|
+
email: 'alice@example.com'
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// Small delay to ensure updated_at timestamp differs from created_at
|
|
164
|
+
// Note: Using real setTimeout here is intentional to test actual timestamp behavior
|
|
165
|
+
await new Promise(resolve => setTimeout(resolve, 10));
|
|
166
|
+
|
|
167
|
+
const updated = await driver.update(TEST_OBJECT, created.id, {
|
|
168
|
+
email: 'alice.new@example.com'
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
expect(updated.email).toBe('alice.new@example.com');
|
|
172
|
+
expect(updated.name).toBe('Alice'); // Unchanged
|
|
173
|
+
expect(updated.created_at).toBe(created.created_at); // Preserved
|
|
174
|
+
expect(updated.updated_at).not.toBe(created.updated_at); // Changed
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('should delete a record', async () => {
|
|
178
|
+
const created = await driver.create(TEST_OBJECT, {
|
|
179
|
+
name: 'Alice'
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
const deleted = await driver.delete(TEST_OBJECT, created.id);
|
|
183
|
+
expect(deleted).toBe(true);
|
|
184
|
+
|
|
185
|
+
const found = await driver.findOne(TEST_OBJECT, created.id);
|
|
186
|
+
expect(found).toBeNull();
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('should return false when deleting non-existent record', async () => {
|
|
190
|
+
const deleted = await driver.delete(TEST_OBJECT, 'non-existent');
|
|
191
|
+
expect(deleted).toBe(false);
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
describe('Query Operations', () => {
|
|
196
|
+
beforeEach(async () => {
|
|
197
|
+
// Create test data
|
|
198
|
+
await driver.create(TEST_OBJECT, {
|
|
199
|
+
id: '1',
|
|
200
|
+
name: 'Alice',
|
|
201
|
+
email: 'alice@example.com',
|
|
202
|
+
role: 'admin',
|
|
203
|
+
age: 30
|
|
204
|
+
});
|
|
205
|
+
await driver.create(TEST_OBJECT, {
|
|
206
|
+
id: '2',
|
|
207
|
+
name: 'Bob',
|
|
208
|
+
email: 'bob@example.com',
|
|
209
|
+
role: 'user',
|
|
210
|
+
age: 25
|
|
211
|
+
});
|
|
212
|
+
await driver.create(TEST_OBJECT, {
|
|
213
|
+
id: '3',
|
|
214
|
+
name: 'Charlie',
|
|
215
|
+
email: 'charlie@example.com',
|
|
216
|
+
role: 'user',
|
|
217
|
+
age: 35
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it('should find all records', async () => {
|
|
222
|
+
const results = await driver.find(TEST_OBJECT);
|
|
223
|
+
expect(results.length).toBe(3);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it('should filter records with equality operator', async () => {
|
|
227
|
+
const results = await driver.find(TEST_OBJECT, {
|
|
228
|
+
filters: [['role', '=', 'user']]
|
|
229
|
+
});
|
|
230
|
+
expect(results.length).toBe(2);
|
|
231
|
+
expect(results.every(r => r.role === 'user')).toBe(true);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it('should filter records with greater than operator', async () => {
|
|
235
|
+
const results = await driver.find(TEST_OBJECT, {
|
|
236
|
+
filters: [['age', '>', 25]]
|
|
237
|
+
});
|
|
238
|
+
expect(results.length).toBe(2);
|
|
239
|
+
expect(results.every(r => r.age > 25)).toBe(true);
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
it('should filter records with contains operator', async () => {
|
|
243
|
+
const results = await driver.find(TEST_OBJECT, {
|
|
244
|
+
filters: [['name', 'contains', 'li']]
|
|
245
|
+
});
|
|
246
|
+
expect(results.length).toBe(2); // Alice and Charlie
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it('should support OR filters', async () => {
|
|
250
|
+
const results = await driver.find(TEST_OBJECT, {
|
|
251
|
+
filters: [
|
|
252
|
+
['name', '=', 'Alice'],
|
|
253
|
+
'or',
|
|
254
|
+
['name', '=', 'Bob']
|
|
255
|
+
]
|
|
256
|
+
});
|
|
257
|
+
expect(results.length).toBe(2);
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
it('should sort records ascending', async () => {
|
|
261
|
+
const results = await driver.find(TEST_OBJECT, {
|
|
262
|
+
sort: [['age', 'asc']]
|
|
263
|
+
});
|
|
264
|
+
expect(results[0].age).toBe(25);
|
|
265
|
+
expect(results[1].age).toBe(30);
|
|
266
|
+
expect(results[2].age).toBe(35);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it('should sort records descending', async () => {
|
|
270
|
+
const results = await driver.find(TEST_OBJECT, {
|
|
271
|
+
sort: [['age', 'desc']]
|
|
272
|
+
});
|
|
273
|
+
expect(results[0].age).toBe(35);
|
|
274
|
+
expect(results[1].age).toBe(30);
|
|
275
|
+
expect(results[2].age).toBe(25);
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it('should support pagination with limit', async () => {
|
|
279
|
+
const results = await driver.find(TEST_OBJECT, {
|
|
280
|
+
limit: 2
|
|
281
|
+
});
|
|
282
|
+
expect(results.length).toBe(2);
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it('should support pagination with skip', async () => {
|
|
286
|
+
const results = await driver.find(TEST_OBJECT, {
|
|
287
|
+
skip: 1,
|
|
288
|
+
limit: 2
|
|
289
|
+
});
|
|
290
|
+
expect(results.length).toBe(2);
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it('should project specific fields', async () => {
|
|
294
|
+
const results = await driver.find(TEST_OBJECT, {
|
|
295
|
+
fields: ['name', 'email']
|
|
296
|
+
});
|
|
297
|
+
expect(results[0]).toHaveProperty('name');
|
|
298
|
+
expect(results[0]).toHaveProperty('email');
|
|
299
|
+
expect(results[0]).not.toHaveProperty('role');
|
|
300
|
+
expect(results[0]).not.toHaveProperty('age');
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
it('should count all records', async () => {
|
|
304
|
+
const count = await driver.count(TEST_OBJECT, {});
|
|
305
|
+
expect(count).toBe(3);
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
it('should count filtered records', async () => {
|
|
309
|
+
const count = await driver.count(TEST_OBJECT, {
|
|
310
|
+
filters: [['role', '=', 'user']]
|
|
311
|
+
});
|
|
312
|
+
expect(count).toBe(2);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
it('should get distinct values', async () => {
|
|
316
|
+
const roles = await driver.distinct(TEST_OBJECT, 'role');
|
|
317
|
+
expect(roles).toHaveLength(2);
|
|
318
|
+
expect(roles).toContain('admin');
|
|
319
|
+
expect(roles).toContain('user');
|
|
320
|
+
});
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
describe('Bulk Operations', () => {
|
|
324
|
+
it('should create multiple records', async () => {
|
|
325
|
+
const results = await driver.createMany(TEST_OBJECT, [
|
|
326
|
+
{ name: 'Alice', email: 'alice@example.com' },
|
|
327
|
+
{ name: 'Bob', email: 'bob@example.com' }
|
|
328
|
+
]);
|
|
329
|
+
|
|
330
|
+
expect(results).toHaveLength(2);
|
|
331
|
+
expect(results[0].name).toBe('Alice');
|
|
332
|
+
expect(results[1].name).toBe('Bob');
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
it('should update multiple records', async () => {
|
|
336
|
+
await driver.create(TEST_OBJECT, { id: '1', name: 'Alice', role: 'user' });
|
|
337
|
+
await driver.create(TEST_OBJECT, { id: '2', name: 'Bob', role: 'user' });
|
|
338
|
+
await driver.create(TEST_OBJECT, { id: '3', name: 'Charlie', role: 'admin' });
|
|
339
|
+
|
|
340
|
+
const result = await driver.updateMany(
|
|
341
|
+
TEST_OBJECT,
|
|
342
|
+
[['role', '=', 'user']],
|
|
343
|
+
{ role: 'member' }
|
|
344
|
+
);
|
|
345
|
+
|
|
346
|
+
expect(result.modifiedCount).toBe(2);
|
|
347
|
+
|
|
348
|
+
const users = await driver.find(TEST_OBJECT, {
|
|
349
|
+
filters: [['role', '=', 'member']]
|
|
350
|
+
});
|
|
351
|
+
expect(users).toHaveLength(2);
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
it('should delete multiple records', async () => {
|
|
355
|
+
await driver.create(TEST_OBJECT, { id: '1', name: 'Alice', role: 'user' });
|
|
356
|
+
await driver.create(TEST_OBJECT, { id: '2', name: 'Bob', role: 'user' });
|
|
357
|
+
await driver.create(TEST_OBJECT, { id: '3', name: 'Charlie', role: 'admin' });
|
|
358
|
+
|
|
359
|
+
const result = await driver.deleteMany(
|
|
360
|
+
TEST_OBJECT,
|
|
361
|
+
[['role', '=', 'user']]
|
|
362
|
+
);
|
|
363
|
+
|
|
364
|
+
expect(result.deletedCount).toBe(2);
|
|
365
|
+
|
|
366
|
+
const remaining = await driver.find(TEST_OBJECT);
|
|
367
|
+
expect(remaining).toHaveLength(1);
|
|
368
|
+
expect(remaining[0].role).toBe('admin');
|
|
369
|
+
});
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
describe('File Persistence', () => {
|
|
373
|
+
it('should persist data to file', async () => {
|
|
374
|
+
await driver.create(TEST_OBJECT, {
|
|
375
|
+
name: 'Alice',
|
|
376
|
+
email: 'alice@example.com'
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
// Disconnect to ensure flush
|
|
380
|
+
await driver.disconnect();
|
|
381
|
+
|
|
382
|
+
// Verify file exists and has content
|
|
383
|
+
expect(fs.existsSync(TEST_FILE)).toBe(true);
|
|
384
|
+
const stats = fs.statSync(TEST_FILE);
|
|
385
|
+
expect(stats.size).toBeGreaterThan(0);
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
it('should load persisted data from file', async () => {
|
|
389
|
+
// Create and save data
|
|
390
|
+
await driver.create(TEST_OBJECT, {
|
|
391
|
+
id: 'persist-1',
|
|
392
|
+
name: 'Alice',
|
|
393
|
+
email: 'alice@example.com'
|
|
394
|
+
});
|
|
395
|
+
await driver.save();
|
|
396
|
+
|
|
397
|
+
// Create new driver instance
|
|
398
|
+
const driver2 = await ExcelDriver.create({
|
|
399
|
+
filePath: TEST_FILE,
|
|
400
|
+
createIfMissing: false
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
const found = await driver2.findOne(TEST_OBJECT, 'persist-1');
|
|
404
|
+
expect(found).toBeDefined();
|
|
405
|
+
expect(found.name).toBe('Alice');
|
|
406
|
+
expect(found.email).toBe('alice@example.com');
|
|
407
|
+
|
|
408
|
+
await driver2.disconnect();
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
it('should support multiple worksheets (object types)', async () => {
|
|
412
|
+
await driver.create('users', { name: 'Alice' });
|
|
413
|
+
await driver.create('products', { name: 'Product A' });
|
|
414
|
+
|
|
415
|
+
const users = await driver.find('users');
|
|
416
|
+
const products = await driver.find('products');
|
|
417
|
+
|
|
418
|
+
expect(users).toHaveLength(1);
|
|
419
|
+
expect(products).toHaveLength(1);
|
|
420
|
+
expect(users[0].name).toBe('Alice');
|
|
421
|
+
expect(products[0].name).toBe('Product A');
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
it('should handle autoSave disabled', async () => {
|
|
425
|
+
const manualDriver = await ExcelDriver.create({
|
|
426
|
+
filePath: path.join(TEST_DIR, 'manual-save.xlsx'),
|
|
427
|
+
autoSave: false,
|
|
428
|
+
createIfMissing: true
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
await manualDriver.create(TEST_OBJECT, {
|
|
432
|
+
name: 'Alice'
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
// Manually save
|
|
436
|
+
await manualDriver.save();
|
|
437
|
+
|
|
438
|
+
// Verify persistence
|
|
439
|
+
const driver2 = await ExcelDriver.create({
|
|
440
|
+
filePath: path.join(TEST_DIR, 'manual-save.xlsx'),
|
|
441
|
+
createIfMissing: false
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
const results = await driver2.find(TEST_OBJECT);
|
|
445
|
+
expect(results).toHaveLength(1);
|
|
446
|
+
|
|
447
|
+
await manualDriver.disconnect();
|
|
448
|
+
await driver2.disconnect();
|
|
449
|
+
});
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
describe('Edge Cases', () => {
|
|
453
|
+
it('should handle empty result sets', async () => {
|
|
454
|
+
const results = await driver.find('non_existent_object');
|
|
455
|
+
expect(results).toEqual([]);
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
it('should handle filters on empty data', async () => {
|
|
459
|
+
const results = await driver.find(TEST_OBJECT, {
|
|
460
|
+
filters: [['name', '=', 'Alice']]
|
|
461
|
+
});
|
|
462
|
+
expect(results).toEqual([]);
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
it('should handle null and undefined values', async () => {
|
|
466
|
+
const result = await driver.create(TEST_OBJECT, {
|
|
467
|
+
name: 'Alice',
|
|
468
|
+
optional_field: null
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
expect(result.optional_field).toBeNull();
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
it('should handle special characters in data', async () => {
|
|
475
|
+
const result = await driver.create(TEST_OBJECT, {
|
|
476
|
+
name: 'O\'Brien',
|
|
477
|
+
description: 'Test "quotes" & special <chars>'
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
const found = await driver.findOne(TEST_OBJECT, result.id);
|
|
481
|
+
expect(found.name).toBe('O\'Brien');
|
|
482
|
+
expect(found.description).toBe('Test "quotes" & special <chars>');
|
|
483
|
+
});
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
describe('File Per Object Mode', () => {
|
|
487
|
+
const FILE_PER_OBJECT_DIR = path.join(TEST_DIR, 'file-per-object');
|
|
488
|
+
let filePerObjectDriver: ExcelDriver;
|
|
489
|
+
|
|
490
|
+
beforeEach(async () => {
|
|
491
|
+
// Clean up directory
|
|
492
|
+
if (fs.existsSync(FILE_PER_OBJECT_DIR)) {
|
|
493
|
+
fs.rmSync(FILE_PER_OBJECT_DIR, { recursive: true, force: true });
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// Create driver in file-per-object mode
|
|
497
|
+
filePerObjectDriver = await ExcelDriver.create({
|
|
498
|
+
filePath: FILE_PER_OBJECT_DIR,
|
|
499
|
+
fileStorageMode: 'file-per-object',
|
|
500
|
+
createIfMissing: true,
|
|
501
|
+
autoSave: true
|
|
502
|
+
});
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
afterEach(async () => {
|
|
506
|
+
if (filePerObjectDriver) {
|
|
507
|
+
await filePerObjectDriver.disconnect();
|
|
508
|
+
}
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
it('should create separate files for each object type', async () => {
|
|
512
|
+
await filePerObjectDriver.create('users', { name: 'Alice' });
|
|
513
|
+
await filePerObjectDriver.create('products', { name: 'Product A' });
|
|
514
|
+
|
|
515
|
+
// Check that separate files exist
|
|
516
|
+
expect(fs.existsSync(path.join(FILE_PER_OBJECT_DIR, 'users.xlsx'))).toBe(true);
|
|
517
|
+
expect(fs.existsSync(path.join(FILE_PER_OBJECT_DIR, 'products.xlsx'))).toBe(true);
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
it('should load data from separate files', async () => {
|
|
521
|
+
await filePerObjectDriver.create('users', { id: 'user-1', name: 'Alice' });
|
|
522
|
+
await filePerObjectDriver.create('products', { id: 'prod-1', name: 'Product A' });
|
|
523
|
+
await filePerObjectDriver.save();
|
|
524
|
+
|
|
525
|
+
// Create new driver instance and load from files
|
|
526
|
+
const driver2 = await ExcelDriver.create({
|
|
527
|
+
filePath: FILE_PER_OBJECT_DIR,
|
|
528
|
+
fileStorageMode: 'file-per-object',
|
|
529
|
+
createIfMissing: false
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
const users = await driver2.find('users');
|
|
533
|
+
const products = await driver2.find('products');
|
|
534
|
+
|
|
535
|
+
expect(users).toHaveLength(1);
|
|
536
|
+
expect(products).toHaveLength(1);
|
|
537
|
+
expect(users[0].name).toBe('Alice');
|
|
538
|
+
expect(products[0].name).toBe('Product A');
|
|
539
|
+
|
|
540
|
+
await driver2.disconnect();
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
it('should support all CRUD operations in file-per-object mode', async () => {
|
|
544
|
+
// Create
|
|
545
|
+
const user = await filePerObjectDriver.create('users', {
|
|
546
|
+
name: 'Bob',
|
|
547
|
+
email: 'bob@example.com'
|
|
548
|
+
});
|
|
549
|
+
expect(user.name).toBe('Bob');
|
|
550
|
+
|
|
551
|
+
// Read
|
|
552
|
+
const found = await filePerObjectDriver.findOne('users', user.id);
|
|
553
|
+
expect(found.name).toBe('Bob');
|
|
554
|
+
|
|
555
|
+
// Update
|
|
556
|
+
await filePerObjectDriver.update('users', user.id, { email: 'bob.new@example.com' });
|
|
557
|
+
const updated = await filePerObjectDriver.findOne('users', user.id);
|
|
558
|
+
expect(updated.email).toBe('bob.new@example.com');
|
|
559
|
+
|
|
560
|
+
// Delete
|
|
561
|
+
await filePerObjectDriver.delete('users', user.id);
|
|
562
|
+
const deleted = await filePerObjectDriver.findOne('users', user.id);
|
|
563
|
+
expect(deleted).toBeNull();
|
|
564
|
+
});
|
|
565
|
+
});
|
|
566
|
+
});
|
package/tsconfig.json
ADDED