File size: 11,279 Bytes
0ad74ed
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
import { describe, it, expect, vi, afterEach } from "vitest";
import {
	update_object,
	walk_and_store_blobs,
	skip_queue,
	post_message,
	handle_file,
	handle_payload
} from "../helpers/data";
import { config_response, endpoint_info } from "./test_data";
import { BlobRef, Command } from "../types";
import { FileData } from "../upload";

const IS_NODE = process.env.TEST_MODE === "node";

describe("walk_and_store_blobs", () => {
	it("should convert a Buffer to a Blob", async () => {
		const buffer = Buffer.from("test data");
		const parts = await walk_and_store_blobs(buffer, "text");

		expect(parts).toHaveLength(1);
		expect(parts[0].blob).toBeInstanceOf(Blob);
	});

	it("should return a Blob when passed a Blob", async () => {
		const blob = new Blob(["test data"]);
		const parts = await walk_and_store_blobs(
			blob,
			undefined,
			[],
			true,
			endpoint_info
		);

		expect(parts[0].blob).toBeInstanceOf(Blob);
	});

	it("should handle arrays", async () => {
		const image = new Blob([]);
		const parts = await walk_and_store_blobs([image]);

		expect(parts).toHaveLength(1);
		expect(parts[0].blob).toBeInstanceOf(Blob);
		expect(parts[0].path).toEqual(["0"]);
	});

	it("should handle deep structures", async () => {
		const image = new Blob([]);
		const parts = await walk_and_store_blobs({ a: { b: { data: { image } } } });

		expect(parts).toHaveLength(1);
		expect(parts[0].blob).toBeInstanceOf(Blob);
		expect(parts[0].path).toEqual(["a", "b", "data", "image"]);
	});

	it("should handle deep structures with arrays", async () => {
		const image = new Blob([]);
		const parts = await walk_and_store_blobs({
			a: [
				{
					b: [
						{
							data: [
								{
									image
								}
							]
						}
					]
				}
			]
		});

		expect(parts[0].blob).toBeInstanceOf(Blob);
	});

	it("should handle deep structures with arrays (with equality check)", async () => {
		const image = new Blob([]);

		const obj = {
			a: [
				{
					b: [
						{
							data: [[image], image, [image, [image]]]
						}
					]
				}
			]
		};
		const parts = await walk_and_store_blobs(obj);

		async function map_path(obj: Record<string, any>, parts: BlobRef[]) {
			const { path, blob } = parts[parts.length - 1];
			let ref = obj;
			path.forEach((p) => (ref = ref[p]));

			// since ref is a Blob and blob is a Blob, we deep equal check the two buffers instead
			if (ref instanceof Blob && blob instanceof Blob) {
				const refBuffer = Buffer.from(await ref.arrayBuffer());
				const blobBuffer = Buffer.from(await blob.arrayBuffer());
				return refBuffer.equals(blobBuffer);
			}

			return ref === blob;
		}

		expect(parts[0].blob).toBeInstanceOf(Blob);
		expect(map_path(obj, parts)).toBeTruthy();
	});

	it("should handle buffer instances and return a BlobRef", async () => {
		const buffer = Buffer.from("test");
		const parts = await walk_and_store_blobs(buffer, undefined, ["blob"]);

		expect(parts).toHaveLength(1);
		expect(parts[0].blob).toBeInstanceOf(Blob);
		expect(parts[0].path).toEqual(["blob"]);
	});

	it("should handle buffer instances with a path and return a BlobRef with the path", async () => {
		const buffer = Buffer.from("test data");
		const parts = await walk_and_store_blobs(buffer);

		expect(parts).toHaveLength(1);
		expect(parts[0].path).toEqual([]);
		expect(parts[0].blob).toBeInstanceOf(Blob);
	});

	it("should convert an object with deep structures to BlobRefs", async () => {
		const param = {
			a: {
				b: {
					data: {
						image: Buffer.from("test image")
					}
				}
			}
		};
		const parts = await walk_and_store_blobs(param);

		expect(parts).toHaveLength(1);
		expect(parts[0].path).toEqual(["a", "b", "data", "image"]);
		expect(parts[0].blob).toBeInstanceOf(Blob);
	});
});
describe("update_object", () => {
	it("should update the value of a nested property", () => {
		const obj = {
			a: {
				b: {
					c: "old value"
				}
			}
		};

		const stack = ["a", "b", "c"];
		const new_val = "new value";

		update_object(obj, new_val, stack);

		expect(obj.a.b.c).toBe(new_val);
	});

	it("should throw an error for invalid key type", () => {
		const obj = {
			a: {
				b: {
					c: "value"
				}
			}
		};

		const stack = ["a", "b", true];
		const newValue = "new value";

		expect(() => {
			// @ts-ignore
			update_object(obj, newValue, stack);
		}).toThrowError("Invalid key type");
	});
});

describe("skip_queue", () => {
	const id = 0;
	const config = config_response;

	it("should not skip queue when global and dependency queue is enabled", () => {
		config.enable_queue = true;
		config.dependencies.find((dep) => dep.id === id)!.queue = true;

		const result = skip_queue(id, config_response);

		expect(result).toBe(false);
	});

	it("should not skip queue when global queue is disabled and dependency queue is enabled", () => {
		config.enable_queue = false;
		config.dependencies.find((dep) => dep.id === id)!.queue = true;

		const result = skip_queue(id, config_response);

		expect(result).toBe(false);
	});

	it("should should skip queue when global queue and dependency queue is disabled", () => {
		config.enable_queue = false;
		config.dependencies.find((dep) => dep.id === id)!.queue = false;

		const result = skip_queue(id, config_response);

		expect(result).toBe(true);
	});

	it("should should skip queue when global queue is enabled and dependency queue is disabled", () => {
		config.enable_queue = true;
		config.dependencies.find((dep) => dep.id === id)!.queue = false;

		const result = skip_queue(id, config_response);

		expect(result).toBe(true);
	});
});

describe("post_message", () => {
	afterEach(() => {
		vi.restoreAllMocks();
	});

	it("should send a message to the parent window and resolve with received data", async () => {
		const test_data = { key: "value" };
		const test_origin = "https://huggingface.co";

		const post_message_mock = vi.fn();

		global.window = {
			// @ts-ignore
			parent: {
				postMessage: post_message_mock
			}
		};

		const message_channel_mock = {
			port1: {
				onmessage: (handler) => {
					onmessage = handler;
				},
				close: vi.fn()
			},
			port2: {}
		};

		vi.stubGlobal("MessageChannel", function () {
			this.port1 = message_channel_mock.port1;
			this.port2 = message_channel_mock.port2;
			return this;
		});

		const promise = post_message(test_data, test_origin);

		if (message_channel_mock.port1.onmessage) {
			message_channel_mock.port1.onmessage({ data: test_data });
		}

		await expect(promise).resolves.toEqual(test_data);
		expect(post_message_mock).toHaveBeenCalledWith(test_data, test_origin, [
			message_channel_mock.port2
		]);
	});
});

describe("handle_file", () => {
	it("should handle a Blob object and return the blob", () => {
		const blob = new Blob(["test data"], { type: "image/png" });
		const result = handle_file(blob) as FileData;

		expect(result).toBe(blob);
	});

	it("should handle a Buffer object and return it as a blob", () => {
		const buffer = Buffer.from("test data");
		const result = handle_file(buffer) as FileData;
		expect(result).toBeInstanceOf(Blob);
	});
	it("should handle a local file path and return a Command object", () => {
		const file_path = "./owl.png";
		const result = handle_file(file_path) as Command;
		expect(result).toBeInstanceOf(Command);
		expect(result).toEqual({
			type: "command",
			command: "upload_file",
			meta: { path: "./owl.png", name: "./owl.png", orig_path: "./owl.png" },
			fileData: undefined
		});
	});

	it("should handle a File object and return it as FileData", () => {
		if (IS_NODE) {
			return;
		}
		const file = new File(["test image"], "test.png", { type: "image/png" });
		const result = handle_file(file) as FileData;
		expect(result).toBeInstanceOf(Blob);
	});

	it("should throw an error for invalid input", () => {
		const invalid_input = 123;

		expect(() => {
			// @ts-ignore
			handle_file(invalid_input);
		}).toThrowError(
			"Invalid input: must be a URL, File, Blob, or Buffer object."
		);
	});
});

describe("handle_payload", () => {
	it("should return an input payload with null in place of `state` when with_null_state is true", () => {
		const resolved_payload = [2];
		const dependency = {
			inputs: [1, 2]
		};
		const components = [
			{ id: 1, type: "number" },
			{ id: 2, type: "state" }
		];
		const with_null_state = true;
		const result = handle_payload(
			resolved_payload,
			// @ts-ignore
			dependency,
			components,
			"input",
			with_null_state
		);
		expect(result).toEqual([2, null]);
	});
	it("should return an input payload with null in place of two `state` components when with_null_state is true", () => {
		const resolved_payload = ["hello", "goodbye"];
		const dependency = {
			inputs: [1, 2, 3, 4]
		};
		const components = [
			{ id: 1, type: "textbox" },
			{ id: 2, type: "state" },
			{ id: 3, type: "textbox" },
			{ id: 4, type: "state" }
		];
		const with_null_state = true;
		const result = handle_payload(
			resolved_payload,
			// @ts-ignore
			dependency,
			components,
			"input",
			with_null_state
		);
		expect(result).toEqual(["hello", null, "goodbye", null]);
	});

	it("should return an output payload without the state component value when with_null_state is false", () => {
		const resolved_payload = ["hello", null];
		const dependency = {
			outputs: [2, 3]
		};
		const components = [
			{ id: 2, type: "textbox" },
			{ id: 3, type: "state" }
		];
		const with_null_state = false;
		const result = handle_payload(
			resolved_payload,
			// @ts-ignore
			dependency,
			components,
			"output",
			with_null_state
		);
		expect(result).toEqual(["hello"]);
	});

	it("should return an ouput payload without the two state component values when with_null_state is false", () => {
		const resolved_payload = ["hello", null, "world", null];
		const dependency = {
			outputs: [2, 3, 4, 5]
		};
		const components = [
			{ id: 2, type: "textbox" },
			{ id: 3, type: "state" },
			{ id: 4, type: "textbox" },
			{ id: 5, type: "state" }
		];
		const with_null_state = false;
		const result = handle_payload(
			resolved_payload,
			// @ts-ignore
			dependency,
			components,
			"output",
			with_null_state
		);
		expect(result).toEqual(["hello", "world"]);
	});

	it("should return an ouput payload with the two state component values when with_null_state is true", () => {
		const resolved_payload = ["hello", null, "world", null];
		const dependency = {
			outputs: [2, 3, 4, 5]
		};
		const components = [
			{ id: 2, type: "textbox" },
			{ id: 3, type: "state" },
			{ id: 4, type: "textbox" },
			{ id: 5, type: "state" }
		];
		const with_null_state = true;
		const result = handle_payload(
			resolved_payload,
			// @ts-ignore
			dependency,
			components,
			"output",
			with_null_state
		);
		expect(result).toEqual(["hello", null, "world", null]);
	});

	it("should return the same payload where no state components are defined", () => {
		const resolved_payload = ["hello", "world"];
		const dependency = {
			inputs: [2, 3]
		};
		const components = [
			{ id: 2, type: "textbox" },
			{ id: 3, type: "textbox" }
		];
		const with_null_state = true;
		const result = handle_payload(
			resolved_payload,
			// @ts-ignore
			dependency,
			components,
			"input",
			with_null_state
		);
		expect(result).toEqual(["hello", "world"]);
	});
});