@luzzle/core 0.0.62 → 0.0.63
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/README.md +38 -8
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.js +2 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/pieces/Piece.js +2 -2
- package/dist/src/pieces/Piece.js.map +1 -1
- package/dist/src/pieces/index.d.ts +1 -0
- package/dist/src/pieces/index.js +1 -0
- package/dist/src/pieces/index.js.map +1 -1
- package/dist/src/pieces/utils/piece.d.ts +3 -2
- package/dist/src/pieces/utils/piece.js +17 -14
- package/dist/src/pieces/utils/piece.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -0
- package/package.json +3 -3
- package/dist/src/database/NodeSqliteDialect.test.d.ts +0 -1
- package/dist/src/database/NodeSqliteDialect.test.js +0 -104
- package/dist/src/database/NodeSqliteDialect.test.js.map +0 -1
- package/dist/src/database/client.test.d.ts +0 -1
- package/dist/src/database/client.test.js +0 -36
- package/dist/src/database/client.test.js.map +0 -1
- package/dist/src/database/migrations.test.d.ts +0 -1
- package/dist/src/database/migrations.test.js +0 -19
- package/dist/src/database/migrations.test.js.map +0 -1
- package/dist/src/database/utils.test.d.ts +0 -1
- package/dist/src/database/utils.test.js +0 -9
- package/dist/src/database/utils.test.js.map +0 -1
- package/dist/src/index.test.d.ts +0 -1
- package/dist/src/index.test.js +0 -9
- package/dist/src/index.test.js.map +0 -1
- package/dist/src/lib/ajv.test.d.ts +0 -1
- package/dist/src/lib/ajv.test.js +0 -49
- package/dist/src/lib/ajv.test.js.map +0 -1
- package/dist/src/lib/frontmatter.test.d.ts +0 -1
- package/dist/src/lib/frontmatter.test.js +0 -37
- package/dist/src/lib/frontmatter.test.js.map +0 -1
- package/dist/src/lib/markdown.test.d.ts +0 -1
- package/dist/src/lib/markdown.test.js +0 -34
- package/dist/src/lib/markdown.test.js.map +0 -1
- package/dist/src/llm/google.test.d.ts +0 -1
- package/dist/src/llm/google.test.js +0 -253
- package/dist/src/llm/google.test.js.map +0 -1
- package/dist/src/pieces/Piece.test.d.ts +0 -1
- package/dist/src/pieces/Piece.test.js +0 -628
- package/dist/src/pieces/Piece.test.js.map +0 -1
- package/dist/src/pieces/Pieces.test.d.ts +0 -1
- package/dist/src/pieces/Pieces.test.js +0 -325
- package/dist/src/pieces/Pieces.test.js.map +0 -1
- package/dist/src/pieces/cache.test.d.ts +0 -1
- package/dist/src/pieces/cache.test.js +0 -72
- package/dist/src/pieces/cache.test.js.map +0 -1
- package/dist/src/pieces/item.test.d.ts +0 -1
- package/dist/src/pieces/item.test.js +0 -243
- package/dist/src/pieces/item.test.js.map +0 -1
- package/dist/src/pieces/items.test.d.ts +0 -1
- package/dist/src/pieces/items.test.js +0 -103
- package/dist/src/pieces/items.test.js.map +0 -1
- package/dist/src/pieces/json.schema.test.d.ts +0 -1
- package/dist/src/pieces/json.schema.test.js +0 -27
- package/dist/src/pieces/json.schema.test.js.map +0 -1
- package/dist/src/pieces/manager.test.d.ts +0 -1
- package/dist/src/pieces/manager.test.js +0 -55
- package/dist/src/pieces/manager.test.js.map +0 -1
- package/dist/src/pieces/utils/frontmatter.path.test.d.ts +0 -1
- package/dist/src/pieces/utils/frontmatter.path.test.js +0 -319
- package/dist/src/pieces/utils/frontmatter.path.test.js.map +0 -1
- package/dist/src/pieces/utils/frontmatter.test.d.ts +0 -1
- package/dist/src/pieces/utils/frontmatter.test.js +0 -335
- package/dist/src/pieces/utils/frontmatter.test.js.map +0 -1
- package/dist/src/pieces/utils/markdown.test.d.ts +0 -1
- package/dist/src/pieces/utils/markdown.test.js +0 -38
- package/dist/src/pieces/utils/markdown.test.js.map +0 -1
- package/dist/src/pieces/utils/piece.test.d.ts +0 -1
- package/dist/src/pieces/utils/piece.test.js +0 -348
- package/dist/src/pieces/utils/piece.test.js.map +0 -1
- package/dist/src/storage/fs.test.d.ts +0 -1
- package/dist/src/storage/fs.test.js +0 -212
- package/dist/src/storage/fs.test.js.map +0 -1
- package/dist/test/db.d.ts +0 -3
- package/dist/test/db.js +0 -19
- package/dist/test/db.js.map +0 -1
- package/dist/tsconfig.tsbuildinfo +0 -1
|
@@ -1,348 +0,0 @@
|
|
|
1
|
-
import { createReadStream, existsSync } from 'fs';
|
|
2
|
-
import { copyFile, stat } from 'fs/promises'; // Direct import matching source
|
|
3
|
-
import { describe, expect, test, vi, afterEach, beforeAll } from 'vitest';
|
|
4
|
-
import { createHash } from 'crypto';
|
|
5
|
-
import { PassThrough, Readable } from 'stream';
|
|
6
|
-
import got from 'got';
|
|
7
|
-
import path from 'path';
|
|
8
|
-
import { ASSETS_DIRECTORY } from '../assets.js';
|
|
9
|
-
import { calculateHashFromFile, isAttachableStream, makePieceAttachment, makePieceValue, detectStreamFileType, } from './piece.js';
|
|
10
|
-
import { makeStorage } from '../../storage/storage.mock.js';
|
|
11
|
-
import { makeMarkdownSample } from '../Piece.fixtures.js';
|
|
12
|
-
vi.mock('fs/promises');
|
|
13
|
-
vi.mock('crypto');
|
|
14
|
-
vi.mock('fs');
|
|
15
|
-
vi.mock('got');
|
|
16
|
-
const mocks = {
|
|
17
|
-
copyFile: vi.mocked(copyFile),
|
|
18
|
-
stat: vi.mocked(stat),
|
|
19
|
-
existsSync: vi.mocked(existsSync),
|
|
20
|
-
createReadStream: vi.mocked(createReadStream),
|
|
21
|
-
createHash: vi.mocked(createHash),
|
|
22
|
-
gotStream: vi.mocked(got.stream),
|
|
23
|
-
};
|
|
24
|
-
const spies = {};
|
|
25
|
-
let fullPngBuffer;
|
|
26
|
-
describe('pieces/utils/piece.ts', () => {
|
|
27
|
-
beforeAll(async () => {
|
|
28
|
-
const { readFile } = await vi.importActual('fs/promises');
|
|
29
|
-
const assetPath = path.resolve('test/assets/favicon.png');
|
|
30
|
-
fullPngBuffer = await readFile(assetPath);
|
|
31
|
-
});
|
|
32
|
-
afterEach(() => {
|
|
33
|
-
Object.values(mocks).forEach((mock) => {
|
|
34
|
-
mock.mockReset();
|
|
35
|
-
});
|
|
36
|
-
Object.keys(spies).forEach((key) => {
|
|
37
|
-
spies[key].mockRestore();
|
|
38
|
-
delete spies[key];
|
|
39
|
-
});
|
|
40
|
-
vi.restoreAllMocks();
|
|
41
|
-
});
|
|
42
|
-
test('calculateHashFromFile', async () => {
|
|
43
|
-
const data = 'data';
|
|
44
|
-
const mockUpdate = vi.fn();
|
|
45
|
-
const mockDigest = vi.fn().mockReturnValue(data);
|
|
46
|
-
const mockReadStream = new PassThrough();
|
|
47
|
-
mocks.createReadStream.mockReturnValueOnce(mockReadStream);
|
|
48
|
-
mocks.createHash.mockReturnValueOnce({
|
|
49
|
-
update: mockUpdate,
|
|
50
|
-
digest: mockDigest,
|
|
51
|
-
});
|
|
52
|
-
const hashPromise = calculateHashFromFile(mockReadStream);
|
|
53
|
-
mockReadStream.emit('data', data);
|
|
54
|
-
mockReadStream.emit('end');
|
|
55
|
-
const hash = await hashPromise;
|
|
56
|
-
expect(mockUpdate).toHaveBeenCalled();
|
|
57
|
-
expect(mockDigest).toHaveBeenCalledWith('hex');
|
|
58
|
-
expect(hash).toEqual(data);
|
|
59
|
-
});
|
|
60
|
-
test('calculateHashFromFile error', async () => {
|
|
61
|
-
const data = 'data';
|
|
62
|
-
const mockUpdate = vi.fn();
|
|
63
|
-
const mockDigest = vi.fn().mockReturnValue(data);
|
|
64
|
-
const mockReadStream = new PassThrough();
|
|
65
|
-
mocks.createReadStream.mockReturnValueOnce(mockReadStream);
|
|
66
|
-
mocks.createHash.mockReturnValueOnce({
|
|
67
|
-
update: mockUpdate,
|
|
68
|
-
digest: mockDigest,
|
|
69
|
-
});
|
|
70
|
-
spies.consoleError = vi.spyOn(console, 'error');
|
|
71
|
-
const hashPromise = calculateHashFromFile(mockReadStream);
|
|
72
|
-
mockReadStream.emit('error', new Error('error'));
|
|
73
|
-
await expect(hashPromise).rejects.toThrowError();
|
|
74
|
-
expect(spies.consoleError).toHaveBeenCalled();
|
|
75
|
-
});
|
|
76
|
-
test('detectStreamFileType should correctly identify PNG from full buffer', async () => {
|
|
77
|
-
const stream = Readable.from([fullPngBuffer]);
|
|
78
|
-
const result = await detectStreamFileType(stream);
|
|
79
|
-
expect(result.type).toEqual({ ext: 'png', mime: 'image/png' });
|
|
80
|
-
const resultChunks = [];
|
|
81
|
-
for await (const chunk of result.stream) {
|
|
82
|
-
resultChunks.push(chunk);
|
|
83
|
-
}
|
|
84
|
-
const finalBuffer = Buffer.concat(resultChunks);
|
|
85
|
-
expect(finalBuffer.toString('hex')).toEqual(fullPngBuffer.toString('hex'));
|
|
86
|
-
});
|
|
87
|
-
test('detectStreamFileType with string chunks (non-Buffer)', async () => {
|
|
88
|
-
const stream = Readable.from(['hello ', 'world']);
|
|
89
|
-
const result = await detectStreamFileType(stream);
|
|
90
|
-
expect(result.type).toBeUndefined();
|
|
91
|
-
const resultChunks = [];
|
|
92
|
-
for await (const chunk of result.stream) {
|
|
93
|
-
resultChunks.push(chunk);
|
|
94
|
-
}
|
|
95
|
-
const finalString = Buffer.concat(resultChunks).toString();
|
|
96
|
-
expect(finalString).toEqual('hello world');
|
|
97
|
-
});
|
|
98
|
-
test('detectStreamFileType with custom maxBytes and multiple chunks', async () => {
|
|
99
|
-
const chunk1 = Buffer.from('hello ');
|
|
100
|
-
const chunk2 = Buffer.from('world');
|
|
101
|
-
const chunk3 = Buffer.from('!');
|
|
102
|
-
const stream = Readable.from([chunk1, chunk2, chunk3]);
|
|
103
|
-
const result = await detectStreamFileType(stream, 5);
|
|
104
|
-
const resultChunks = [];
|
|
105
|
-
for await (const chunk of result.stream) {
|
|
106
|
-
resultChunks.push(chunk);
|
|
107
|
-
}
|
|
108
|
-
const finalString = Buffer.concat(resultChunks).toString();
|
|
109
|
-
expect(finalString).toEqual('hello world!');
|
|
110
|
-
});
|
|
111
|
-
test('isAttachableStream returns true for valid stream wrapper', () => {
|
|
112
|
-
const stream = new PassThrough();
|
|
113
|
-
expect(isAttachableStream({ stream })).toBe(true);
|
|
114
|
-
expect(isAttachableStream({ stream, filename: 'file.jpg' })).toBe(true);
|
|
115
|
-
});
|
|
116
|
-
test('isAttachableStream returns false for non-objects', () => {
|
|
117
|
-
expect(isAttachableStream('string')).toBe(false);
|
|
118
|
-
expect(isAttachableStream(42)).toBe(false);
|
|
119
|
-
expect(isAttachableStream(null)).toBe(false);
|
|
120
|
-
expect(isAttachableStream(undefined)).toBe(false);
|
|
121
|
-
});
|
|
122
|
-
test('isAttachableStream returns false for objects without stream', () => {
|
|
123
|
-
expect(isAttachableStream({})).toBe(false);
|
|
124
|
-
expect(isAttachableStream({ filename: 'file.jpg' })).toBe(false);
|
|
125
|
-
});
|
|
126
|
-
test('isAttachableStream returns false when stream has no pipe method', () => {
|
|
127
|
-
expect(isAttachableStream({ stream: {} })).toBe(false);
|
|
128
|
-
expect(isAttachableStream({ stream: null })).toBe(false);
|
|
129
|
-
});
|
|
130
|
-
test('makePieceValue', async () => {
|
|
131
|
-
const field = { name: 'title', type: 'string' };
|
|
132
|
-
const value = 'new title';
|
|
133
|
-
const pieceValue = await makePieceValue(field, value);
|
|
134
|
-
expect(pieceValue).toEqual(value);
|
|
135
|
-
});
|
|
136
|
-
test('makePieceValue array', async () => {
|
|
137
|
-
const field = {
|
|
138
|
-
name: 'title',
|
|
139
|
-
type: 'array',
|
|
140
|
-
items: { type: 'string' },
|
|
141
|
-
};
|
|
142
|
-
const value = 'new title';
|
|
143
|
-
const pieceValue = await makePieceValue(field, value);
|
|
144
|
-
expect(pieceValue).toEqual(value);
|
|
145
|
-
});
|
|
146
|
-
test('makePieceValue boolean', async () => {
|
|
147
|
-
const field = { name: 'title', type: 'boolean' };
|
|
148
|
-
const pieceValueT = await makePieceValue(field, 'true');
|
|
149
|
-
const pieceValueF = await makePieceValue(field, 'false');
|
|
150
|
-
expect(pieceValueT).toEqual(true);
|
|
151
|
-
expect(pieceValueF).toEqual(false);
|
|
152
|
-
});
|
|
153
|
-
test('makePieceValue integer', async () => {
|
|
154
|
-
const field = { name: 'title', type: 'integer' };
|
|
155
|
-
const pieceValue = await makePieceValue(field, '101');
|
|
156
|
-
expect(pieceValue).toEqual(101);
|
|
157
|
-
});
|
|
158
|
-
test('makePieceValue preserves objects', async () => {
|
|
159
|
-
const field = { name: 'meta', type: 'object' };
|
|
160
|
-
const value = { author: 'Bob' };
|
|
161
|
-
expect(await makePieceValue(field, value)).toBe(value);
|
|
162
|
-
});
|
|
163
|
-
test('makePieceValue path asset', async () => {
|
|
164
|
-
const field = { name: 'title', type: 'string', format: 'asset' };
|
|
165
|
-
const asset = '/path/to/asset';
|
|
166
|
-
const readable = new PassThrough();
|
|
167
|
-
mocks.createReadStream.mockReturnValueOnce(readable);
|
|
168
|
-
mocks.stat.mockResolvedValueOnce({ isFile: () => true }); // Properly mock resolved value
|
|
169
|
-
const pieceValuePromise = makePieceValue(field, asset);
|
|
170
|
-
process.nextTick(() => {
|
|
171
|
-
readable.emit('open');
|
|
172
|
-
});
|
|
173
|
-
const pieceValue = await pieceValuePromise;
|
|
174
|
-
expect(pieceValue.stream).toEqual(readable);
|
|
175
|
-
});
|
|
176
|
-
test('makePieceValue url asset', async () => {
|
|
177
|
-
const field = { name: 'title', type: 'string', format: 'asset' };
|
|
178
|
-
const asset = 'https://path/to/asset';
|
|
179
|
-
const readable = new PassThrough();
|
|
180
|
-
mocks.gotStream.mockReturnValueOnce(readable);
|
|
181
|
-
const pieceValuePromise = makePieceValue(field, asset);
|
|
182
|
-
readable.emit('response', { statusCode: 200 });
|
|
183
|
-
const pieceValue = await pieceValuePromise;
|
|
184
|
-
expect(pieceValue.stream).toEqual(readable);
|
|
185
|
-
});
|
|
186
|
-
test('makePieceValue url asset bad status Code', async () => {
|
|
187
|
-
const field = { name: 'title', type: 'string', format: 'asset' };
|
|
188
|
-
const asset = 'https://path/to/asset';
|
|
189
|
-
const readable = new PassThrough();
|
|
190
|
-
mocks.gotStream.mockReturnValueOnce(readable);
|
|
191
|
-
const pieceValuePromise = makePieceValue(field, asset);
|
|
192
|
-
readable.emit('response', { statusCode: 500 });
|
|
193
|
-
await expect(pieceValuePromise).rejects.toThrow();
|
|
194
|
-
});
|
|
195
|
-
test('makePieceValue bad url asset', async () => {
|
|
196
|
-
const field = { name: 'title', type: 'string', format: 'asset' };
|
|
197
|
-
const asset = 'https://path/to/asset';
|
|
198
|
-
const readable = new PassThrough();
|
|
199
|
-
mocks.gotStream.mockReturnValueOnce(readable);
|
|
200
|
-
const pieceValuePromise = makePieceValue(field, asset);
|
|
201
|
-
readable.emit('error', new Error('test error'));
|
|
202
|
-
await expect(pieceValuePromise).rejects.toThrow('test error');
|
|
203
|
-
});
|
|
204
|
-
test('makePieceValue bad file asset', async () => {
|
|
205
|
-
const field = { name: 'title', type: 'string', format: 'asset' };
|
|
206
|
-
const asset = '/path/to/bad/file.jpg';
|
|
207
|
-
const readable = new PassThrough();
|
|
208
|
-
mocks.createReadStream.mockReturnValueOnce(readable);
|
|
209
|
-
mocks.stat.mockResolvedValueOnce({ isFile: () => true });
|
|
210
|
-
spies.consoleError = vi.spyOn(console, 'error');
|
|
211
|
-
const pieceValuePromise = makePieceValue(field, asset);
|
|
212
|
-
// Emit error synchronously or ensure the promise chain catches it
|
|
213
|
-
setTimeout(() => {
|
|
214
|
-
readable.emit('error', new Error('test file error'));
|
|
215
|
-
}, 0);
|
|
216
|
-
await expect(pieceValuePromise).rejects.toThrow('test file error');
|
|
217
|
-
expect(spies.consoleError).toHaveBeenCalled();
|
|
218
|
-
});
|
|
219
|
-
test('makePieceValue existing asset', async () => {
|
|
220
|
-
const field = { name: 'title', type: 'string', format: 'asset' };
|
|
221
|
-
const asset = `${ASSETS_DIRECTORY}/path/to/asset`;
|
|
222
|
-
const readable = new PassThrough();
|
|
223
|
-
mocks.createReadStream.mockReturnValueOnce(readable);
|
|
224
|
-
mocks.stat.mockResolvedValueOnce({ isFile: () => true });
|
|
225
|
-
const pieceValue = await makePieceValue(field, asset);
|
|
226
|
-
expect(pieceValue).toEqual(asset);
|
|
227
|
-
});
|
|
228
|
-
test('makePieceValue not-existant path', async () => {
|
|
229
|
-
const field = { name: 'title', type: 'string', format: 'asset' };
|
|
230
|
-
const asset = `path/to/asset`;
|
|
231
|
-
const readable = new PassThrough();
|
|
232
|
-
mocks.createReadStream.mockReturnValueOnce(readable);
|
|
233
|
-
mocks.stat.mockResolvedValueOnce(null);
|
|
234
|
-
const waiting = makePieceValue(field, asset);
|
|
235
|
-
await expect(waiting).rejects.toThrowError();
|
|
236
|
-
});
|
|
237
|
-
test('makePieceValue with stream', async () => {
|
|
238
|
-
const field = { name: 'title', type: 'string', format: 'asset' };
|
|
239
|
-
const readable = new PassThrough();
|
|
240
|
-
const pieceValue = await makePieceValue(field, { stream: readable });
|
|
241
|
-
expect(mocks.stat).not.toHaveBeenCalled();
|
|
242
|
-
expect(pieceValue.stream).toEqual(readable);
|
|
243
|
-
});
|
|
244
|
-
test('makePieceValue with invalid value', async () => {
|
|
245
|
-
const field = { name: 'title', type: 'string', format: 'asset' };
|
|
246
|
-
const making = makePieceValue(field, 55);
|
|
247
|
-
await expect(making).rejects.toThrowError();
|
|
248
|
-
});
|
|
249
|
-
test('makePieceAttachment should create an asset from a stream (PNG via magic bytes)', async () => {
|
|
250
|
-
const field = { name: 'cover', type: 'string', format: 'asset' };
|
|
251
|
-
const storage = makeStorage('root');
|
|
252
|
-
const markdown = makeMarkdownSample('samplePath', 'books', '', { cover: 'cover.jpg' });
|
|
253
|
-
const mocksWriteStream = new PassThrough();
|
|
254
|
-
const mockStream = { stream: Readable.from([fullPngBuffer]), filename: 'photo.jpg' };
|
|
255
|
-
spies.createWriteStream = vi
|
|
256
|
-
.spyOn(storage, 'createWriteStream')
|
|
257
|
-
.mockReturnValue(mocksWriteStream);
|
|
258
|
-
spies.exists = vi.spyOn(storage, 'exists').mockResolvedValue(false);
|
|
259
|
-
spies.makeDir = vi.spyOn(storage, 'makeDirectory').mockResolvedValue(undefined);
|
|
260
|
-
const asset = await makePieceAttachment(markdown.filePath, field, mockStream, storage);
|
|
261
|
-
const pieceDir = markdown.filePath.replace(/\.[^.]+$/, '');
|
|
262
|
-
expect(asset).toBe(path.join(ASSETS_DIRECTORY, pieceDir, 'photo.png'));
|
|
263
|
-
});
|
|
264
|
-
test('makePieceAttachment should work with field arrays (PNG)', async () => {
|
|
265
|
-
const field = {
|
|
266
|
-
name: 'cover',
|
|
267
|
-
type: 'array',
|
|
268
|
-
items: { format: 'asset' },
|
|
269
|
-
};
|
|
270
|
-
const storage = makeStorage('root');
|
|
271
|
-
const markdown = makeMarkdownSample('samplePath', 'books', '', { cover: 'cover.jpg' });
|
|
272
|
-
const mocksWriteStream = new PassThrough();
|
|
273
|
-
const mockStream = { stream: Readable.from([fullPngBuffer]), filename: 'photo.jpg' };
|
|
274
|
-
spies.createWriteStream = vi
|
|
275
|
-
.spyOn(storage, 'createWriteStream')
|
|
276
|
-
.mockReturnValue(mocksWriteStream);
|
|
277
|
-
spies.exists = vi.spyOn(storage, 'exists').mockResolvedValue(false);
|
|
278
|
-
spies.makeDir = vi.spyOn(storage, 'makeDirectory').mockResolvedValue(undefined);
|
|
279
|
-
const asset = await makePieceAttachment(markdown.filePath, field, mockStream, storage);
|
|
280
|
-
const pieceDir = markdown.filePath.replace(/\.[^.]+$/, '');
|
|
281
|
-
expect(asset).toBe(path.join(ASSETS_DIRECTORY, pieceDir, 'photo.png'));
|
|
282
|
-
});
|
|
283
|
-
test('makePieceAttachment should use filename for name and ext (text file fallback)', async () => {
|
|
284
|
-
const field = { name: 'script', type: 'string', format: 'asset' };
|
|
285
|
-
const storage = makeStorage('root');
|
|
286
|
-
const markdown = makeMarkdownSample('samplePath', 'books', '', { script: 'deploy.bash' });
|
|
287
|
-
const mocksWriteStream = new PassThrough();
|
|
288
|
-
const mockStream = {
|
|
289
|
-
stream: Readable.from([Buffer.from('#!/bin/bash\necho hi')]),
|
|
290
|
-
filename: 'deploy.bash',
|
|
291
|
-
};
|
|
292
|
-
spies.createWriteStream = vi
|
|
293
|
-
.spyOn(storage, 'createWriteStream')
|
|
294
|
-
.mockReturnValue(mocksWriteStream);
|
|
295
|
-
spies.exists = vi.spyOn(storage, 'exists').mockResolvedValue(false);
|
|
296
|
-
spies.makeDir = vi.spyOn(storage, 'makeDirectory').mockResolvedValue(undefined);
|
|
297
|
-
const asset = await makePieceAttachment(markdown.filePath, field, mockStream, storage);
|
|
298
|
-
const pieceDir = markdown.filePath.replace(/\.[^.]+$/, '');
|
|
299
|
-
expect(asset).toBe(path.join(ASSETS_DIRECTORY, pieceDir, 'deploy.bash'));
|
|
300
|
-
});
|
|
301
|
-
test('makePieceAttachment should fall back to field name with no ext for bare Readable', async () => {
|
|
302
|
-
const field = { name: 'cover', type: 'string', format: 'asset' };
|
|
303
|
-
const storage = makeStorage('root');
|
|
304
|
-
const markdown = makeMarkdownSample('samplePath.md', 'books', '', { cover: 'cover.bin' });
|
|
305
|
-
const mocksWriteStream = new PassThrough();
|
|
306
|
-
// Generic Readable: no path info, no magic bytes detectable
|
|
307
|
-
const mockGenericReadable = {
|
|
308
|
-
stream: Readable.from([Buffer.from('some binary data')]),
|
|
309
|
-
};
|
|
310
|
-
spies.createWriteStream = vi
|
|
311
|
-
.spyOn(storage, 'createWriteStream')
|
|
312
|
-
.mockReturnValue(mocksWriteStream);
|
|
313
|
-
spies.exists = vi.spyOn(storage, 'exists').mockResolvedValue(false);
|
|
314
|
-
spies.makeDir = vi.spyOn(storage, 'makeDirectory').mockResolvedValue(undefined);
|
|
315
|
-
const asset = await makePieceAttachment(markdown.filePath, field, mockGenericReadable, storage);
|
|
316
|
-
const pieceDir = markdown.filePath.replace(/\.[^.]+$/, '');
|
|
317
|
-
// Falls back to field name, no extension (not .md)
|
|
318
|
-
expect(asset).toBe(path.join(ASSETS_DIRECTORY, pieceDir, 'cover'));
|
|
319
|
-
});
|
|
320
|
-
test('makePieceAttachment should increment counter on filename collision', async () => {
|
|
321
|
-
const field = { name: 'cover', type: 'string', format: 'asset' };
|
|
322
|
-
const storage = makeStorage('root');
|
|
323
|
-
const markdown = makeMarkdownSample('samplePath', 'books', '', { cover: 'cover.jpg' });
|
|
324
|
-
const mocksWriteStream = new PassThrough();
|
|
325
|
-
const mockStream = { stream: Readable.from([fullPngBuffer]), filename: 'photo.jpg' };
|
|
326
|
-
spies.createWriteStream = vi
|
|
327
|
-
.spyOn(storage, 'createWriteStream')
|
|
328
|
-
.mockReturnValue(mocksWriteStream);
|
|
329
|
-
spies.makeDir = vi.spyOn(storage, 'makeDirectory').mockResolvedValue(undefined);
|
|
330
|
-
// First call: attachDir doesn't exist, target file doesn't exist
|
|
331
|
-
spies.exists = vi
|
|
332
|
-
.spyOn(storage, 'exists')
|
|
333
|
-
.mockResolvedValueOnce(false) // attachDir check
|
|
334
|
-
.mockResolvedValueOnce(true) // photo.png exists → collision
|
|
335
|
-
.mockResolvedValueOnce(false); // photo-2.png free
|
|
336
|
-
const asset = await makePieceAttachment(markdown.filePath, field, mockStream, storage);
|
|
337
|
-
const pieceDir = markdown.filePath.replace(/\.[^.]+$/, '');
|
|
338
|
-
expect(asset).toBe(path.join(ASSETS_DIRECTORY, pieceDir, 'photo-2.png'));
|
|
339
|
-
});
|
|
340
|
-
test('makePieceAttachment throws for non-asset field', async () => {
|
|
341
|
-
const field = { name: 'title', type: 'string' };
|
|
342
|
-
const stream = { stream: new PassThrough() };
|
|
343
|
-
const storage = makeStorage('root');
|
|
344
|
-
const asset = makePieceAttachment('file', field, stream, storage);
|
|
345
|
-
await expect(asset).rejects.toThrowError();
|
|
346
|
-
});
|
|
347
|
-
});
|
|
348
|
-
//# sourceMappingURL=piece.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"piece.test.js","sourceRoot":"","sources":["../../../../src/pieces/utils/piece.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { createReadStream, existsSync, ReadStream, Stats, WriteStream } from 'fs'\nimport { copyFile, stat } from 'fs/promises' // Direct import matching source\nimport { describe, expect, test, vi, afterEach, beforeAll, MockInstance } from 'vitest'\nimport { createHash } from 'crypto'\nimport { PassThrough, Readable } from 'stream'\nimport got, { Request } from 'got'\nimport path from 'path'\nimport { ASSETS_DIRECTORY } from '../assets.js'\nimport {\n\tcalculateHashFromFile,\n\tisAttachableStream,\n\tmakePieceAttachment,\n\tmakePieceValue,\n\tdetectStreamFileType,\n\tAttachableStream,\n} from './piece.js'\nimport { PieceFrontmatterSchemaField } from './frontmatter.js'\nimport { makeStorage } from '../../storage/storage.mock.js'\nimport { makeMarkdownSample } from '../Piece.fixtures.js'\n\nvi.mock('fs/promises')\nvi.mock('crypto')\nvi.mock('fs')\nvi.mock('got')\n\nconst mocks = {\n\tcopyFile: vi.mocked(copyFile),\n\tstat: vi.mocked(stat),\n\texistsSync: vi.mocked(existsSync),\n\tcreateReadStream: vi.mocked(createReadStream),\n\tcreateHash: vi.mocked(createHash),\n\tgotStream: vi.mocked(got.stream),\n}\n\nconst spies: { [key: string]: MockInstance } = {}\n\nlet fullPngBuffer: Buffer\n\ndescribe('pieces/utils/piece.ts', () => {\n\tbeforeAll(async () => {\n\t\tconst { readFile } = await vi.importActual<typeof import('fs/promises')>('fs/promises')\n\t\tconst assetPath = path.resolve('test/assets/favicon.png')\n\t\tfullPngBuffer = await readFile(assetPath)\n\t})\n\n\tafterEach(() => {\n\t\tObject.values(mocks).forEach((mock) => {\n\t\t\tmock.mockReset()\n\t\t})\n\n\t\tObject.keys(spies).forEach((key) => {\n\t\t\tspies[key].mockRestore()\n\t\t\tdelete spies[key]\n\t\t})\n\t\tvi.restoreAllMocks()\n\t})\n\n\ttest('calculateHashFromFile', async () => {\n\t\tconst data = 'data'\n\n\t\tconst mockUpdate = vi.fn()\n\t\tconst mockDigest = vi.fn().mockReturnValue(data)\n\t\tconst mockReadStream = new PassThrough() as unknown as ReadStream\n\n\t\tmocks.createReadStream.mockReturnValueOnce(mockReadStream)\n\t\tmocks.createHash.mockReturnValueOnce({\n\t\t\tupdate: mockUpdate,\n\t\t\tdigest: mockDigest,\n\t\t} as unknown as ReturnType<typeof createHash>)\n\n\t\tconst hashPromise = calculateHashFromFile(mockReadStream)\n\n\t\tmockReadStream.emit('data', data)\n\t\tmockReadStream.emit('end')\n\n\t\tconst hash = await hashPromise\n\n\t\texpect(mockUpdate).toHaveBeenCalled()\n\t\texpect(mockDigest).toHaveBeenCalledWith('hex')\n\t\texpect(hash).toEqual(data)\n\t})\n\n\ttest('calculateHashFromFile error', async () => {\n\t\tconst data = 'data'\n\n\t\tconst mockUpdate = vi.fn()\n\t\tconst mockDigest = vi.fn().mockReturnValue(data)\n\t\tconst mockReadStream = new PassThrough() as unknown as ReadStream\n\n\t\tmocks.createReadStream.mockReturnValueOnce(mockReadStream)\n\t\tmocks.createHash.mockReturnValueOnce({\n\t\t\tupdate: mockUpdate,\n\t\t\tdigest: mockDigest,\n\t\t} as unknown as ReturnType<typeof createHash>)\n\t\tspies.consoleError = vi.spyOn(console, 'error')\n\n\t\tconst hashPromise = calculateHashFromFile(mockReadStream)\n\n\t\tmockReadStream.emit('error', new Error('error'))\n\n\t\tawait expect(hashPromise).rejects.toThrowError()\n\t\texpect(spies.consoleError).toHaveBeenCalled()\n\t})\n\n\ttest('detectStreamFileType should correctly identify PNG from full buffer', async () => {\n\t\tconst stream = Readable.from([fullPngBuffer])\n\n\t\tconst result = await detectStreamFileType(stream)\n\n\t\texpect(result.type).toEqual({ ext: 'png', mime: 'image/png' })\n\n\t\tconst resultChunks = []\n\t\tfor await (const chunk of result.stream) {\n\t\t\tresultChunks.push(chunk)\n\t\t}\n\t\tconst finalBuffer = Buffer.concat(resultChunks)\n\n\t\texpect(finalBuffer.toString('hex')).toEqual(fullPngBuffer.toString('hex'))\n\t})\n\n\ttest('detectStreamFileType with string chunks (non-Buffer)', async () => {\n\t\tconst stream = Readable.from(['hello ', 'world'])\n\n\t\tconst result = await detectStreamFileType(stream)\n\n\t\texpect(result.type).toBeUndefined()\n\n\t\tconst resultChunks = []\n\t\tfor await (const chunk of result.stream) {\n\t\t\tresultChunks.push(chunk)\n\t\t}\n\t\tconst finalString = Buffer.concat(resultChunks).toString()\n\n\t\texpect(finalString).toEqual('hello world')\n\t})\n\n\ttest('detectStreamFileType with custom maxBytes and multiple chunks', async () => {\n\t\tconst chunk1 = Buffer.from('hello ')\n\t\tconst chunk2 = Buffer.from('world')\n\t\tconst chunk3 = Buffer.from('!')\n\t\tconst stream = Readable.from([chunk1, chunk2, chunk3])\n\n\t\tconst result = await detectStreamFileType(stream, 5)\n\n\t\tconst resultChunks = []\n\t\tfor await (const chunk of result.stream) {\n\t\t\tresultChunks.push(chunk)\n\t\t}\n\t\tconst finalString = Buffer.concat(resultChunks).toString()\n\n\t\texpect(finalString).toEqual('hello world!')\n\t})\n\n\ttest('isAttachableStream returns true for valid stream wrapper', () => {\n\t\tconst stream = new PassThrough()\n\t\texpect(isAttachableStream({ stream })).toBe(true)\n\t\texpect(isAttachableStream({ stream, filename: 'file.jpg' })).toBe(true)\n\t})\n\n\ttest('isAttachableStream returns false for non-objects', () => {\n\t\texpect(isAttachableStream('string')).toBe(false)\n\t\texpect(isAttachableStream(42)).toBe(false)\n\t\texpect(isAttachableStream(null)).toBe(false)\n\t\texpect(isAttachableStream(undefined)).toBe(false)\n\t})\n\n\ttest('isAttachableStream returns false for objects without stream', () => {\n\t\texpect(isAttachableStream({})).toBe(false)\n\t\texpect(isAttachableStream({ filename: 'file.jpg' })).toBe(false)\n\t})\n\n\ttest('isAttachableStream returns false when stream has no pipe method', () => {\n\t\texpect(isAttachableStream({ stream: {} })).toBe(false)\n\t\texpect(isAttachableStream({ stream: null })).toBe(false)\n\t})\n\n\ttest('makePieceValue', async () => {\n\t\tconst field = { name: 'title', type: 'string' } as PieceFrontmatterSchemaField\n\t\tconst value = 'new title'\n\n\t\tconst pieceValue = await makePieceValue(field, value)\n\n\t\texpect(pieceValue).toEqual(value)\n\t})\n\n\ttest('makePieceValue array', async () => {\n\t\tconst field = {\n\t\t\tname: 'title',\n\t\t\ttype: 'array',\n\t\t\titems: { type: 'string' },\n\t\t} as PieceFrontmatterSchemaField\n\t\tconst value = 'new title'\n\n\t\tconst pieceValue = await makePieceValue(field, value)\n\n\t\texpect(pieceValue).toEqual(value)\n\t})\n\n\ttest('makePieceValue boolean', async () => {\n\t\tconst field = { name: 'title', type: 'boolean' } as PieceFrontmatterSchemaField\n\n\t\tconst pieceValueT = await makePieceValue(field, 'true')\n\t\tconst pieceValueF = await makePieceValue(field, 'false')\n\n\t\texpect(pieceValueT).toEqual(true)\n\t\texpect(pieceValueF).toEqual(false)\n\t})\n\n\ttest('makePieceValue integer', async () => {\n\t\tconst field = { name: 'title', type: 'integer' } as PieceFrontmatterSchemaField\n\n\t\tconst pieceValue = await makePieceValue(field, '101')\n\n\t\texpect(pieceValue).toEqual(101)\n\t})\n\n\ttest('makePieceValue preserves objects', async () => {\n\t\tconst field = { name: 'meta', type: 'object' } as PieceFrontmatterSchemaField\n\n\t\tconst value = { author: 'Bob' }\n\n\t\texpect(await makePieceValue(field, value)).toBe(value)\n\t})\n\n\ttest('makePieceValue path asset', async () => {\n\t\tconst field = { name: 'title', type: 'string', format: 'asset' } as PieceFrontmatterSchemaField\n\t\tconst asset = '/path/to/asset'\n\t\tconst readable = new PassThrough() as unknown as ReadStream\n\n\t\tmocks.createReadStream.mockReturnValueOnce(readable)\n\t\tmocks.stat.mockResolvedValueOnce({ isFile: () => true } as Stats) // Properly mock resolved value\n\n\t\tconst pieceValuePromise = makePieceValue(field, asset)\n\n\t\tprocess.nextTick(() => {\n\t\t\treadable.emit('open')\n\t\t})\n\n\t\tconst pieceValue = await pieceValuePromise as AttachableStream\n\n\t\texpect(pieceValue.stream).toEqual(readable)\n\t})\n\n\ttest('makePieceValue url asset', async () => {\n\t\tconst field = { name: 'title', type: 'string', format: 'asset' } as PieceFrontmatterSchemaField\n\t\tconst asset = 'https://path/to/asset'\n\t\tconst readable = new PassThrough() as unknown as Request\n\n\t\tmocks.gotStream.mockReturnValueOnce(readable)\n\n\t\tconst pieceValuePromise = makePieceValue(field, asset)\n\n\t\treadable.emit('response', { statusCode: 200 })\n\n\t\tconst pieceValue = await pieceValuePromise as AttachableStream\n\n\t\texpect(pieceValue.stream).toEqual(readable)\n\t})\n\n\ttest('makePieceValue url asset bad status Code', async () => {\n\t\tconst field = { name: 'title', type: 'string', format: 'asset' } as PieceFrontmatterSchemaField\n\t\tconst asset = 'https://path/to/asset'\n\t\tconst readable = new PassThrough() as unknown as Request\n\n\t\tmocks.gotStream.mockReturnValueOnce(readable)\n\n\t\tconst pieceValuePromise = makePieceValue(field, asset)\n\n\t\treadable.emit('response', { statusCode: 500 })\n\n\t\tawait expect(pieceValuePromise).rejects.toThrow()\n\t})\n\n\ttest('makePieceValue bad url asset', async () => {\n\t\tconst field = { name: 'title', type: 'string', format: 'asset' } as PieceFrontmatterSchemaField\n\t\tconst asset = 'https://path/to/asset'\n\t\tconst readable = new PassThrough() as unknown as Request\n\n\t\tmocks.gotStream.mockReturnValueOnce(readable)\n\n\t\tconst pieceValuePromise = makePieceValue(field, asset)\n\n\t\treadable.emit('error', new Error('test error'))\n\n\t\tawait expect(pieceValuePromise).rejects.toThrow('test error')\n\t})\n\n\ttest('makePieceValue bad file asset', async () => {\n\t\tconst field = { name: 'title', type: 'string', format: 'asset' } as PieceFrontmatterSchemaField\n\t\tconst asset = '/path/to/bad/file.jpg'\n\t\tconst readable = new PassThrough() as unknown as ReadStream\n\n\t\tmocks.createReadStream.mockReturnValueOnce(readable)\n\t\tmocks.stat.mockResolvedValueOnce({ isFile: () => true } as Stats)\n\t\tspies.consoleError = vi.spyOn(console, 'error')\n\n\t\tconst pieceValuePromise = makePieceValue(field, asset)\n\n\t\t// Emit error synchronously or ensure the promise chain catches it\n\t\tsetTimeout(() => {\n\t\t\treadable.emit('error', new Error('test file error'))\n\t\t}, 0)\n\n\t\tawait expect(pieceValuePromise).rejects.toThrow('test file error')\n\t\texpect(spies.consoleError).toHaveBeenCalled()\n\t})\n\n\ttest('makePieceValue existing asset', async () => {\n\t\tconst field = { name: 'title', type: 'string', format: 'asset' } as PieceFrontmatterSchemaField\n\t\tconst asset = `${ASSETS_DIRECTORY}/path/to/asset`\n\t\tconst readable = new PassThrough() as unknown as ReadStream\n\n\t\tmocks.createReadStream.mockReturnValueOnce(readable)\n\t\tmocks.stat.mockResolvedValueOnce({ isFile: () => true } as Stats)\n\n\t\tconst pieceValue = await makePieceValue(field, asset)\n\n\t\texpect(pieceValue).toEqual(asset)\n\t})\n\n\ttest('makePieceValue not-existant path', async () => {\n\t\tconst field = { name: 'title', type: 'string', format: 'asset' } as PieceFrontmatterSchemaField\n\t\tconst asset = `path/to/asset`\n\t\tconst readable = new PassThrough() as unknown as ReadStream\n\n\t\tmocks.createReadStream.mockReturnValueOnce(readable)\n\t\tmocks.stat.mockResolvedValueOnce(null as unknown as Stats)\n\n\t\tconst waiting = makePieceValue(field, asset)\n\n\t\tawait expect(waiting).rejects.toThrowError()\n\t})\n\n\ttest('makePieceValue with stream', async () => {\n\t\tconst field = { name: 'title', type: 'string', format: 'asset' } as PieceFrontmatterSchemaField\n\t\tconst readable = new PassThrough() as unknown as ReadStream\n\t\tconst pieceValue = await makePieceValue(field, { stream: readable }) as AttachableStream\n\n\t\texpect(mocks.stat).not.toHaveBeenCalled()\n\t\texpect(pieceValue.stream).toEqual(readable)\n\t})\n\n\ttest('makePieceValue with invalid value', async () => {\n\t\tconst field = { name: 'title', type: 'string', format: 'asset' } as PieceFrontmatterSchemaField\n\t\tconst making = makePieceValue(field, 55)\n\n\t\tawait expect(making).rejects.toThrowError()\n\t})\n\n\ttest('makePieceAttachment should create an asset from a stream (PNG via magic bytes)', async () => {\n\t\tconst field = { name: 'cover', type: 'string', format: 'asset' } as PieceFrontmatterSchemaField\n\t\tconst storage = makeStorage('root')\n\t\tconst markdown = makeMarkdownSample('samplePath', 'books', '', { cover: 'cover.jpg' })\n\t\tconst mocksWriteStream = new PassThrough() as unknown as WriteStream\n\n\t\tconst mockStream = { stream: Readable.from([fullPngBuffer]), filename: 'photo.jpg' }\n\n\t\tspies.createWriteStream = vi\n\t\t\t.spyOn(storage, 'createWriteStream')\n\t\t\t.mockReturnValue(mocksWriteStream)\n\t\tspies.exists = vi.spyOn(storage, 'exists').mockResolvedValue(false)\n\t\tspies.makeDir = vi.spyOn(storage, 'makeDirectory').mockResolvedValue(undefined)\n\n\t\tconst asset = await makePieceAttachment(markdown.filePath, field, mockStream, storage)\n\t\tconst pieceDir = markdown.filePath.replace(/\\.[^.]+$/, '')\n\t\texpect(asset).toBe(path.join(ASSETS_DIRECTORY, pieceDir, 'photo.png'))\n\t})\n\n\ttest('makePieceAttachment should work with field arrays (PNG)', async () => {\n\t\tconst field = {\n\t\t\tname: 'cover',\n\t\t\ttype: 'array',\n\t\t\titems: { format: 'asset' },\n\t\t} as PieceFrontmatterSchemaField\n\t\tconst storage = makeStorage('root')\n\t\tconst markdown = makeMarkdownSample('samplePath', 'books', '', { cover: 'cover.jpg' })\n\t\tconst mocksWriteStream = new PassThrough() as unknown as WriteStream\n\n\t\tconst mockStream = { stream: Readable.from([fullPngBuffer]), filename: 'photo.jpg' }\n\n\t\tspies.createWriteStream = vi\n\t\t\t.spyOn(storage, 'createWriteStream')\n\t\t\t.mockReturnValue(mocksWriteStream)\n\t\tspies.exists = vi.spyOn(storage, 'exists').mockResolvedValue(false)\n\t\tspies.makeDir = vi.spyOn(storage, 'makeDirectory').mockResolvedValue(undefined)\n\n\t\tconst asset = await makePieceAttachment(markdown.filePath, field, mockStream, storage)\n\t\tconst pieceDir = markdown.filePath.replace(/\\.[^.]+$/, '')\n\t\texpect(asset).toBe(path.join(ASSETS_DIRECTORY, pieceDir, 'photo.png'))\n\t})\n\n\ttest('makePieceAttachment should use filename for name and ext (text file fallback)', async () => {\n\t\tconst field = { name: 'script', type: 'string', format: 'asset' } as PieceFrontmatterSchemaField\n\t\tconst storage = makeStorage('root')\n\t\tconst markdown = makeMarkdownSample('samplePath', 'books', '', { script: 'deploy.bash' })\n\t\tconst mocksWriteStream = new PassThrough() as unknown as WriteStream\n\n\t\tconst mockStream = {\n\t\t\tstream: Readable.from([Buffer.from('#!/bin/bash\\necho hi')]),\n\t\t\tfilename: 'deploy.bash',\n\t\t}\n\n\t\tspies.createWriteStream = vi\n\t\t\t.spyOn(storage, 'createWriteStream')\n\t\t\t.mockReturnValue(mocksWriteStream)\n\t\tspies.exists = vi.spyOn(storage, 'exists').mockResolvedValue(false)\n\t\tspies.makeDir = vi.spyOn(storage, 'makeDirectory').mockResolvedValue(undefined)\n\n\t\tconst asset = await makePieceAttachment(markdown.filePath, field, mockStream, storage)\n\t\tconst pieceDir = markdown.filePath.replace(/\\.[^.]+$/, '')\n\t\texpect(asset).toBe(path.join(ASSETS_DIRECTORY, pieceDir, 'deploy.bash'))\n\t})\n\n\ttest('makePieceAttachment should fall back to field name with no ext for bare Readable', async () => {\n\t\tconst field = { name: 'cover', type: 'string', format: 'asset' } as PieceFrontmatterSchemaField\n\t\tconst storage = makeStorage('root')\n\t\tconst markdown = makeMarkdownSample('samplePath.md', 'books', '', { cover: 'cover.bin' })\n\t\tconst mocksWriteStream = new PassThrough() as unknown as WriteStream\n\n\t\t// Generic Readable: no path info, no magic bytes detectable\n\t\tconst mockGenericReadable = {\n\t\t\tstream: Readable.from([Buffer.from('some binary data')]) as unknown as Readable,\n\t\t}\n\n\t\tspies.createWriteStream = vi\n\t\t\t.spyOn(storage, 'createWriteStream')\n\t\t\t.mockReturnValue(mocksWriteStream)\n\t\tspies.exists = vi.spyOn(storage, 'exists').mockResolvedValue(false)\n\t\tspies.makeDir = vi.spyOn(storage, 'makeDirectory').mockResolvedValue(undefined)\n\n\t\tconst asset = await makePieceAttachment(markdown.filePath, field, mockGenericReadable, storage)\n\t\tconst pieceDir = markdown.filePath.replace(/\\.[^.]+$/, '')\n\t\t// Falls back to field name, no extension (not .md)\n\t\texpect(asset).toBe(path.join(ASSETS_DIRECTORY, pieceDir, 'cover'))\n\t})\n\n\ttest('makePieceAttachment should increment counter on filename collision', async () => {\n\t\tconst field = { name: 'cover', type: 'string', format: 'asset' } as PieceFrontmatterSchemaField\n\t\tconst storage = makeStorage('root')\n\t\tconst markdown = makeMarkdownSample('samplePath', 'books', '', { cover: 'cover.jpg' })\n\t\tconst mocksWriteStream = new PassThrough() as unknown as WriteStream\n\n\t\tconst mockStream = {stream: Readable.from([fullPngBuffer]), filename: 'photo.jpg'}\n\n\t\tspies.createWriteStream = vi\n\t\t\t.spyOn(storage, 'createWriteStream')\n\t\t\t.mockReturnValue(mocksWriteStream)\n\t\tspies.makeDir = vi.spyOn(storage, 'makeDirectory').mockResolvedValue(undefined)\n\n\t\t// First call: attachDir doesn't exist, target file doesn't exist\n\t\tspies.exists = vi\n\t\t\t.spyOn(storage, 'exists')\n\t\t\t.mockResolvedValueOnce(false) // attachDir check\n\t\t\t.mockResolvedValueOnce(true) // photo.png exists → collision\n\t\t\t.mockResolvedValueOnce(false) // photo-2.png free\n\n\t\tconst asset = await makePieceAttachment(markdown.filePath, field, mockStream, storage)\n\t\tconst pieceDir = markdown.filePath.replace(/\\.[^.]+$/, '')\n\t\texpect(asset).toBe(path.join(ASSETS_DIRECTORY, pieceDir, 'photo-2.png'))\n\t})\n\n\ttest('makePieceAttachment throws for non-asset field', async () => {\n\t\tconst field = { name: 'title', type: 'string' } as PieceFrontmatterSchemaField\n\t\tconst stream = { stream: new PassThrough() as unknown as Request }\n\t\tconst storage = makeStorage('root')\n\t\tconst asset = makePieceAttachment('file', field, stream, storage)\n\n\t\tawait expect(asset).rejects.toThrowError()\n\t})\n})\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test, vi, afterEach } from 'vitest';
|
|
2
|
-
import { readFile, writeFile, stat, unlink, mkdir, readdir } from 'fs/promises';
|
|
3
|
-
import { fdir } from 'fdir';
|
|
4
|
-
import { createReadStream, createWriteStream, existsSync, } from 'fs';
|
|
5
|
-
import StorageFileSystem from './fs.js';
|
|
6
|
-
import { resolve } from 'path';
|
|
7
|
-
import { PassThrough } from 'stream';
|
|
8
|
-
vi.mock('fs');
|
|
9
|
-
// vi.mock('path') // Use real path module
|
|
10
|
-
vi.mock('fs/promises');
|
|
11
|
-
vi.mock('fdir', () => {
|
|
12
|
-
const fdir = vi.fn();
|
|
13
|
-
fdir.prototype = {
|
|
14
|
-
withRelativePaths: vi.fn().mockReturnThis(),
|
|
15
|
-
withDirs: vi.fn().mockReturnThis(),
|
|
16
|
-
crawl: vi.fn().mockImplementation(() => ({
|
|
17
|
-
sync: vi.fn().mockReturnValue([]),
|
|
18
|
-
})),
|
|
19
|
-
};
|
|
20
|
-
return { fdir };
|
|
21
|
-
});
|
|
22
|
-
const spies = {};
|
|
23
|
-
const mocks = {
|
|
24
|
-
stat: vi.mocked(stat),
|
|
25
|
-
readFile: vi.mocked(readFile),
|
|
26
|
-
writeFile: vi.mocked(writeFile),
|
|
27
|
-
unlink: vi.mocked(unlink),
|
|
28
|
-
mkdir: vi.mocked(mkdir),
|
|
29
|
-
fdir: vi.mocked(fdir),
|
|
30
|
-
readdir: vi.mocked(readdir),
|
|
31
|
-
createReadStream: vi.mocked(createReadStream),
|
|
32
|
-
createWriteStream: vi.mocked(createWriteStream),
|
|
33
|
-
existsSync: vi.mocked(existsSync),
|
|
34
|
-
// relative: vi.mocked(relative),
|
|
35
|
-
// resolve: vi.mocked(resolve),
|
|
36
|
-
};
|
|
37
|
-
describe('lib/storage/fs.ts', () => {
|
|
38
|
-
afterEach(() => {
|
|
39
|
-
Object.values(mocks).forEach((mock) => {
|
|
40
|
-
mock.mockReset();
|
|
41
|
-
});
|
|
42
|
-
Object.keys(spies).forEach((key) => {
|
|
43
|
-
spies[key].mockRestore();
|
|
44
|
-
delete spies[key];
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
test('constructor', async () => {
|
|
48
|
-
const root = '/root/dir';
|
|
49
|
-
mocks.existsSync.mockReturnValueOnce(true);
|
|
50
|
-
const storage = new StorageFileSystem(root);
|
|
51
|
-
expect(storage.root).toEqual(root);
|
|
52
|
-
});
|
|
53
|
-
test('constructor throws', async () => {
|
|
54
|
-
const root = '/root/dir';
|
|
55
|
-
mocks.existsSync.mockReturnValueOnce(false);
|
|
56
|
-
expect(() => new StorageFileSystem(root)).toThrowError();
|
|
57
|
-
});
|
|
58
|
-
test('parseArgPath', async () => {
|
|
59
|
-
const root = '/root/dir';
|
|
60
|
-
const expectedRelative = 'relative/path';
|
|
61
|
-
const inputPath = resolve(root, expectedRelative);
|
|
62
|
-
mocks.existsSync.mockReturnValueOnce(true);
|
|
63
|
-
// mocks.relative.mockReturnValueOnce(relative)
|
|
64
|
-
const storage = new StorageFileSystem(root);
|
|
65
|
-
const parsedPath = storage.parseArgPath(inputPath);
|
|
66
|
-
expect(parsedPath).toEqual(expectedRelative);
|
|
67
|
-
});
|
|
68
|
-
test('readFile as utf8', async () => {
|
|
69
|
-
const root = '/root/dir';
|
|
70
|
-
const expectedPath = resolve(root, './path');
|
|
71
|
-
const contents = 'file contents';
|
|
72
|
-
mocks.existsSync.mockReturnValueOnce(true);
|
|
73
|
-
mocks.readFile.mockResolvedValueOnce(contents);
|
|
74
|
-
const storage = new StorageFileSystem(root);
|
|
75
|
-
const file = await storage.readFile('./path', 'text');
|
|
76
|
-
expect(mocks.readFile).toHaveBeenCalledWith(expectedPath, 'utf-8');
|
|
77
|
-
expect(file).toEqual(contents);
|
|
78
|
-
});
|
|
79
|
-
test('readFile as binary', async () => {
|
|
80
|
-
const root = '/root/dir';
|
|
81
|
-
const expectedPath = resolve(root, './path');
|
|
82
|
-
const contents = 'file contents';
|
|
83
|
-
mocks.existsSync.mockReturnValueOnce(true);
|
|
84
|
-
mocks.readFile.mockResolvedValueOnce(contents);
|
|
85
|
-
const storage = new StorageFileSystem(root);
|
|
86
|
-
const file = await storage.readFile('./path');
|
|
87
|
-
expect(mocks.readFile).toHaveBeenCalledWith(expectedPath);
|
|
88
|
-
expect(file).toEqual(contents);
|
|
89
|
-
});
|
|
90
|
-
test('writeFile buffer', async () => {
|
|
91
|
-
const root = '/root/dir';
|
|
92
|
-
const expectedPath = resolve(root, './path');
|
|
93
|
-
const buffer = Buffer.from('file contents');
|
|
94
|
-
mocks.existsSync.mockReturnValueOnce(true);
|
|
95
|
-
mocks.writeFile.mockResolvedValueOnce(undefined);
|
|
96
|
-
const storage = new StorageFileSystem(root);
|
|
97
|
-
await storage.writeFile('./path', buffer);
|
|
98
|
-
expect(mocks.writeFile).toHaveBeenCalledWith(expectedPath, buffer, 'binary');
|
|
99
|
-
});
|
|
100
|
-
test('writeFile text', async () => {
|
|
101
|
-
const root = '/root/dir';
|
|
102
|
-
const expectedPath = resolve(root, './path');
|
|
103
|
-
const contents = 'file contents';
|
|
104
|
-
mocks.existsSync.mockReturnValueOnce(true);
|
|
105
|
-
mocks.writeFile.mockResolvedValueOnce(undefined);
|
|
106
|
-
const storage = new StorageFileSystem(root);
|
|
107
|
-
await storage.writeFile('./path', contents);
|
|
108
|
-
expect(mocks.writeFile).toHaveBeenCalledWith(expectedPath, contents, 'utf8');
|
|
109
|
-
});
|
|
110
|
-
test('getFilesIn recursive', async () => {
|
|
111
|
-
const root = '/root/dir';
|
|
112
|
-
const dirs = ['dir1', 'dir2'];
|
|
113
|
-
mocks.existsSync.mockReturnValueOnce(true);
|
|
114
|
-
spies.fdirSync = vi.spyOn(fdir.prototype, 'crawl').mockImplementation(() => ({
|
|
115
|
-
withPromise: vi.fn().mockResolvedValueOnce(dirs),
|
|
116
|
-
withRelativePaths: vi.fn().mockReturnThis(),
|
|
117
|
-
}));
|
|
118
|
-
const storage = new StorageFileSystem(root);
|
|
119
|
-
const results = await storage.getFilesIn('./path', { deep: true });
|
|
120
|
-
expect(spies.fdirSync).toHaveBeenCalled();
|
|
121
|
-
expect(results).toEqual(dirs);
|
|
122
|
-
});
|
|
123
|
-
test('getFilesIn', async () => {
|
|
124
|
-
const root = '/root/dir';
|
|
125
|
-
const dirs = [
|
|
126
|
-
{ name: 'dir1', isFile: () => false },
|
|
127
|
-
{ name: 'file1', isFile: () => true },
|
|
128
|
-
];
|
|
129
|
-
mocks.existsSync.mockReturnValueOnce(true);
|
|
130
|
-
mocks.readdir.mockResolvedValueOnce(dirs);
|
|
131
|
-
const storage = new StorageFileSystem(root);
|
|
132
|
-
const results = await storage.getFilesIn('./path');
|
|
133
|
-
expect(mocks.readdir).toHaveBeenCalled();
|
|
134
|
-
expect(results).toEqual(['dir1/', 'file1']);
|
|
135
|
-
});
|
|
136
|
-
test('exists', async () => {
|
|
137
|
-
const root = '/root/dir';
|
|
138
|
-
mocks.existsSync.mockReturnValueOnce(true);
|
|
139
|
-
mocks.stat.mockResolvedValueOnce({});
|
|
140
|
-
const storage = new StorageFileSystem(root);
|
|
141
|
-
const result = await storage.exists('./path');
|
|
142
|
-
expect(result).toEqual(true);
|
|
143
|
-
});
|
|
144
|
-
test('delete', async () => {
|
|
145
|
-
const root = '/root/dir';
|
|
146
|
-
mocks.existsSync.mockReturnValueOnce(true);
|
|
147
|
-
mocks.unlink.mockResolvedValueOnce(undefined);
|
|
148
|
-
const storage = new StorageFileSystem(root);
|
|
149
|
-
await storage.delete('./path');
|
|
150
|
-
expect(mocks.unlink).toHaveBeenCalledOnce();
|
|
151
|
-
});
|
|
152
|
-
test('stat', async () => {
|
|
153
|
-
const root = '/root/dir';
|
|
154
|
-
const stats = { size: 123, isFile: () => true, mtime: new Date() };
|
|
155
|
-
mocks.existsSync.mockReturnValueOnce(true);
|
|
156
|
-
mocks.stat.mockResolvedValueOnce(stats);
|
|
157
|
-
const storage = new StorageFileSystem(root);
|
|
158
|
-
const results = await storage.stat('./path');
|
|
159
|
-
expect(results).toEqual({
|
|
160
|
-
size: stats.size,
|
|
161
|
-
type: 'file',
|
|
162
|
-
last_modified: stats.mtime,
|
|
163
|
-
});
|
|
164
|
-
});
|
|
165
|
-
test('stat on dir', async () => {
|
|
166
|
-
const root = '/root/dir';
|
|
167
|
-
const stats = { size: 123, isFile: () => false, mtime: new Date() };
|
|
168
|
-
mocks.existsSync.mockReturnValueOnce(true);
|
|
169
|
-
mocks.stat.mockResolvedValueOnce(stats);
|
|
170
|
-
const storage = new StorageFileSystem(root);
|
|
171
|
-
const results = await storage.stat('./path');
|
|
172
|
-
expect(results).toEqual({
|
|
173
|
-
size: stats.size,
|
|
174
|
-
type: 'directory',
|
|
175
|
-
last_modified: stats.mtime,
|
|
176
|
-
});
|
|
177
|
-
});
|
|
178
|
-
test('createReadStream', async () => {
|
|
179
|
-
const root = '/root/dir';
|
|
180
|
-
const stream = new PassThrough();
|
|
181
|
-
mocks.existsSync.mockReturnValueOnce(true);
|
|
182
|
-
mocks.createReadStream.mockReturnValueOnce(stream);
|
|
183
|
-
const storage = new StorageFileSystem(root);
|
|
184
|
-
const results = storage.createReadStream('./path');
|
|
185
|
-
expect(results).toEqual(stream);
|
|
186
|
-
});
|
|
187
|
-
test('createWritestream', async () => {
|
|
188
|
-
const root = '/root/dir';
|
|
189
|
-
const stream = new PassThrough();
|
|
190
|
-
mocks.existsSync.mockReturnValueOnce(true);
|
|
191
|
-
mocks.createWriteStream.mockReturnValueOnce(stream);
|
|
192
|
-
const storage = new StorageFileSystem(root);
|
|
193
|
-
const results = storage.createWriteStream('./path');
|
|
194
|
-
expect(results).toEqual(stream);
|
|
195
|
-
});
|
|
196
|
-
test('makeDirectory', async () => {
|
|
197
|
-
const root = '/root/dir';
|
|
198
|
-
const expectedPath = resolve(root, './path');
|
|
199
|
-
mocks.existsSync.mockReturnValueOnce(true);
|
|
200
|
-
mocks.mkdir.mockResolvedValueOnce('');
|
|
201
|
-
const storage = new StorageFileSystem(root);
|
|
202
|
-
await storage.makeDirectory('./path');
|
|
203
|
-
expect(mocks.mkdir).toHaveBeenCalledWith(expectedPath, { recursive: true });
|
|
204
|
-
});
|
|
205
|
-
test('prevents traversal', async () => {
|
|
206
|
-
const root = '/root/dir';
|
|
207
|
-
mocks.existsSync.mockReturnValueOnce(true);
|
|
208
|
-
const storage = new StorageFileSystem(root);
|
|
209
|
-
await expect(() => storage.readFile('../outside', 'text')).rejects.toThrow('Path traversal attempt detected');
|
|
210
|
-
});
|
|
211
|
-
});
|
|
212
|
-
//# sourceMappingURL=fs.test.js.map
|