mirror of
https://github.com/penpot/penpot.git
synced 2026-10-29 05:50:35 +00:00
Update media-processor dependencies (multer 2.4, vitest 5 and others) and fix mockFile to emit Buffer chunks so multer memory storage Buffer.concat works. String chunks caused TypeError and 5s timeouts in two tests. AI-assisted-by: muse-spark-1.3-contributor
213 lines
6.2 KiB
TypeScript
213 lines
6.2 KiB
TypeScript
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
import { createHybridStorage } from "../src/upload-storage.js";
|
|
import type { Request } from "express";
|
|
import { Readable } from "node:stream";
|
|
import { rm } from "node:fs/promises";
|
|
|
|
function mockReq(contentLength?: string): Request {
|
|
const headers: Record<string, string> = {};
|
|
if (contentLength !== undefined) {
|
|
headers["content-length"] = contentLength;
|
|
}
|
|
return { headers } as Request;
|
|
}
|
|
|
|
function mockFile(content: string = "test content") {
|
|
const stream = Readable.from([Buffer.from(content)]);
|
|
return {
|
|
fieldname: "file",
|
|
originalname: "test.txt",
|
|
encoding: "7bit",
|
|
mimetype: "text/plain",
|
|
stream,
|
|
} as Express.Multer.File;
|
|
}
|
|
|
|
describe("createHybridStorage", () => {
|
|
let storage: ReturnType<typeof createHybridStorage>;
|
|
let tempDirs: string[] = [];
|
|
|
|
beforeEach(() => {
|
|
storage = createHybridStorage({ memoryThreshold: 1024 });
|
|
});
|
|
|
|
afterEach(async () => {
|
|
for (const dir of tempDirs) {
|
|
await rm(dir, { recursive: true, force: true });
|
|
}
|
|
tempDirs = [];
|
|
});
|
|
|
|
it("uses memory storage when Content-Length is below threshold", async () => {
|
|
const req = mockReq("100");
|
|
const file = mockFile("small content");
|
|
|
|
await new Promise<void>((resolve, reject) => {
|
|
storage._handleFile(req, file, (err, info) => {
|
|
if (err) reject(err);
|
|
else {
|
|
expect(info).toBeDefined();
|
|
expect((info as any).path).toBeUndefined();
|
|
expect((info as any).buffer).toBeDefined();
|
|
resolve();
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
it("uses disk storage when Content-Length is above threshold", async () => {
|
|
const req = mockReq("2048");
|
|
const file = mockFile("x".repeat(2048));
|
|
|
|
await new Promise<void>((resolve, reject) => {
|
|
storage._handleFile(req, file, (err, info) => {
|
|
if (err) reject(err);
|
|
else {
|
|
expect(info).toBeDefined();
|
|
expect((info as any).path).toBeDefined();
|
|
expect((info as any).destination).toBeDefined();
|
|
tempDirs.push((info as any).destination);
|
|
resolve();
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
it("uses disk storage when Content-Length is absent (chunked transfer)", async () => {
|
|
const req = mockReq();
|
|
const file = mockFile("chunked content");
|
|
|
|
await new Promise<void>((resolve, reject) => {
|
|
storage._handleFile(req, file, (err, info) => {
|
|
if (err) reject(err);
|
|
else {
|
|
expect(info).toBeDefined();
|
|
expect((info as any).path).toBeDefined();
|
|
expect((info as any).destination).toBeDefined();
|
|
tempDirs.push((info as any).destination);
|
|
resolve();
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
it("uses disk storage when Content-Length is invalid", async () => {
|
|
const req = mockReq("not-a-number");
|
|
const file = mockFile("content");
|
|
|
|
await new Promise<void>((resolve, reject) => {
|
|
storage._handleFile(req, file, (err, info) => {
|
|
if (err) reject(err);
|
|
else {
|
|
expect(info).toBeDefined();
|
|
expect((info as any).path).toBeDefined();
|
|
tempDirs.push((info as any).destination);
|
|
resolve();
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
it("removes file from disk", async () => {
|
|
const req = mockReq("2048");
|
|
const file = mockFile("x".repeat(2048));
|
|
|
|
const info = await new Promise<any>((resolve, reject) => {
|
|
storage._handleFile(req, file, (err, info) => {
|
|
if (err) reject(err);
|
|
else resolve(info);
|
|
});
|
|
});
|
|
|
|
tempDirs.push(info.destination);
|
|
|
|
await new Promise<void>((resolve, reject) => {
|
|
storage._removeFile(req, { ...file, path: info.path } as any, (err) => {
|
|
if (err) reject(err);
|
|
else resolve();
|
|
});
|
|
});
|
|
});
|
|
|
|
it("uses memory storage when Content-Length is 0", async () => {
|
|
const req = mockReq("0");
|
|
const file = mockFile("");
|
|
|
|
await new Promise<void>((resolve, reject) => {
|
|
storage._handleFile(req, file, (err, info) => {
|
|
if (err) reject(err);
|
|
else {
|
|
expect(info).toBeDefined();
|
|
expect((info as any).path).toBeUndefined();
|
|
expect((info as any).buffer).toBeDefined();
|
|
resolve();
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
it("uses disk storage when Content-Length equals threshold", async () => {
|
|
const req = mockReq("1024");
|
|
const file = mockFile("x".repeat(1024));
|
|
|
|
await new Promise<void>((resolve, reject) => {
|
|
storage._handleFile(req, file, (err, info) => {
|
|
if (err) reject(err);
|
|
else {
|
|
expect(info).toBeDefined();
|
|
expect((info as any).path).toBeDefined();
|
|
expect((info as any).destination).toBeDefined();
|
|
tempDirs.push((info as any).destination);
|
|
resolve();
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
it("uses disk storage when Content-Length is very large", async () => {
|
|
const req = mockReq("1073741824"); // 1GB
|
|
const file = mockFile("x"); // Small actual content, but large Content-Length
|
|
|
|
await new Promise<void>((resolve, reject) => {
|
|
storage._handleFile(req, file, (err, info) => {
|
|
if (err) reject(err);
|
|
else {
|
|
expect(info).toBeDefined();
|
|
expect((info as any).path).toBeDefined();
|
|
expect((info as any).destination).toBeDefined();
|
|
tempDirs.push((info as any).destination);
|
|
resolve();
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
it("reuses the same temp directory for concurrent uploads", async () => {
|
|
const req1 = mockReq("2048");
|
|
const file1 = mockFile("x".repeat(2048));
|
|
const req2 = mockReq("2048");
|
|
const file2 = mockFile("y".repeat(2048));
|
|
|
|
const [info1, info2] = await Promise.all([
|
|
new Promise<any>((resolve, reject) => {
|
|
storage._handleFile(req1, file1, (err, info) => {
|
|
if (err) reject(err);
|
|
else resolve(info);
|
|
});
|
|
}),
|
|
new Promise<any>((resolve, reject) => {
|
|
storage._handleFile(req2, file2, (err, info) => {
|
|
if (err) reject(err);
|
|
else resolve(info);
|
|
});
|
|
}),
|
|
]);
|
|
|
|
tempDirs.push(info1.destination);
|
|
tempDirs.push(info2.destination);
|
|
|
|
// Both uploads should use the same temp directory
|
|
expect(info1.destination).toBe(info2.destination);
|
|
});
|
|
});
|