499 lines
18 KiB
TypeScript
499 lines
18 KiB
TypeScript
import fs from "fs";
|
|
import { join } from "path";
|
|
import { Readable } from "stream";
|
|
import { toBinaryUploadRequest, type Uploadable } from "../../../src/core/file/index";
|
|
|
|
describe("toBinaryUploadRequest", () => {
|
|
const TEST_FILE_PATH = join(__dirname, "..", "test-file.txt");
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
describe("Buffer input", () => {
|
|
it("should handle Buffer with all metadata", async () => {
|
|
const buffer = Buffer.from("test data");
|
|
const input: Uploadable.WithMetadata = {
|
|
data: buffer,
|
|
filename: "test.txt",
|
|
contentType: "text/plain",
|
|
contentLength: 42,
|
|
};
|
|
|
|
const result = await toBinaryUploadRequest(input);
|
|
|
|
expect(result.body).toBe(buffer);
|
|
expect(result.headers).toEqual({
|
|
"Content-Disposition": 'attachment; filename="test.txt"',
|
|
"Content-Type": "text/plain",
|
|
"Content-Length": "42",
|
|
});
|
|
});
|
|
|
|
it("should handle Buffer without metadata", async () => {
|
|
const buffer = Buffer.from("test data");
|
|
const input: Uploadable.WithMetadata = {
|
|
data: buffer,
|
|
};
|
|
|
|
const result = await toBinaryUploadRequest(input);
|
|
|
|
expect(result.body).toBe(buffer);
|
|
expect(result.headers).toEqual({
|
|
"Content-Length": "9", // buffer.length
|
|
});
|
|
});
|
|
|
|
it("should handle Buffer passed directly", async () => {
|
|
const buffer = Buffer.from("test data");
|
|
|
|
const result = await toBinaryUploadRequest(buffer);
|
|
|
|
expect(result.body).toBe(buffer);
|
|
expect(result.headers).toEqual({
|
|
"Content-Length": "9", // buffer.length
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("ArrayBuffer input", () => {
|
|
it("should handle ArrayBuffer with metadata", async () => {
|
|
const arrayBuffer = new ArrayBuffer(10);
|
|
const input: Uploadable.WithMetadata = {
|
|
data: arrayBuffer,
|
|
filename: "data.bin",
|
|
contentType: "application/octet-stream",
|
|
};
|
|
|
|
const result = await toBinaryUploadRequest(input);
|
|
|
|
expect(result.body).toBe(arrayBuffer);
|
|
expect(result.headers).toEqual({
|
|
"Content-Disposition": 'attachment; filename="data.bin"',
|
|
"Content-Type": "application/octet-stream",
|
|
"Content-Length": "10", // arrayBuffer.byteLength
|
|
});
|
|
});
|
|
|
|
it("should handle ArrayBuffer passed directly", async () => {
|
|
const arrayBuffer = new ArrayBuffer(10);
|
|
|
|
const result = await toBinaryUploadRequest(arrayBuffer);
|
|
|
|
expect(result.body).toBe(arrayBuffer);
|
|
expect(result.headers).toEqual({
|
|
"Content-Length": "10", // arrayBuffer.byteLength
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("Uint8Array input", () => {
|
|
it("should handle Uint8Array with metadata", async () => {
|
|
const uint8Array = new Uint8Array([1, 2, 3, 4, 5]);
|
|
const input: Uploadable.WithMetadata = {
|
|
data: uint8Array,
|
|
filename: "bytes.bin",
|
|
contentType: "application/octet-stream",
|
|
};
|
|
|
|
const result = await toBinaryUploadRequest(input);
|
|
|
|
expect(result.body).toBe(uint8Array);
|
|
expect(result.headers).toEqual({
|
|
"Content-Disposition": 'attachment; filename="bytes.bin"',
|
|
"Content-Type": "application/octet-stream",
|
|
"Content-Length": "5", // uint8Array.byteLength
|
|
});
|
|
});
|
|
|
|
it("should handle Uint8Array passed directly", async () => {
|
|
const uint8Array = new Uint8Array([1, 2, 3, 4, 5]);
|
|
|
|
const result = await toBinaryUploadRequest(uint8Array);
|
|
|
|
expect(result.body).toBe(uint8Array);
|
|
expect(result.headers).toEqual({
|
|
"Content-Length": "5", // uint8Array.byteLength
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("Blob input", () => {
|
|
it("should handle Blob with metadata", async () => {
|
|
const blob = new Blob(["test content"], { type: "text/plain" });
|
|
const input: Uploadable.WithMetadata = {
|
|
data: blob,
|
|
filename: "override.txt",
|
|
contentType: "text/html", // Override blob's type
|
|
};
|
|
|
|
const result = await toBinaryUploadRequest(input);
|
|
|
|
expect(result.body).toBe(blob);
|
|
expect(result.headers).toEqual({
|
|
"Content-Disposition": 'attachment; filename="override.txt"',
|
|
"Content-Type": "text/html", // Should use provided contentType
|
|
"Content-Length": "12", // blob.size
|
|
});
|
|
});
|
|
|
|
it("should handle Blob with intrinsic type", async () => {
|
|
const blob = new Blob(["test content"], { type: "application/json" });
|
|
const input: Uploadable.WithMetadata = {
|
|
data: blob,
|
|
filename: "data.json",
|
|
};
|
|
|
|
const result = await toBinaryUploadRequest(input);
|
|
|
|
expect(result.body).toBe(blob);
|
|
expect(result.headers).toEqual({
|
|
"Content-Disposition": 'attachment; filename="data.json"',
|
|
"Content-Type": "application/json", // Should use blob's type
|
|
"Content-Length": "12", // blob.size
|
|
});
|
|
});
|
|
|
|
it("should handle Blob passed directly", async () => {
|
|
const blob = new Blob(["test content"], { type: "text/plain" });
|
|
|
|
const result = await toBinaryUploadRequest(blob);
|
|
|
|
expect(result.body).toBe(blob);
|
|
expect(result.headers).toEqual({
|
|
"Content-Type": "text/plain", // Should use blob's type
|
|
"Content-Length": "12", // blob.size
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("File input", () => {
|
|
it("should handle File with metadata", async () => {
|
|
const file = new File(["file content"], "original.txt", { type: "text/plain" });
|
|
const input: Uploadable.WithMetadata = {
|
|
data: file,
|
|
filename: "renamed.txt",
|
|
contentType: "text/html", // Override file's type
|
|
};
|
|
|
|
const result = await toBinaryUploadRequest(input);
|
|
|
|
expect(result.body).toBe(file);
|
|
expect(result.headers).toEqual({
|
|
"Content-Disposition": 'attachment; filename="renamed.txt"',
|
|
"Content-Type": "text/html", // Should use provided contentType
|
|
"Content-Length": "12", // file.size
|
|
});
|
|
});
|
|
|
|
it("should handle File with intrinsic properties", async () => {
|
|
const file = new File(["file content"], "test.json", { type: "application/json" });
|
|
const input: Uploadable.WithMetadata = {
|
|
data: file,
|
|
};
|
|
|
|
const result = await toBinaryUploadRequest(input);
|
|
|
|
expect(result.body).toBe(file);
|
|
expect(result.headers).toEqual({
|
|
"Content-Disposition": 'attachment; filename="test.json"', // Should use file's name
|
|
"Content-Type": "application/json", // Should use file's type
|
|
"Content-Length": "12", // file.size
|
|
});
|
|
});
|
|
|
|
it("should handle File passed directly", async () => {
|
|
const file = new File(["file content"], "direct.txt", { type: "text/plain" });
|
|
|
|
const result = await toBinaryUploadRequest(file);
|
|
|
|
expect(result.body).toBe(file);
|
|
expect(result.headers).toEqual({
|
|
"Content-Disposition": 'attachment; filename="direct.txt"',
|
|
"Content-Type": "text/plain",
|
|
"Content-Length": "12", // file.size
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("ReadableStream input", () => {
|
|
it("should handle ReadableStream with metadata", async () => {
|
|
const stream = new ReadableStream({
|
|
start(controller) {
|
|
controller.enqueue(new TextEncoder().encode("stream data"));
|
|
controller.close();
|
|
},
|
|
});
|
|
const input: Uploadable.WithMetadata = {
|
|
data: stream,
|
|
filename: "stream.txt",
|
|
contentType: "text/plain",
|
|
contentLength: 100,
|
|
};
|
|
|
|
const result = await toBinaryUploadRequest(input);
|
|
|
|
expect(result.body).toBe(stream);
|
|
expect(result.headers).toEqual({
|
|
"Content-Disposition": 'attachment; filename="stream.txt"',
|
|
"Content-Type": "text/plain",
|
|
"Content-Length": "100", // Should use provided contentLength
|
|
});
|
|
});
|
|
|
|
it("should handle ReadableStream without size", async () => {
|
|
const stream = new ReadableStream({
|
|
start(controller) {
|
|
controller.enqueue(new TextEncoder().encode("stream data"));
|
|
controller.close();
|
|
},
|
|
});
|
|
const input: Uploadable.WithMetadata = {
|
|
data: stream,
|
|
filename: "stream.txt",
|
|
contentType: "text/plain",
|
|
};
|
|
|
|
const result = await toBinaryUploadRequest(input);
|
|
|
|
expect(result.body).toBe(stream);
|
|
expect(result.headers).toEqual({
|
|
"Content-Disposition": 'attachment; filename="stream.txt"',
|
|
"Content-Type": "text/plain",
|
|
// No Content-Length header since it cannot be determined from ReadableStream
|
|
});
|
|
});
|
|
|
|
it("should handle ReadableStream passed directly", async () => {
|
|
const stream = new ReadableStream({
|
|
start(controller) {
|
|
controller.enqueue(new TextEncoder().encode("stream data"));
|
|
controller.close();
|
|
},
|
|
});
|
|
|
|
const result = await toBinaryUploadRequest(stream);
|
|
|
|
expect(result.body).toBe(stream);
|
|
expect(result.headers).toEqual({
|
|
// No headers since no metadata provided and cannot be determined
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("Node.js Readable stream input", () => {
|
|
it("should handle Readable stream with metadata", async () => {
|
|
const readable = new Readable({
|
|
read() {
|
|
this.push("readable data");
|
|
this.push(null);
|
|
},
|
|
});
|
|
const input: Uploadable.WithMetadata = {
|
|
data: readable,
|
|
filename: "readable.txt",
|
|
contentType: "text/plain",
|
|
contentLength: 50,
|
|
};
|
|
|
|
const result = await toBinaryUploadRequest(input);
|
|
|
|
expect(result.body).toBe(readable);
|
|
expect(result.headers).toEqual({
|
|
"Content-Disposition": 'attachment; filename="readable.txt"',
|
|
"Content-Type": "text/plain",
|
|
"Content-Length": "50", // Should use provided contentLength
|
|
});
|
|
});
|
|
|
|
it("should handle Readable stream without size", async () => {
|
|
const readable = new Readable({
|
|
read() {
|
|
this.push("readable data");
|
|
this.push(null);
|
|
},
|
|
});
|
|
const input: Uploadable.WithMetadata = {
|
|
data: readable,
|
|
filename: "readable.txt",
|
|
contentType: "text/plain",
|
|
};
|
|
|
|
const result = await toBinaryUploadRequest(input);
|
|
|
|
expect(result.body).toBe(readable);
|
|
expect(result.headers).toEqual({
|
|
"Content-Disposition": 'attachment; filename="readable.txt"',
|
|
"Content-Type": "text/plain",
|
|
// No Content-Length header since it cannot be determined from Readable
|
|
});
|
|
});
|
|
|
|
it("should handle Readable stream passed directly", async () => {
|
|
const readable = new Readable({
|
|
read() {
|
|
this.push("readable data");
|
|
this.push(null);
|
|
},
|
|
});
|
|
|
|
const result = await toBinaryUploadRequest(readable);
|
|
|
|
expect(result.body).toBe(readable);
|
|
expect(result.headers).toEqual({
|
|
// No headers since no metadata provided and cannot be determined
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("File path input (FromPath type)", () => {
|
|
it("should handle file path with all metadata", async () => {
|
|
const input: Uploadable.FromPath = {
|
|
path: TEST_FILE_PATH,
|
|
filename: "custom.txt",
|
|
contentType: "text/html",
|
|
contentLength: 42,
|
|
};
|
|
|
|
const result = await toBinaryUploadRequest(input);
|
|
|
|
expect(result.body).toBeInstanceOf(fs.ReadStream);
|
|
expect(result.headers).toEqual({
|
|
"Content-Disposition": 'attachment; filename="custom.txt"',
|
|
"Content-Type": "text/html",
|
|
"Content-Length": "42", // Should use provided contentLength
|
|
});
|
|
});
|
|
|
|
it("should handle file path with minimal metadata", async () => {
|
|
const input: Uploadable.FromPath = {
|
|
path: TEST_FILE_PATH,
|
|
contentType: "text/plain",
|
|
};
|
|
|
|
const result = await toBinaryUploadRequest(input);
|
|
|
|
expect(result.body).toBeInstanceOf(fs.ReadStream);
|
|
expect(result.headers).toEqual({
|
|
"Content-Disposition": 'attachment; filename="test-file.txt"', // Should extract from path
|
|
"Content-Type": "text/plain",
|
|
"Content-Length": "21", // Should determine from file system (test file is 21 bytes)
|
|
});
|
|
});
|
|
|
|
it("should handle file path with no metadata", async () => {
|
|
const input: Uploadable.FromPath = {
|
|
path: TEST_FILE_PATH,
|
|
};
|
|
|
|
const result = await toBinaryUploadRequest(input);
|
|
|
|
expect(result.body).toBeInstanceOf(fs.ReadStream);
|
|
expect(result.headers).toEqual({
|
|
"Content-Disposition": 'attachment; filename="test-file.txt"', // Should extract from path
|
|
"Content-Length": "21", // Should determine from file system (test file is 21 bytes)
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("ArrayBufferView input", () => {
|
|
it("should handle ArrayBufferView with metadata", async () => {
|
|
const arrayBuffer = new ArrayBuffer(10);
|
|
const arrayBufferView = new Int8Array(arrayBuffer);
|
|
const input: Uploadable.WithMetadata = {
|
|
data: arrayBufferView,
|
|
filename: "view.bin",
|
|
contentType: "application/octet-stream",
|
|
};
|
|
|
|
const result = await toBinaryUploadRequest(input);
|
|
|
|
expect(result.body).toBe(arrayBufferView);
|
|
expect(result.headers).toEqual({
|
|
"Content-Disposition": 'attachment; filename="view.bin"',
|
|
"Content-Type": "application/octet-stream",
|
|
"Content-Length": "10", // arrayBufferView.byteLength
|
|
});
|
|
});
|
|
|
|
it("should handle ArrayBufferView passed directly", async () => {
|
|
const arrayBuffer = new ArrayBuffer(10);
|
|
const arrayBufferView = new Int8Array(arrayBuffer);
|
|
|
|
const result = await toBinaryUploadRequest(arrayBufferView);
|
|
|
|
expect(result.body).toBe(arrayBufferView);
|
|
expect(result.headers).toEqual({
|
|
"Content-Length": "10", // arrayBufferView.byteLength
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("Edge cases", () => {
|
|
it("should handle empty headers when no metadata is available", async () => {
|
|
const buffer = Buffer.from("");
|
|
const input: Uploadable.WithMetadata = {
|
|
data: buffer,
|
|
};
|
|
|
|
const result = await toBinaryUploadRequest(input);
|
|
|
|
expect(result.body).toBe(buffer);
|
|
expect(result.headers).toEqual({
|
|
"Content-Length": "0",
|
|
});
|
|
});
|
|
|
|
it("should handle zero contentLength", async () => {
|
|
const buffer = Buffer.from("test");
|
|
const input: Uploadable.WithMetadata = {
|
|
data: buffer,
|
|
contentLength: 0,
|
|
};
|
|
|
|
const result = await toBinaryUploadRequest(input);
|
|
|
|
expect(result.body).toBe(buffer);
|
|
expect(result.headers).toEqual({
|
|
"Content-Length": "0", // Should use provided 0
|
|
});
|
|
});
|
|
|
|
it("should handle null filename", async () => {
|
|
const buffer = Buffer.from("test");
|
|
const input: Uploadable.WithMetadata = {
|
|
data: buffer,
|
|
filename: undefined,
|
|
contentType: "text/plain",
|
|
};
|
|
|
|
const result = await toBinaryUploadRequest(input);
|
|
|
|
expect(result.body).toBe(buffer);
|
|
expect(result.headers).toEqual({
|
|
"Content-Type": "text/plain",
|
|
"Content-Length": "4",
|
|
// No Content-Disposition since filename is undefined
|
|
});
|
|
});
|
|
|
|
it("should handle null contentType", async () => {
|
|
const buffer = Buffer.from("test");
|
|
const input: Uploadable.WithMetadata = {
|
|
data: buffer,
|
|
filename: "test.txt",
|
|
contentType: undefined,
|
|
};
|
|
|
|
const result = await toBinaryUploadRequest(input);
|
|
|
|
expect(result.body).toBe(buffer);
|
|
expect(result.headers).toEqual({
|
|
"Content-Disposition": 'attachment; filename="test.txt"',
|
|
"Content-Length": "4",
|
|
// No Content-Type since contentType is undefined
|
|
});
|
|
});
|
|
});
|
|
});
|