
























@@ -1,3 +1,5 @@
1+import { createServer, type Server } from "node:http";
2+import { fetch as undiciFetch } from "undici";
13import { afterEach, describe, expect, it, vi } from "vitest";
24import { serializeRequestBody } from "./rest-body.js";
35import { RequestClient } from "./rest.js";
@@ -406,6 +408,77 @@ describe("RequestClient", () => {
406408expect(form.get("files[0]")).toBeInstanceOf(Blob);
407409});
408410411+it("dispatches multipart uploads with a multipart/form-data content type", async () => {
412+const fetchSpy = vi.fn(async (_input: string | URL | Request, init?: RequestInit) => {
413+expect(init?.headers).toBeInstanceOf(Headers);
414+expect((init?.headers as Headers).get("Content-Type")).toMatch(
415+/^multipart\/form-data; boundary=/,
416+);
417+expect(init?.body).not.toBeInstanceOf(FormData);
418+const request = new Request("https://discord.test/upload", {
419+method: "POST",
420+headers: init?.headers,
421+body: init?.body,
422+});
423+expect(request.headers.get("Content-Type")).toMatch(/^multipart\/form-data; boundary=/);
424+return new Response(JSON.stringify({ id: "msg" }), {
425+status: 200,
426+headers: { "Content-Type": "application/json" },
427+});
428+});
429+const client = new RequestClient("test-token", { fetch: fetchSpy, queueRequests: false });
430+431+await expect(
432+client.post("/channels/c1/messages", {
433+body: {
434+content: "file",
435+files: [{ name: "a.txt", data: new Uint8Array([1]), contentType: "text/plain" }],
436+},
437+}),
438+).resolves.toEqual({ id: "msg" });
439+440+expect(fetchSpy).toHaveBeenCalledTimes(1);
441+});
442+443+it("dispatches multipart uploads through undici fetch with a multipart/form-data content type", async () => {
444+const server = await new Promise<Server>((resolve) => {
445+const srv = createServer((req, res) => {
446+expect(req.headers["content-type"]).toMatch(/^multipart\/form-data; boundary=/);
447+req.resume();
448+req.on("end", () => {
449+res.writeHead(200, { "Content-Type": "application/json" });
450+res.end(JSON.stringify({ id: "msg" }));
451+});
452+});
453+srv.listen(0, () => resolve(srv));
454+});
455+try {
456+const address = server.address();
457+if (!address || typeof address === "string") {
458+throw new Error("test server did not bind to a TCP port");
459+}
460+const client = new RequestClient("test-token", {
461+baseUrl: `http://127.0.0.1:${address.port}`,
462+apiVersion: 10,
463+fetch: undiciFetch as unknown as typeof fetch,
464+queueRequests: false,
465+});
466+467+await expect(
468+client.post("/channels/c1/messages", {
469+body: {
470+content: "file",
471+files: [{ name: "a.txt", data: new Uint8Array([1]), contentType: "text/plain" }],
472+},
473+}),
474+).resolves.toEqual({ id: "msg" });
475+} finally {
476+await new Promise<void>((resolve, reject) => {
477+server.close((err) => (err ? reject(err) : resolve()));
478+});
479+}
480+});
481+409482it("serializes form multipart uploads for sticker-style endpoints", () => {
410483const headers = new Headers();
411484const body = serializeRequestBody(
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。