diff --git a/tests/unit/utils/net/headers.spec.ts b/tests/unit/utils/net/headers.spec.ts new file mode 100644 index 0000000..1cb546e --- /dev/null +++ b/tests/unit/utils/net/headers.spec.ts @@ -0,0 +1,585 @@ +import { createReadStream, statSync } from "node:fs"; +import mockFs from "mock-fs"; +import { ArrayMap, MultiMap } from "@/utils/collections/map"; +import { Blob } from "@/utils/net/blob"; +import { appendHeader, appendHeaders, cloneHeaders, deleteHeader, deleteHeaders, getHeader, hasHeader, inferHttpRequestBodyHeaders, setDefaultHeader, setDefaultHeaders, setHeader, setHeaders } from "@/utils/net"; + +beforeEach(() => { + mockFs({ + "file.json": "{}", + }); +}); + +afterEach(() => { + mockFs.restore(); +}); + +describe("hasHeader", () => { + test("returns true when header exists in a Record", () => { + const headers = { "Content-Type": "application/json" }; + + expect(hasHeader(headers, "Content-Type")).toBe(true); + }); + + test("returns false when header does not exist in a Record", () => { + const headers = { "Content-Type": "application/json" }; + + expect(hasHeader(headers, "Authorization")).toBe(false); + }); + + test("returns true when header exists in an Iterable", () => { + const headers = [["Content-Type", "application/json"]]; + + expect(hasHeader(headers, "Content-Type")).toBe(true); + }); + + test("returns false when header does not exist in an Iterable", () => { + const headers = [["Content-Type", "application/json"]]; + + expect(hasHeader(headers, "Authorization")).toBe(false); + }); + + test("returns true when header exists in a Map", () => { + const headers = new Map([["Content-Type", "application/json"]]); + + expect(hasHeader(headers, "Content-Type")).toBe(true); + }); + + test("returns false when header does not exist in a Map", () => { + const headers = new Map([["Content-Type", "application/json"]]); + + expect(hasHeader(headers, "Authorization")).toBe(false); + }); + + test("is case-sensitive by default", () => { + const headers = { "Content-Type": "application/json" }; + + expect(hasHeader(headers, "content-type")).toBe(false); + }); + + test("uses a custom comparer provided by the Map instance", () => { + const headers = new ArrayMap([["Content-Type", "application/json"]], (a, b) => a.toLowerCase() === b.toLowerCase()); + + expect(hasHeader(headers, "content-type")).toBe(true); + }); +}); + +describe("getHeader", () => { + test("returns undefined when headers are not present", () => { + expect(getHeader(null, "Content-Type")).toBeUndefined(); + expect(getHeader(undefined, "Content-Type")).toBeUndefined(); + }); + + test("returns the value of the header when headers are stored in a Record", () => { + const headers = { "Content-Type": "application/json" }; + + expect(getHeader(headers, "Content-Type")).toBe("application/json"); + }); + + test("returns undefined when the header does not exist in a Record", () => { + const headers = { "Content-Type": "application/json" }; + + expect(getHeader(headers, "Authorization")).toBeUndefined(); + }); + + test("returns the value of the header when headers are stored in an Iterable", () => { + const headers = [["Content-Type", "application/json"]]; + + expect(getHeader(headers, "Content-Type")).toBe("application/json"); + }); + + test("returns undefined when the header does not exist in an Iterable", () => { + const headers = [["Content-Type", "application/json"]]; + + expect(getHeader(headers, "Authorization")).toBeUndefined(); + }); + + test("returns the value of the header when headers are stored in a Map", () => { + const headers = new Map([["Content-Type", "application/json"]]); + + expect(getHeader(headers, "Content-Type")).toBe("application/json"); + }); + + test("returns undefined when the header does not exist in a Map", () => { + const headers = new Map([["Content-Type", "application/json"]]); + + expect(getHeader(headers, "Authorization")).toBeUndefined(); + }); + + test("returns the value of the header when headers are stored in a MultiMap", () => { + const headers = new MultiMap([["Content-Type", ["application/json", "charset=utf-8"]]]) as unknown as Map; + + expect(getHeader(headers, "Content-Type")).toBe("application/json, charset=utf-8"); + }); + + test("returns undefined when the header does not exist in a MultiMap", () => { + const headers = new MultiMap([["Content-Type", ["application/json"]]]) as unknown as Map; + + expect(getHeader(headers, "Authorization")).toBeUndefined(); + }); +}); + +describe("appendHeader", () => { + test("appends a header value when headers are stored in a Record", () => { + const headers = { "Content-Type": "application/json" }; + + const updatedHeaders = appendHeader(headers, "Content-Type", "charset=utf-8"); + + expect(getHeader(updatedHeaders, "Content-Type")).toBe("application/json, charset=utf-8"); + }); + + test("appends a header value when headers are stored in an Iterable", () => { + const headers = [["Content-Type", "application/json"]]; + + const updatedHeaders = appendHeader(headers, "Content-Type", "charset=utf-8"); + + expect(getHeader(updatedHeaders, "Content-Type")).toBe("application/json, charset=utf-8"); + }); + + test("appends a header value when headers are stored in a Map", () => { + const headers = new Map([["Content-Type", "application/json"]]); + + const updatedHeaders = appendHeader(headers, "Content-Type", "charset=utf-8"); + + expect(getHeader(updatedHeaders, "Content-Type")).toBe("application/json, charset=utf-8"); + }); + + test("appends a header value when headers are stored in a MultiMap", () => { + const headers = new MultiMap([["Content-Type", ["application/json"]]]) as unknown as Map; + + appendHeader(headers, "Content-Type", "charset=utf-8"); + + expect(getHeader(headers, "Content-Type")).toBe("application/json, charset=utf-8"); + }); + + test("creates a new header when the header does not exist", () => { + const headers = { }; + + const updatedHeaders = appendHeader(headers, "Content-Type", "application/json"); + + expect(getHeader(updatedHeaders, "Content-Type")).toBe("application/json"); + }); +}); + +describe("appendHeaders", () => { + test("appends multiple headers when headers are stored in a Record", () => { + const headers = { "Content-Type": "application/json" }; + const newHeaders = { Authorization: "Bearer token" }; + + const updatedHeaders = appendHeaders(headers, newHeaders); + + expect(getHeader(updatedHeaders, "Content-Type")).toBe("application/json"); + expect(getHeader(updatedHeaders, "Authorization")).toBe("Bearer token"); + }); + + test("appends multiple headers when headers are stored in an Iterable", () => { + const headers = [["Content-Type", "application/json"]]; + const newHeaders = [["Authorization", "Bearer token"]]; + + const updatedHeaders = appendHeaders(headers, newHeaders); + + expect(getHeader(updatedHeaders, "Content-Type")).toBe("application/json"); + expect(getHeader(updatedHeaders, "Authorization")).toBe("Bearer token"); + }); + + test("appends multiple headers when headers are stored in a Map", () => { + const headers = new Map([["Content-Type", "application/json"]]); + const newHeaders = new Map([["Authorization", "Bearer token"]]); + + const updatedHeaders = appendHeaders(headers, newHeaders); + + expect(getHeader(updatedHeaders, "Content-Type")).toBe("application/json"); + expect(getHeader(updatedHeaders, "Authorization")).toBe("Bearer token"); + }); + + test("appends multiple headers when headers are stored in a MultiMap", () => { + const headers = new MultiMap([["Content-Type", ["application/json"]]]) as unknown as Map; + const newHeaders = new MultiMap([["Authorization", ["Bearer token"]]]) as unknown as Map; + + const updatedHeaders = appendHeaders(headers, newHeaders); + + expect(getHeader(updatedHeaders, "Content-Type")).toBe("application/json"); + expect(getHeader(updatedHeaders, "Authorization")).toBe("Bearer token"); + }); +}); + +describe("setHeader", () => { + test("sets a header value when headers are stored in a Record", () => { + const headers = { "Content-Type": "application/json" }; + + const updatedHeaders = setHeader(headers, "Content-Type", "text/plain"); + + expect(getHeader(updatedHeaders, "Content-Type")).toBe("text/plain"); + }); + + test("sets a header value when headers are stored in an Iterable", () => { + const headers = [["Content-Type", "application/json"]]; + + const updatedHeaders = setHeader(headers, "Content-Type", "text/plain"); + + expect(getHeader(updatedHeaders, "Content-Type")).toBe("text/plain"); + }); + + test("sets a header value when headers are stored in a Map", () => { + const headers = new Map([["Content-Type", "application/json"]]); + + const updatedHeaders = setHeader(headers, "Content-Type", "text/plain"); + + expect(getHeader(updatedHeaders, "Content-Type")).toBe("text/plain"); + }); + + test("sets a header value when headers are stored in a MultiMap", () => { + const headers = new MultiMap([["Content-Type", ["application/json"]]]) as unknown as Map; + + const updatedHeaders = setHeader(headers, "Content-Type", "text/plain"); + + expect(getHeader(updatedHeaders, "Content-Type")).toBe("text/plain"); + }); + + test("deletes a header when the value is null or undefined", () => { + const headers = { "Content-Type": "application/json", "Authorization": "Bearer token" }; + + const updatedHeaders = setHeader(setHeader(headers, "Content-Type", null), "Authorization", undefined); + + expect(hasHeader(updatedHeaders, "Content-Type")).toBe(false); + expect(hasHeader(updatedHeaders, "Authorization")).toBe(false); + }); +}); + +describe("setHeaders", () => { + test("sets multiple headers when headers are stored in a Record", () => { + const headers = { "Content-Type": "application/json" }; + const newHeaders = { "Authorization": "Bearer token", "Content-Type": "text/plain" }; + + const updatedHeaders = setHeaders(headers, newHeaders); + + expect(getHeader(updatedHeaders, "Content-Type")).toBe("text/plain"); + expect(getHeader(updatedHeaders, "Authorization")).toBe("Bearer token"); + }); + + test("sets multiple headers when headers are stored in an Iterable", () => { + const headers = [["Content-Type", "application/json"]]; + const newHeaders = [["Authorization", "Bearer token"], ["Content-Type", "text/plain"]]; + + const updatedHeaders = setHeaders(headers, newHeaders); + + expect(getHeader(updatedHeaders, "Content-Type")).toBe("text/plain"); + expect(getHeader(updatedHeaders, "Authorization")).toBe("Bearer token"); + }); + + test("sets multiple headers when headers are stored in a Map", () => { + const headers = new Map([["Content-Type", "application/json"]]); + const newHeaders = new Map([["Authorization", "Bearer token"], ["Content-Type", "text/plain"]]); + + const updatedHeaders = setHeaders(headers, newHeaders); + + expect(getHeader(updatedHeaders, "Content-Type")).toBe("text/plain"); + expect(getHeader(updatedHeaders, "Authorization")).toBe("Bearer token"); + }); + + test("sets multiple headers when headers are stored in a MultiMap", () => { + const headers = new MultiMap([["Content-Type", ["application/json"]]]) as unknown as Map; + const newHeaders = new MultiMap([["Authorization", ["Bearer token"]], ["Content-Type", ["text/plain"]]]) as unknown as Map; + + const updatedHeaders = setHeaders(headers, newHeaders); + + expect(getHeader(updatedHeaders, "Content-Type")).toBe("text/plain"); + expect(getHeader(updatedHeaders, "Authorization")).toBe("Bearer token"); + }); + + test("deletes multiple headers when a new value is null or undefined", () => { + const headers = { "Content-Type": "application/json", "Authorization": "Bearer token" }; + const newHeaders = { "Content-Type": null, "Authorization": undefined }; + + const updatedHeaders = setHeaders(headers, newHeaders); + + expect(hasHeader(updatedHeaders, "Content-Type")).toBe(false); + expect(hasHeader(updatedHeaders, "Authorization")).toBe(false); + }); +}); + +describe("setDefaultHeader", () => { + test("does not set the header value when the header already exists in a Record", () => { + const headers = { "Content-Type": "application/json" }; + + const updatedHeaders = setDefaultHeader(headers, "Content-Type", "text/plain"); + + expect(getHeader(updatedHeaders, "Content-Type")).toBe("application/json"); + }); + + test("sets the header value when the header does not exist in a Record", () => { + const headers = { "Content-Type": "application/json" }; + + const updatedHeaders = setDefaultHeader(headers, "Authorization", "Bearer token"); + + expect(getHeader(updatedHeaders, "Authorization")).toBe("Bearer token"); + }); + + test("does not set the header value when the header already exists in an Iterable", () => { + const headers = [["Content-Type", "application/json"]]; + + const updatedHeaders = setDefaultHeader(headers, "Content-Type", "text/plain"); + + expect(getHeader(updatedHeaders, "Content-Type")).toBe("application/json"); + }); + + test("sets the header value when the header does not exist in an Iterable", () => { + const headers = [["Content-Type", "application/json"]]; + + const updatedHeaders = setDefaultHeader(headers, "Authorization", "Bearer token"); + + expect(getHeader(updatedHeaders, "Authorization")).toBe("Bearer token"); + }); + + test("does not set the header value when the header already exists in a Map", () => { + const headers = new Map([["Content-Type", "application/json"]]); + + const updatedHeaders = setDefaultHeader(headers, "Content-Type", "text/plain"); + + expect(getHeader(updatedHeaders, "Content-Type")).toBe("application/json"); + }); + + test("sets the header value when the header does not exist in a Map", () => { + const headers = new Map([["Content-Type", "application/json"]]); + + const updatedHeaders = setDefaultHeader(headers, "Authorization", "Bearer token"); + + expect(getHeader(updatedHeaders, "Authorization")).toBe("Bearer token"); + }); +}); + +describe("setDefaultHeaders", () => { + test("does not set the header values when the headers already exist in a Record", () => { + const headers = { "Content-Type": "application/json", "Authorization": "Bearer token" }; + const defaultHeaders = { "Content-Type": "text/plain", "Authorization": "Basic auth" }; + + const updatedHeaders = setDefaultHeaders(headers, defaultHeaders); + + expect(getHeader(updatedHeaders, "Content-Type")).toBe("application/json"); + expect(getHeader(updatedHeaders, "Authorization")).toBe("Bearer token"); + }); + + test("sets the header values when the headers do not exist in a Record", () => { + const headers = { "Content-Type": "application/json" }; + const defaultHeaders = { Authorization: "Bearer token" }; + + const updatedHeaders = setDefaultHeaders(headers, defaultHeaders); + + expect(getHeader(updatedHeaders, "Content-Type")).toBe("application/json"); + expect(getHeader(updatedHeaders, "Authorization")).toBe("Bearer token"); + }); + + test("does not set the header values when the headers already exist in an Iterable", () => { + const headers = [["Content-Type", "application/json"], ["Authorization", "Bearer token"]]; + const defaultHeaders = [["Content-Type", "text/plain"], ["Authorization", "Basic auth"]]; + + const updatedHeaders = setDefaultHeaders(headers, defaultHeaders); + + expect(getHeader(updatedHeaders, "Content-Type")).toBe("application/json"); + expect(getHeader(updatedHeaders, "Authorization")).toBe("Bearer token"); + }); + + test("sets the header values when the headers do not exist in an Iterable", () => { + const headers = [["Content-Type", "application/json"]]; + const defaultHeaders = [["Authorization", "Bearer token"]]; + + const updatedHeaders = setDefaultHeaders(headers, defaultHeaders); + + expect(getHeader(updatedHeaders, "Content-Type")).toBe("application/json"); + expect(getHeader(updatedHeaders, "Authorization")).toBe("Bearer token"); + }); + + test("does not set the header values when the headers already exist in a Map", () => { + const headers = new Map([["Content-Type", "application/json"], ["Authorization", "Bearer token"]]); + const defaultHeaders = new Map([["Content-Type", "text/plain"], ["Authorization", "Basic auth"]]); + + const updatedHeaders = setDefaultHeaders(headers, defaultHeaders); + + expect(getHeader(updatedHeaders, "Content-Type")).toBe("application/json"); + expect(getHeader(updatedHeaders, "Authorization")).toBe("Bearer token"); + }); + + test("sets the header values when the headers do not exist in a Map", () => { + const headers = new Map([["Content-Type", "application/json"]]); + const defaultHeaders = new Map([["Authorization", "Bearer token"]]); + + const updatedHeaders = setDefaultHeaders(headers, defaultHeaders); + + expect(getHeader(updatedHeaders, "Content-Type")).toBe("application/json"); + expect(getHeader(updatedHeaders, "Authorization")).toBe("Bearer token"); + }); +}); + +describe("deleteHeader", () => { + test("deletes the header when headers are stored in a Record", () => { + const headers = { "Content-Type": "application/json" }; + + const updatedHeaders = deleteHeader(headers, "Content-Type"); + + expect(hasHeader(updatedHeaders, "Content-Type")).toBe(false); + }); + + test("does not delete other headers when headers are stored in a Record", () => { + const headers = { "Content-Type": "application/json", "Authorization": "Bearer token" }; + + const updatedHeaders = deleteHeader(headers, "Content-Type"); + + expect(hasHeader(updatedHeaders, "Authorization")).toBe(true); + }); + + test("deletes the header when headers are stored in an Iterable", () => { + const headers = [["Content-Type", "application/json"]]; + + const updatedHeaders = deleteHeader(headers, "Content-Type"); + + expect(hasHeader(updatedHeaders, "Content-Type")).toBe(false); + }); + + test("does not delete other headers when headers are stored in an Iterable", () => { + const headers = [["Content-Type", "application/json"], ["Authorization", "Bearer token"]]; + + const updatedHeaders = deleteHeader(headers, "Content-Type"); + + expect(hasHeader(updatedHeaders, "Authorization")).toBe(true); + }); + + test("deletes the header when headers are stored in a Map", () => { + const headers = new Map([["Content-Type", "application/json"]]); + + const updatedHeaders = deleteHeader(headers, "Content-Type"); + + expect(hasHeader(updatedHeaders, "Content-Type")).toBe(false); + }); + + test("does not delete other headers when headers are stored in a Map", () => { + const headers = new Map([["Content-Type", "application/json"], ["Authorization", "Bearer token"]]); + + const updatedHeaders = deleteHeader(headers, "Content-Type"); + + expect(hasHeader(updatedHeaders, "Authorization")).toBe(true); + }); +}); + +describe("deleteHeaders", () => { + test("deletes multiple headers when headers are stored in a Record", () => { + const headers = { "Content-Type": "application/json", "Authorization": "Bearer token" }; + + const updatedHeaders = deleteHeaders(headers, ["Content-Type", "Authorization"]); + + expect(hasHeader(updatedHeaders, "Content-Type")).toBe(false); + expect(hasHeader(updatedHeaders, "Authorization")).toBe(false); + }); + + test("does not delete other headers when headers are stored in a Record", () => { + const headers = { "Content-Type": "application/json", "Authorization": "Bearer token", "Accept": "application/json" }; + + const updatedHeaders = deleteHeaders(headers, ["Content-Type", "Authorization"]); + + expect(hasHeader(updatedHeaders, "Accept")).toBe(true); + }); + + test("deletes multiple headers when headers are stored in an Iterable", () => { + const headers = [["Content-Type", "application/json"], ["Authorization", "Bearer token"]]; + + const updatedHeaders = deleteHeaders(headers, ["Content-Type", "Authorization"]); + + expect(hasHeader(updatedHeaders, "Content-Type")).toBe(false); + expect(hasHeader(updatedHeaders, "Authorization")).toBe(false); + }); + + test("does not delete other headers when headers are stored in an Iterable", () => { + const headers = [["Content-Type", "application/json"], ["Authorization", "Bearer token"], ["Accept", "application/json"]]; + + const updatedHeaders = deleteHeaders(headers, ["Content-Type", "Authorization"]); + + expect(hasHeader(updatedHeaders, "Accept")).toBe(true); + }); + + test("deletes multiple headers when headers are stored in a Map", () => { + const headers = new Map([["Content-Type", "application/json"], ["Authorization", "Bearer token"]]); + + const updatedHeaders = deleteHeaders(headers, ["Content-Type", "Authorization"]); + + expect(hasHeader(updatedHeaders, "Content-Type")).toBe(false); + expect(hasHeader(updatedHeaders, "Authorization")).toBe(false); + }); + + test("does not delete other headers when headers are stored in a Map", () => { + const headers = new Map([["Content-Type", "application/json"], ["Authorization", "Bearer token"], ["Accept", "application/json"]]); + + const updatedHeaders = deleteHeaders(headers, ["Content-Type", "Authorization"]); + + expect(hasHeader(updatedHeaders, "Accept")).toBe(true); + }); +}); + +describe("cloneHeaders", () => { + test("returns undefined when headers are not present", () => { + expect(cloneHeaders(null)).toBeUndefined(); + expect(cloneHeaders(undefined)).toBeUndefined(); + }); + + test("clones the headers when headers are stored in a Record", () => { + const headers = { "Content-Type": "application/json" }; + + const clonedHeaders = cloneHeaders(headers); + + expect(clonedHeaders).not.toBe(headers); + expect(clonedHeaders).toEqual(headers); + }); + + test("clones the headers when headers are stored in an Iterable", () => { + const headers = [["Content-Type", "application/json"]]; + + const clonedHeaders = cloneHeaders(headers); + + expect(clonedHeaders).not.toBe(headers); + expect(clonedHeaders).toEqual(headers); + }); + + test("clones the headers when headers are stored in a Map", () => { + const headers = new Map([["Content-Type", "application/json"]]); + + const clonedHeaders = cloneHeaders(headers); + + expect(clonedHeaders).not.toBe(headers); + expect(clonedHeaders).toEqual(headers); + }); +}); + +describe("inferHttpRequestBodyHeaders", () => { + test("returns empty headers when body is not streamable", () => { + const body = "non-streamable body"; + + const headers = inferHttpRequestBodyHeaders(body); + + expect(headers).toEqual({}); + }); + + test("returns correct headers when body is a Blob", () => { + const body = new Blob(["blob content"]); + + const headers = inferHttpRequestBodyHeaders(body); + + expect(headers["Content-Type"]).toBe("application/octet-stream"); + expect(headers["Content-Length"]).toBe(String(body.size)); + }); + + test("returns correct headers when body is a Buffer", () => { + const body = Buffer.from("buffer content"); + + const headers = inferHttpRequestBodyHeaders(body); + + expect(headers["Content-Type"]).toBe("application/octet-stream"); + expect(headers["Content-Length"]).toBe(String(body.byteLength)); + }); + + test("returns correct headers when body is a ReadableStream created from a file path", () => { + const body = createReadStream("file.json"); + + const headers = inferHttpRequestBodyHeaders(body); + + expect(headers["Content-Type"]).toBe("application/octet-stream"); + expect(headers["Content-Length"]).toBe(String(statSync("file.json").size)); + }); +});