@uploadista/flow-images-nodes 0.0.13-beta.5 → 0.0.13
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/dist/index.d.cts +460 -108
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +460 -108
- package/dist/index.d.mts.map +1 -1
- package/package.json +12 -7
- package/tests/image-nodes.test.ts +680 -0
- package/vitest.config.ts +39 -0
|
@@ -0,0 +1,680 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "@effect/vitest";
|
|
2
|
+
import { UploadistaError } from "@uploadista/core/errors";
|
|
3
|
+
import {
|
|
4
|
+
TestImageAiPlugin,
|
|
5
|
+
TestImagePlugin,
|
|
6
|
+
TestUploadServer,
|
|
7
|
+
} from "@uploadista/core/testing";
|
|
8
|
+
import type { UploadFile } from "@uploadista/core/types";
|
|
9
|
+
import { Effect, Layer } from "effect";
|
|
10
|
+
import {
|
|
11
|
+
createDescribeImageNode,
|
|
12
|
+
createOptimizeNode,
|
|
13
|
+
createRemoveBackgroundNode,
|
|
14
|
+
createResizeNode,
|
|
15
|
+
createTransformImageNode,
|
|
16
|
+
} from "../src/index";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Test utilities for creating sample data
|
|
20
|
+
*/
|
|
21
|
+
const createTestUploadFile = (overrides?: Partial<UploadFile>): UploadFile => ({
|
|
22
|
+
id: "test-file-1",
|
|
23
|
+
offset: 0,
|
|
24
|
+
size: 1024,
|
|
25
|
+
storage: {
|
|
26
|
+
id: "test-storage",
|
|
27
|
+
type: "memory",
|
|
28
|
+
},
|
|
29
|
+
metadata: {
|
|
30
|
+
mimeType: "image/jpeg",
|
|
31
|
+
originalName: "test-image.jpg",
|
|
32
|
+
fileName: "test-image.jpg",
|
|
33
|
+
extension: "jpg",
|
|
34
|
+
width: 800,
|
|
35
|
+
height: 600,
|
|
36
|
+
},
|
|
37
|
+
url: "https://example.com/test-image.jpg",
|
|
38
|
+
creationDate: new Date().toISOString(),
|
|
39
|
+
...overrides,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Test layer combining all mocks
|
|
44
|
+
*/
|
|
45
|
+
const TestLayer = Layer.mergeAll(
|
|
46
|
+
TestImagePlugin,
|
|
47
|
+
TestImageAiPlugin,
|
|
48
|
+
TestUploadServer,
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
// Mock fetch for URL availability tests
|
|
52
|
+
global.fetch = vi.fn(() =>
|
|
53
|
+
Promise.resolve({
|
|
54
|
+
ok: true,
|
|
55
|
+
status: 200,
|
|
56
|
+
} as Response),
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
describe("Image Nodes", () => {
|
|
60
|
+
describe("OptimizeNode", () => {
|
|
61
|
+
it.effect("should create optimize node with correct properties", () =>
|
|
62
|
+
Effect.gen(function* () {
|
|
63
|
+
const node = yield* createOptimizeNode("optimize-1", {
|
|
64
|
+
quality: 80,
|
|
65
|
+
format: "webp",
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
expect(node.id).toBe("optimize-1");
|
|
69
|
+
expect(node.name).toBe("Optimize");
|
|
70
|
+
expect(node.description).toBe("Optimizes an image for web delivery");
|
|
71
|
+
}).pipe(Effect.provide(TestLayer)),
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
it.effect("should optimize image to webp format", () =>
|
|
75
|
+
Effect.gen(function* () {
|
|
76
|
+
const node = yield* createOptimizeNode("optimize-webp", {
|
|
77
|
+
quality: 85,
|
|
78
|
+
format: "webp",
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const testFile = createTestUploadFile();
|
|
82
|
+
|
|
83
|
+
const result = yield* node.run({
|
|
84
|
+
data: testFile,
|
|
85
|
+
jobId: "test-job",
|
|
86
|
+
flowId: "test-flow",
|
|
87
|
+
storageId: "test-storage",
|
|
88
|
+
clientId: "test-client",
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
expect(result.type).toBe("complete");
|
|
92
|
+
if (result.type === "complete") {
|
|
93
|
+
expect(result.data).toBeDefined();
|
|
94
|
+
expect(result.data.metadata?.mimeType).toBe("image/webp");
|
|
95
|
+
expect(result.data.metadata?.fileName).toContain(".webp");
|
|
96
|
+
}
|
|
97
|
+
}).pipe(Effect.provide(TestLayer)),
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
it.effect("should optimize image to jpeg format", () =>
|
|
101
|
+
Effect.gen(function* () {
|
|
102
|
+
const node = yield* createOptimizeNode("optimize-jpeg", {
|
|
103
|
+
quality: 90,
|
|
104
|
+
format: "jpeg",
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const testFile = createTestUploadFile();
|
|
108
|
+
|
|
109
|
+
const result = yield* node.run({
|
|
110
|
+
data: testFile,
|
|
111
|
+
jobId: "test-job",
|
|
112
|
+
flowId: "test-flow",
|
|
113
|
+
storageId: "test-storage",
|
|
114
|
+
clientId: "test-client",
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
expect(result.type).toBe("complete");
|
|
118
|
+
if (result.type === "complete") {
|
|
119
|
+
expect(result.data.metadata?.mimeType).toBe("image/jpeg");
|
|
120
|
+
expect(result.data.metadata?.fileName).toContain(".jpg");
|
|
121
|
+
}
|
|
122
|
+
}).pipe(Effect.provide(TestLayer)),
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
it.effect("should optimize image to png format", () =>
|
|
126
|
+
Effect.gen(function* () {
|
|
127
|
+
const node = yield* createOptimizeNode("optimize-png", {
|
|
128
|
+
quality: 100,
|
|
129
|
+
format: "png",
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const testFile = createTestUploadFile();
|
|
133
|
+
|
|
134
|
+
const result = yield* node.run({
|
|
135
|
+
data: testFile,
|
|
136
|
+
jobId: "test-job",
|
|
137
|
+
flowId: "test-flow",
|
|
138
|
+
storageId: "test-storage",
|
|
139
|
+
clientId: "test-client",
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
expect(result.type).toBe("complete");
|
|
143
|
+
if (result.type === "complete") {
|
|
144
|
+
expect(result.data.metadata?.mimeType).toBe("image/png");
|
|
145
|
+
expect(result.data.metadata?.fileName).toContain(".png");
|
|
146
|
+
}
|
|
147
|
+
}).pipe(Effect.provide(TestLayer)),
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
it.effect("should optimize image to avif format", () =>
|
|
151
|
+
Effect.gen(function* () {
|
|
152
|
+
const node = yield* createOptimizeNode("optimize-avif", {
|
|
153
|
+
quality: 75,
|
|
154
|
+
format: "avif",
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
const testFile = createTestUploadFile();
|
|
158
|
+
|
|
159
|
+
const result = yield* node.run({
|
|
160
|
+
data: testFile,
|
|
161
|
+
jobId: "test-job",
|
|
162
|
+
flowId: "test-flow",
|
|
163
|
+
storageId: "test-storage",
|
|
164
|
+
clientId: "test-client",
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
expect(result.type).toBe("complete");
|
|
168
|
+
if (result.type === "complete") {
|
|
169
|
+
expect(result.data.metadata?.mimeType).toBe("image/avif");
|
|
170
|
+
expect(result.data.metadata?.fileName).toContain(".avif");
|
|
171
|
+
}
|
|
172
|
+
}).pipe(Effect.provide(TestLayer)),
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
it.effect("should handle different quality levels", () =>
|
|
176
|
+
Effect.gen(function* () {
|
|
177
|
+
const highQualityNode = yield* createOptimizeNode("optimize-high", {
|
|
178
|
+
quality: 95,
|
|
179
|
+
format: "jpeg",
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
const lowQualityNode = yield* createOptimizeNode("optimize-low", {
|
|
183
|
+
quality: 50,
|
|
184
|
+
format: "jpeg",
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
const testFile = createTestUploadFile();
|
|
188
|
+
|
|
189
|
+
const highResult = yield* highQualityNode.run({
|
|
190
|
+
data: testFile,
|
|
191
|
+
jobId: "test-job",
|
|
192
|
+
flowId: "test-flow",
|
|
193
|
+
storageId: "test-storage",
|
|
194
|
+
clientId: "test-client",
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
const lowResult = yield* lowQualityNode.run({
|
|
198
|
+
data: testFile,
|
|
199
|
+
jobId: "test-job",
|
|
200
|
+
flowId: "test-flow",
|
|
201
|
+
storageId: "test-storage",
|
|
202
|
+
clientId: "test-client",
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
// Lower quality should result in smaller file
|
|
206
|
+
expect(highResult.type).toBe("complete");
|
|
207
|
+
expect(lowResult.type).toBe("complete");
|
|
208
|
+
if (
|
|
209
|
+
highResult.type === "complete" &&
|
|
210
|
+
lowResult.type === "complete" &&
|
|
211
|
+
highResult.data.size &&
|
|
212
|
+
lowResult.data.size
|
|
213
|
+
) {
|
|
214
|
+
expect(lowResult.data.size).toBeLessThan(highResult.data.size);
|
|
215
|
+
}
|
|
216
|
+
}).pipe(Effect.provide(TestLayer)),
|
|
217
|
+
);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
describe("ResizeNode", () => {
|
|
221
|
+
it.effect("should create resize node with correct properties", () =>
|
|
222
|
+
Effect.gen(function* () {
|
|
223
|
+
const node = yield* createResizeNode("resize-1", {
|
|
224
|
+
width: 800,
|
|
225
|
+
height: 600,
|
|
226
|
+
fit: "cover",
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
expect(node.id).toBe("resize-1");
|
|
230
|
+
expect(node.name).toBe("Resize");
|
|
231
|
+
expect(node.description).toBe(
|
|
232
|
+
"Resizes an image to the specified dimensions",
|
|
233
|
+
);
|
|
234
|
+
}).pipe(Effect.provide(TestLayer)),
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
it.effect("should resize image with both width and height", () =>
|
|
238
|
+
Effect.gen(function* () {
|
|
239
|
+
const node = yield* createResizeNode("resize-both", {
|
|
240
|
+
width: 400,
|
|
241
|
+
height: 300,
|
|
242
|
+
fit: "cover",
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
const testFile = createTestUploadFile();
|
|
246
|
+
|
|
247
|
+
const result = yield* node.run({
|
|
248
|
+
data: testFile,
|
|
249
|
+
jobId: "test-job",
|
|
250
|
+
flowId: "test-flow",
|
|
251
|
+
storageId: "test-storage",
|
|
252
|
+
clientId: "test-client",
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
expect(result.type).toBe("complete");
|
|
256
|
+
if (result.type === "complete") {
|
|
257
|
+
expect(result.data).toBeDefined();
|
|
258
|
+
expect(result.data.size).toBeGreaterThan(0);
|
|
259
|
+
}
|
|
260
|
+
}).pipe(Effect.provide(TestLayer)),
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
it.effect("should resize image with width only", () =>
|
|
264
|
+
Effect.gen(function* () {
|
|
265
|
+
const node = yield* createResizeNode("resize-width", {
|
|
266
|
+
width: 1000,
|
|
267
|
+
fit: "contain",
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
const testFile = createTestUploadFile();
|
|
271
|
+
|
|
272
|
+
const result = yield* node.run({
|
|
273
|
+
data: testFile,
|
|
274
|
+
jobId: "test-job",
|
|
275
|
+
flowId: "test-flow",
|
|
276
|
+
storageId: "test-storage",
|
|
277
|
+
clientId: "test-client",
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
expect(result.type).toBe("complete");
|
|
281
|
+
if (result.type === "complete") {
|
|
282
|
+
expect(result.data).toBeDefined();
|
|
283
|
+
}
|
|
284
|
+
}).pipe(Effect.provide(TestLayer)),
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
it.effect("should resize image with height only", () =>
|
|
288
|
+
Effect.gen(function* () {
|
|
289
|
+
const node = yield* createResizeNode("resize-height", {
|
|
290
|
+
height: 500,
|
|
291
|
+
fit: "cover",
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
const testFile = createTestUploadFile();
|
|
295
|
+
|
|
296
|
+
const result = yield* node.run({
|
|
297
|
+
data: testFile,
|
|
298
|
+
jobId: "test-job",
|
|
299
|
+
flowId: "test-flow",
|
|
300
|
+
storageId: "test-storage",
|
|
301
|
+
clientId: "test-client",
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
expect(result.type).toBe("complete");
|
|
305
|
+
if (result.type === "complete") {
|
|
306
|
+
expect(result.data).toBeDefined();
|
|
307
|
+
}
|
|
308
|
+
}).pipe(Effect.provide(TestLayer)),
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
it.effect("should handle different fit modes", () =>
|
|
312
|
+
Effect.gen(function* () {
|
|
313
|
+
const fitModes: Array<"cover" | "contain" | "fill"> = [
|
|
314
|
+
"cover",
|
|
315
|
+
"contain",
|
|
316
|
+
"fill",
|
|
317
|
+
];
|
|
318
|
+
|
|
319
|
+
for (const fit of fitModes) {
|
|
320
|
+
const node = yield* createResizeNode(`resize-${fit}`, {
|
|
321
|
+
width: 800,
|
|
322
|
+
height: 600,
|
|
323
|
+
fit,
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
const testFile = createTestUploadFile();
|
|
327
|
+
|
|
328
|
+
const result = yield* node.run({
|
|
329
|
+
data: testFile,
|
|
330
|
+
jobId: "test-job",
|
|
331
|
+
flowId: "test-flow",
|
|
332
|
+
storageId: "test-storage",
|
|
333
|
+
clientId: "test-client",
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
expect(result.type).toBe("complete");
|
|
337
|
+
if (result.type === "complete") {
|
|
338
|
+
expect(result.data).toBeDefined();
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}).pipe(Effect.provide(TestLayer)),
|
|
342
|
+
);
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
describe("TransformImageNode", () => {
|
|
346
|
+
it.effect("should create transform node with correct properties", () =>
|
|
347
|
+
Effect.gen(function* () {
|
|
348
|
+
const node = yield* createTransformImageNode("transform-1", {
|
|
349
|
+
transformations: [{ type: "grayscale" }],
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
expect(node.id).toBe("transform-1");
|
|
353
|
+
expect(node.name).toBe("Transform Image");
|
|
354
|
+
expect(node.description).toContain("1 transformation");
|
|
355
|
+
}).pipe(Effect.provide(TestLayer)),
|
|
356
|
+
);
|
|
357
|
+
|
|
358
|
+
it.effect("should apply single transformation", () =>
|
|
359
|
+
Effect.gen(function* () {
|
|
360
|
+
const node = yield* createTransformImageNode("transform-single", {
|
|
361
|
+
transformations: [{ type: "grayscale" }],
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
const testFile = createTestUploadFile();
|
|
365
|
+
|
|
366
|
+
const result = yield* node.run({
|
|
367
|
+
data: testFile,
|
|
368
|
+
jobId: "test-job",
|
|
369
|
+
flowId: "test-flow",
|
|
370
|
+
storageId: "test-storage",
|
|
371
|
+
clientId: "test-client",
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
expect(result.type).toBe("complete");
|
|
375
|
+
if (result.type === "complete") {
|
|
376
|
+
expect(result.data).toBeDefined();
|
|
377
|
+
}
|
|
378
|
+
}).pipe(Effect.provide(TestLayer)),
|
|
379
|
+
);
|
|
380
|
+
|
|
381
|
+
it.effect("should apply multiple transformations in sequence", () =>
|
|
382
|
+
Effect.gen(function* () {
|
|
383
|
+
const node = yield* createTransformImageNode("transform-multi", {
|
|
384
|
+
transformations: [
|
|
385
|
+
{ type: "blur", sigma: 5 },
|
|
386
|
+
{ type: "grayscale" },
|
|
387
|
+
{ type: "rotate", angle: 90 },
|
|
388
|
+
],
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
const testFile = createTestUploadFile();
|
|
392
|
+
|
|
393
|
+
const result = yield* node.run({
|
|
394
|
+
data: testFile,
|
|
395
|
+
jobId: "test-job",
|
|
396
|
+
flowId: "test-flow",
|
|
397
|
+
storageId: "test-storage",
|
|
398
|
+
clientId: "test-client",
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
expect(result.type).toBe("complete");
|
|
402
|
+
if (result.type === "complete") {
|
|
403
|
+
expect(result.data).toBeDefined();
|
|
404
|
+
}
|
|
405
|
+
expect(node.description).toContain("3 transformations");
|
|
406
|
+
}).pipe(Effect.provide(TestLayer)),
|
|
407
|
+
);
|
|
408
|
+
|
|
409
|
+
it.effect("should handle blur transformation", () =>
|
|
410
|
+
Effect.gen(function* () {
|
|
411
|
+
const node = yield* createTransformImageNode("transform-blur", {
|
|
412
|
+
transformations: [{ type: "blur", sigma: 3 }],
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
const testFile = createTestUploadFile();
|
|
416
|
+
|
|
417
|
+
const result = yield* node.run({
|
|
418
|
+
data: testFile,
|
|
419
|
+
jobId: "test-job",
|
|
420
|
+
flowId: "test-flow",
|
|
421
|
+
storageId: "test-storage",
|
|
422
|
+
clientId: "test-client",
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
expect(result.type).toBe("complete");
|
|
426
|
+
if (result.type === "complete") {
|
|
427
|
+
expect(result.data).toBeDefined();
|
|
428
|
+
}
|
|
429
|
+
}).pipe(Effect.provide(TestLayer)),
|
|
430
|
+
);
|
|
431
|
+
|
|
432
|
+
it.effect("should handle rotate transformation", () =>
|
|
433
|
+
Effect.gen(function* () {
|
|
434
|
+
const node = yield* createTransformImageNode("transform-rotate", {
|
|
435
|
+
transformations: [{ type: "rotate", angle: 180 }],
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
const testFile = createTestUploadFile();
|
|
439
|
+
|
|
440
|
+
const result = yield* node.run({
|
|
441
|
+
data: testFile,
|
|
442
|
+
jobId: "test-job",
|
|
443
|
+
flowId: "test-flow",
|
|
444
|
+
storageId: "test-storage",
|
|
445
|
+
clientId: "test-client",
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
expect(result.type).toBe("complete");
|
|
449
|
+
if (result.type === "complete") {
|
|
450
|
+
expect(result.data).toBeDefined();
|
|
451
|
+
}
|
|
452
|
+
}).pipe(Effect.provide(TestLayer)),
|
|
453
|
+
);
|
|
454
|
+
|
|
455
|
+
it.effect("should handle flip transformation", () =>
|
|
456
|
+
Effect.gen(function* () {
|
|
457
|
+
const node = yield* createTransformImageNode("transform-flip", {
|
|
458
|
+
transformations: [{ type: "flip", direction: "horizontal" }],
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
const testFile = createTestUploadFile();
|
|
462
|
+
|
|
463
|
+
const result = yield* node.run({
|
|
464
|
+
data: testFile,
|
|
465
|
+
jobId: "test-job",
|
|
466
|
+
flowId: "test-flow",
|
|
467
|
+
storageId: "test-storage",
|
|
468
|
+
clientId: "test-client",
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
expect(result.type).toBe("complete");
|
|
472
|
+
if (result.type === "complete") {
|
|
473
|
+
expect(result.data).toBeDefined();
|
|
474
|
+
}
|
|
475
|
+
}).pipe(Effect.provide(TestLayer)),
|
|
476
|
+
);
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
describe("DescribeImageNode", () => {
|
|
480
|
+
it.effect("should create describe image node with correct properties", () =>
|
|
481
|
+
Effect.gen(function* () {
|
|
482
|
+
const node = yield* createDescribeImageNode("describe-1");
|
|
483
|
+
|
|
484
|
+
expect(node.id).toBe("describe-1");
|
|
485
|
+
expect(node.name).toBe("Describe Image");
|
|
486
|
+
expect(node.description).toBe("Describes the image using AI");
|
|
487
|
+
}).pipe(Effect.provide(TestLayer)),
|
|
488
|
+
);
|
|
489
|
+
|
|
490
|
+
it.effect("should describe image and add description to metadata", () =>
|
|
491
|
+
Effect.gen(function* () {
|
|
492
|
+
const node = yield* createDescribeImageNode("describe-test");
|
|
493
|
+
|
|
494
|
+
const testFile = createTestUploadFile();
|
|
495
|
+
|
|
496
|
+
const result = yield* node.run({
|
|
497
|
+
data: testFile,
|
|
498
|
+
jobId: "test-job",
|
|
499
|
+
flowId: "test-flow",
|
|
500
|
+
storageId: "test-storage",
|
|
501
|
+
clientId: "test-client",
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
expect(result.type).toBe("complete");
|
|
505
|
+
if (result.type === "complete") {
|
|
506
|
+
expect(result.data).toBeDefined();
|
|
507
|
+
expect(result.data.metadata?.description).toBeDefined();
|
|
508
|
+
expect(typeof result.data.metadata?.description).toBe("string");
|
|
509
|
+
expect(result.data.metadata?.description).toContain("test image");
|
|
510
|
+
}
|
|
511
|
+
}).pipe(Effect.provide(TestLayer)),
|
|
512
|
+
);
|
|
513
|
+
|
|
514
|
+
it.effect("should fail when URL is missing", () =>
|
|
515
|
+
Effect.gen(function* () {
|
|
516
|
+
const node = yield* createDescribeImageNode("describe-no-url");
|
|
517
|
+
|
|
518
|
+
const testFile = createTestUploadFile({ url: undefined });
|
|
519
|
+
|
|
520
|
+
const result = yield* Effect.either(
|
|
521
|
+
node.run({
|
|
522
|
+
data: testFile,
|
|
523
|
+
jobId: "test-job",
|
|
524
|
+
flowId: "test-flow",
|
|
525
|
+
storageId: "test-storage",
|
|
526
|
+
clientId: "test-client",
|
|
527
|
+
}),
|
|
528
|
+
);
|
|
529
|
+
|
|
530
|
+
expect(result._tag).toBe("Left");
|
|
531
|
+
if (result._tag === "Left") {
|
|
532
|
+
expect(result.left).toBeInstanceOf(UploadistaError);
|
|
533
|
+
expect(result.left.code).toBe("FLOW_NODE_ERROR");
|
|
534
|
+
}
|
|
535
|
+
}).pipe(Effect.provide(TestLayer)),
|
|
536
|
+
);
|
|
537
|
+
|
|
538
|
+
it.effect("should pass credential ID to AI plugin", () =>
|
|
539
|
+
Effect.gen(function* () {
|
|
540
|
+
const credentialId = "test-cred-123";
|
|
541
|
+
const node = yield* createDescribeImageNode("describe-cred", {
|
|
542
|
+
credentialId,
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
const testFile = createTestUploadFile();
|
|
546
|
+
|
|
547
|
+
const result = yield* node.run({
|
|
548
|
+
data: testFile,
|
|
549
|
+
jobId: "test-job",
|
|
550
|
+
flowId: "test-flow",
|
|
551
|
+
storageId: "test-storage",
|
|
552
|
+
clientId: "test-client",
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
expect(result.type).toBe("complete");
|
|
556
|
+
if (result.type === "complete") {
|
|
557
|
+
expect(result.data).toBeDefined();
|
|
558
|
+
expect(result.data.metadata?.description).toBeDefined();
|
|
559
|
+
}
|
|
560
|
+
}).pipe(Effect.provide(TestLayer)),
|
|
561
|
+
);
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
describe("RemoveBackgroundNode", () => {
|
|
565
|
+
it.effect(
|
|
566
|
+
"should create remove background node with correct properties",
|
|
567
|
+
() =>
|
|
568
|
+
Effect.gen(function* () {
|
|
569
|
+
const node = yield* createRemoveBackgroundNode("remove-bg-1");
|
|
570
|
+
|
|
571
|
+
expect(node.id).toBe("remove-bg-1");
|
|
572
|
+
expect(node.name).toBe("Remove Background");
|
|
573
|
+
expect(node.description).toBe("Removes the background from an image");
|
|
574
|
+
}).pipe(Effect.provide(TestLayer)),
|
|
575
|
+
);
|
|
576
|
+
|
|
577
|
+
it.effect("should remove background and create new file", () =>
|
|
578
|
+
Effect.gen(function* () {
|
|
579
|
+
const node = yield* createRemoveBackgroundNode("remove-bg-test");
|
|
580
|
+
|
|
581
|
+
const testFile = createTestUploadFile();
|
|
582
|
+
|
|
583
|
+
const result = yield* node.run({
|
|
584
|
+
data: testFile,
|
|
585
|
+
jobId: "test-job",
|
|
586
|
+
flowId: "test-flow",
|
|
587
|
+
storageId: "test-storage",
|
|
588
|
+
clientId: "test-client",
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
expect(result.type).toBe("complete");
|
|
592
|
+
if (result.type === "complete") {
|
|
593
|
+
expect(result.data).toBeDefined();
|
|
594
|
+
// Should be a new file ID
|
|
595
|
+
expect(result.data.id).not.toBe(testFile.id);
|
|
596
|
+
expect(result.data.url).toBeDefined();
|
|
597
|
+
}
|
|
598
|
+
}).pipe(Effect.provide(TestLayer)),
|
|
599
|
+
);
|
|
600
|
+
|
|
601
|
+
it.effect("should fail when URL is missing", () =>
|
|
602
|
+
Effect.gen(function* () {
|
|
603
|
+
const node = yield* createRemoveBackgroundNode("remove-bg-no-url");
|
|
604
|
+
|
|
605
|
+
const testFile = createTestUploadFile({ url: undefined });
|
|
606
|
+
|
|
607
|
+
const result = yield* Effect.either(
|
|
608
|
+
node.run({
|
|
609
|
+
data: testFile,
|
|
610
|
+
jobId: "test-job",
|
|
611
|
+
flowId: "test-flow",
|
|
612
|
+
storageId: "test-storage",
|
|
613
|
+
clientId: "test-client",
|
|
614
|
+
}),
|
|
615
|
+
);
|
|
616
|
+
|
|
617
|
+
expect(result._tag).toBe("Left");
|
|
618
|
+
if (result._tag === "Left") {
|
|
619
|
+
expect(result.left).toBeInstanceOf(UploadistaError);
|
|
620
|
+
expect(result.left.code).toBe("FLOW_NODE_ERROR");
|
|
621
|
+
}
|
|
622
|
+
}).pipe(Effect.provide(TestLayer)),
|
|
623
|
+
);
|
|
624
|
+
|
|
625
|
+
it.effect("should pass credential ID to AI plugin", () =>
|
|
626
|
+
Effect.gen(function* () {
|
|
627
|
+
const credentialId = "test-cred-456";
|
|
628
|
+
const node = yield* createRemoveBackgroundNode("remove-bg-cred", {
|
|
629
|
+
credentialId,
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
const testFile = createTestUploadFile();
|
|
633
|
+
|
|
634
|
+
const result = yield* node.run({
|
|
635
|
+
data: testFile,
|
|
636
|
+
jobId: "test-job",
|
|
637
|
+
flowId: "test-flow",
|
|
638
|
+
storageId: "test-storage",
|
|
639
|
+
clientId: "test-client",
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
expect(result.type).toBe("complete");
|
|
643
|
+
if (result.type === "complete") {
|
|
644
|
+
expect(result.data).toBeDefined();
|
|
645
|
+
}
|
|
646
|
+
}).pipe(Effect.provide(TestLayer)),
|
|
647
|
+
);
|
|
648
|
+
|
|
649
|
+
it.effect("should preserve image metadata", () =>
|
|
650
|
+
Effect.gen(function* () {
|
|
651
|
+
const node = yield* createRemoveBackgroundNode("remove-bg-metadata");
|
|
652
|
+
|
|
653
|
+
const testFile = createTestUploadFile({
|
|
654
|
+
metadata: {
|
|
655
|
+
mimeType: "image/png",
|
|
656
|
+
originalName: "photo.png",
|
|
657
|
+
fileName: "photo.png",
|
|
658
|
+
extension: "png",
|
|
659
|
+
width: 1920,
|
|
660
|
+
height: 1080,
|
|
661
|
+
},
|
|
662
|
+
});
|
|
663
|
+
|
|
664
|
+
const result = yield* node.run({
|
|
665
|
+
data: testFile,
|
|
666
|
+
jobId: "test-job",
|
|
667
|
+
flowId: "test-flow",
|
|
668
|
+
storageId: "test-storage",
|
|
669
|
+
clientId: "test-client",
|
|
670
|
+
});
|
|
671
|
+
|
|
672
|
+
expect(result.type).toBe("complete");
|
|
673
|
+
if (result.type === "complete") {
|
|
674
|
+
expect(result.data.metadata?.mimeType).toBe("image/png");
|
|
675
|
+
expect(result.data.metadata?.extension).toBe("png");
|
|
676
|
+
}
|
|
677
|
+
}).pipe(Effect.provide(TestLayer)),
|
|
678
|
+
);
|
|
679
|
+
});
|
|
680
|
+
});
|