Files
bun.sh/test/js/node/util/mime-api.test.ts

410 lines
14 KiB
TypeScript

import { describe, expect, test } from "bun:test";
import { bunExe } from "harness";
import { MIMEParams, MIMEType } from "util";
describe("MIME API", () => {
const WHITESPACES = "\t\n\f\r ";
const NOT_HTTP_TOKEN_CODE_POINT = ",";
const NOT_HTTP_QUOTED_STRING_CODE_POINT = "\n";
test("class instance integrity", () => {
const mime = new MIMEType("application/ecmascript; ");
const mime_descriptors = Object.getOwnPropertyDescriptors(mime);
const mime_proto = Object.getPrototypeOf(mime);
const mime_impersonator = { __proto__: mime_proto };
for (const key of Object.keys(mime_descriptors)) {
const descriptor = mime_descriptors[key];
if (descriptor.get) {
const getter = descriptor.get;
expect(() => getter.call(mime_impersonator)).toThrow(/invalid receiver/i);
}
if (descriptor.set) {
const setter = descriptor.set;
expect(() => setter.call(mime_impersonator, "x")).toThrow(/invalid receiver/i);
}
}
});
test("basic properties and string conversion", () => {
const mime = new MIMEType("application/ecmascript; ");
expect(JSON.stringify(mime)).toBe(JSON.stringify("application/ecmascript"));
expect(`${mime}`).toBe("application/ecmascript");
expect(mime.essence).toBe("application/ecmascript");
expect(mime.type).toBe("application");
expect(mime.subtype).toBe("ecmascript");
expect(mime.params).toBeDefined();
expect([...mime.params]).toEqual([]);
expect(mime.params.has("not found")).toBe(false);
expect(mime.params.get("not found")).toBe(null);
expect(mime.params.delete("not found")).toBe(undefined);
});
test("type property manipulation", () => {
const mime = new MIMEType("application/ecmascript; ");
mime.type = "text";
expect(mime.type).toBe("text");
expect(JSON.stringify(mime)).toBe(JSON.stringify("text/ecmascript"));
expect(`${mime}`).toBe("text/ecmascript");
expect(mime.essence).toBe("text/ecmascript");
expect(() => {
mime.type = `${WHITESPACES}text`;
}).toThrow(/The MIME syntax for a type in/);
expect(() => {
mime.type = "";
}).toThrow(/type/i);
expect(() => {
mime.type = "/";
}).toThrow(/type/i);
expect(() => {
mime.type = "x/";
}).toThrow(/type/i);
expect(() => {
mime.type = "/x";
}).toThrow(/type/i);
expect(() => {
mime.type = NOT_HTTP_TOKEN_CODE_POINT;
}).toThrow(/type/i);
expect(() => {
mime.type = `${NOT_HTTP_TOKEN_CODE_POINT}/`;
}).toThrow(/type/i);
expect(() => {
mime.type = `/${NOT_HTTP_TOKEN_CODE_POINT}`;
}).toThrow(/type/i);
});
test("subtype property manipulation", () => {
const mime = new MIMEType("application/ecmascript; ");
mime.type = "text";
mime.subtype = "javascript";
expect(mime.type).toBe("text");
expect(JSON.stringify(mime)).toBe(JSON.stringify("text/javascript"));
expect(`${mime}`).toBe("text/javascript");
expect(mime.essence).toBe("text/javascript");
expect(`${mime.params}`).toBe("");
expect(`${new MIMEParams()}`).toBe("");
// @ts-expect-error
expect(`${new MIMEParams(mime.params)}`).toBe("");
// @ts-expect-error
expect(`${new MIMEParams(`${mime.params}`)}`).toBe("");
expect(() => {
mime.subtype = `javascript${WHITESPACES}`;
}).toThrow(/The MIME syntax for a subtype in/);
expect(() => {
mime.subtype = "";
}).toThrow(/subtype/i);
expect(() => {
mime.subtype = ";";
}).toThrow(/subtype/i);
expect(() => {
mime.subtype = "x;";
}).toThrow(/subtype/i);
expect(() => {
mime.subtype = ";x";
}).toThrow(/subtype/i);
expect(() => {
mime.subtype = NOT_HTTP_TOKEN_CODE_POINT;
}).toThrow(/subtype/i);
expect(() => {
mime.subtype = `${NOT_HTTP_TOKEN_CODE_POINT};`;
}).toThrow(/subtype/i);
expect(() => {
mime.subtype = `;${NOT_HTTP_TOKEN_CODE_POINT}`;
}).toThrow(/subtype/i);
});
test("parameters manipulation", () => {
const mime = new MIMEType("application/ecmascript; ");
mime.type = "text";
mime.subtype = "javascript";
const params = mime.params;
// Setting parameters
params.set("charset", "utf-8");
expect(params.has("charset")).toBe(true);
expect(params.get("charset")).toBe("utf-8");
expect([...params]).toEqual([["charset", "utf-8"]]);
expect(JSON.stringify(mime)).toBe(JSON.stringify("text/javascript;charset=utf-8"));
expect(`${mime}`).toBe("text/javascript;charset=utf-8");
expect(mime.essence).toBe("text/javascript");
expect(`${mime.params}`).toBe("charset=utf-8");
// @ts-expect-error
expect(`${new MIMEParams(mime.params)}`).toBe("");
// @ts-expect-error
expect(`${new MIMEParams(`${mime.params}`)}`).toBe("");
// Multiple parameters
params.set("goal", "module");
expect(params.has("goal")).toBe(true);
expect(params.get("goal")).toBe("module");
expect([...params]).toEqual([
["charset", "utf-8"],
["goal", "module"],
]);
expect(JSON.stringify(mime)).toBe(JSON.stringify("text/javascript;charset=utf-8;goal=module"));
expect(`${mime}`).toBe("text/javascript;charset=utf-8;goal=module");
expect(mime.essence).toBe("text/javascript");
expect(`${mime.params}`).toBe("charset=utf-8;goal=module");
// Invalid parameter name
expect(() => {
params.set(`${WHITESPACES}goal`, "module");
}).toThrow(/The MIME syntax for a parameter name in/);
// Updating a parameter
params.set("charset", "iso-8859-1");
expect(params.has("charset")).toBe(true);
expect(params.get("charset")).toBe("iso-8859-1");
expect([...params]).toEqual([
["charset", "iso-8859-1"],
["goal", "module"],
]);
expect(JSON.stringify(mime)).toBe(JSON.stringify("text/javascript;charset=iso-8859-1;goal=module"));
expect(`${mime}`).toBe("text/javascript;charset=iso-8859-1;goal=module");
expect(mime.essence).toBe("text/javascript");
// Deleting a parameter
params.delete("charset");
expect(params.has("charset")).toBe(false);
expect(params.get("charset")).toBe(null);
expect([...params]).toEqual([["goal", "module"]]);
expect(JSON.stringify(mime)).toBe(JSON.stringify("text/javascript;goal=module"));
expect(`${mime}`).toBe("text/javascript;goal=module");
expect(mime.essence).toBe("text/javascript");
// Empty parameter value
params.set("x", "");
expect(params.has("x")).toBe(true);
expect(params.get("x")).toBe("");
expect([...params]).toEqual([
["goal", "module"],
["x", ""],
]);
expect(JSON.stringify(mime)).toBe(JSON.stringify('text/javascript;goal=module;x=""'));
expect(`${mime}`).toBe('text/javascript;goal=module;x=""');
expect(mime.essence).toBe("text/javascript");
});
test("invalid parameter names", () => {
const mime = new MIMEType("text/javascript");
const params = mime.params;
expect(() => params.set("", "x")).toThrow(/parameter name/i);
expect(() => params.set("=", "x")).toThrow(/parameter name/i);
expect(() => params.set("x=", "x")).toThrow(/parameter name/i);
expect(() => params.set("=x", "x")).toThrow(/parameter name/i);
expect(() => params.set(`${NOT_HTTP_TOKEN_CODE_POINT}=`, "x")).toThrow(/parameter name/i);
expect(() => params.set(`${NOT_HTTP_TOKEN_CODE_POINT}x`, "x")).toThrow(/parameter name/i);
expect(() => params.set(`x${NOT_HTTP_TOKEN_CODE_POINT}`, "x")).toThrow(/parameter name/i);
});
test("invalid parameter values", () => {
const mime = new MIMEType("text/javascript");
const params = mime.params;
expect(() => params.set("x", `${NOT_HTTP_QUOTED_STRING_CODE_POINT};`)).toThrow(/parameter value/i);
expect(() => params.set("x", `${NOT_HTTP_QUOTED_STRING_CODE_POINT}x`)).toThrow(/parameter value/i);
expect(() => params.set("x", `x${NOT_HTTP_QUOTED_STRING_CODE_POINT}`)).toThrow(/parameter value/i);
});
});
test("Exact match with node", () => {
const result = Bun.spawnSync({
cmd: [bunExe(), import.meta.dir + "/exact/mime-test.js"],
});
expect(result.stderr.toString("utf-8")).toBe("");
expect(result.exitCode).toBe(0);
// exact output on v23.4.0
expect(result.stdout.toString("utf-8")).toMatchInlineSnapshot(`
"=== BASIC PROPERTIES AND STRING CONVERSION ===
mime1: application/ecmascript
JSON.stringify: "application/ecmascript"
essence: application/ecmascript
type: application
subtype: ecmascript
params empty: true
params.has("not found"): false
params.get("not found"): true
=== TYPE PROPERTY MANIPULATION ===
Original: application/javascript
After type change: text/javascript
essence: text/javascript
Error on empty type as expected
Error on invalid type as expected
=== SUBTYPE PROPERTY MANIPULATION ===
Original: text/plain
After subtype change: text/javascript
Error on empty subtype as expected
Error on invalid subtype as expected
=== PARAMETERS MANIPULATION ===
params.has("charset"): true
params.get("charset"): utf-8
params entries length: 1
mime with charset: text/javascript;charset=utf-8
params.has("goal"): true
params.get("goal"): module
params entries length: 2
mime with multiple params: text/javascript;charset=utf-8;goal=module
updated charset: iso-8859-1
mime with updated charset: text/javascript;charset=iso-8859-1;goal=module
params.has("charset") after delete: false
params.get("charset") after delete: true
params entries length after delete: 1
mime after param delete: text/javascript;goal=module
params.has("x"): true
params.get("x"): empty string
mime with empty param: text/javascript;goal=module;x=""
=== PARAMETER CASE SENSITIVITY ===
mime5: text/javascript;charset=UTF-8
mime5.params.get("CHARSET"): true
mime5.params.get("charset"): UTF-8
mime5.params.has("CHARSET"): false
mime5.params.has("charset"): true
mime5.params.has("abc"): false
mime5.params.has("def"): false
mime5.params.get("CHARSET") after set: UTF-8
mime5.params.has("CHARSET") after set: true
=== QUOTED PARAMETER VALUES ===
mime6: text/plain;charset=utf-8
mime6.params.get("charset"): utf-8
mime with filename: text/javascript;goal=module;x="";filename="file with spaces.txt"
=== INVALID PARAMETERS ===
Error on empty param name as expected
Error on invalid param name as expected
Error on invalid param value as expected
=== PARAMS ITERATION ===
Iterating params.entries():
charset: utf-8
format: flowed
Iterating params.keys():
charset
format
Iterating params.values():
utf-8
flowed
Iterating params directly:
charset: utf-8
format: flowed
=== PARSING EDGE CASES ===
mime8: text/plain;charset=utf-8;goal=module
Has empty param: false
mime9: text/plain;charset=utf-8
mime9 charset: utf-8
=== TO STRING AND TO JSON ===
toString(): text/plain;charset=utf-8
toJSON(): text/plain;charset=utf-8
params toString(): charset=utf-8
params toJSON(): charset=utf-8
=== BASIC MIMEPARAMS OPERATIONS ===
New params empty: true
params.has("charset"): true
params.get("charset"): utf-8
params entries length: 1
params toString(): charset=utf-8
=== CASE SENSITIVITY ===
params.has("CHARSET"): false
params.get("CHARSET"): true
After setting CHARSET, params.has("CHARSET"): true
After setting CHARSET, params.get("CHARSET"): iso-8859-1
params.has("charset"): true
params.get("charset"): utf-8
params entries length: 2
params toString(): charset=utf-8;CHARSET=iso-8859-1
=== DELETE OPERATION ===
After delete, params.has("charset"): false
After delete, params.get("charset"): true
params.has("CHARSET"): true
params entries length: 1
params toString(): CHARSET=iso-8859-1
=== MULTIPLE PARAMETERS ===
params entries length: 3
params toString(): CHARSET=iso-8859-1;format=flowed;delsp=yes
=== QUOTED VALUES ===
params.get("filename"): file with spaces.txt
params toString(): CHARSET=iso-8859-1;format=flowed;delsp=yes;filename="file with spaces.txt"
=== EMPTY VALUES ===
params.has("empty"): true
params.get("empty"): empty string
params toString() with empty value: CHARSET=iso-8859-1;format=flowed;delsp=yes;filename="file with spaces.txt";empty=""
=== ESCAPE SEQUENCES IN QUOTED VALUES ===
params.get("path"): C:\\Program Files\\App
params toString() with backslashes: CHARSET=iso-8859-1;format=flowed;delsp=yes;filename="file with spaces.txt";empty="";path="C:\\\\Program Files\\\\App"
=== SPECIAL CHARACTERS ===
params.get("test"): !#$%&'*+-.^_\`|~
params toString() with special chars: CHARSET=iso-8859-1;format=flowed;delsp=yes;filename="file with spaces.txt";empty="";path="C:\\\\Program Files\\\\App";test=!#$%&'*+-.^_\`|~
=== ERROR CASES ===
Empty name error: TypeError
Invalid name error: TypeError
Invalid value error: TypeError
=== ITERATION METHODS ===
Keys:
CHARSET
format
delsp
filename
empty
path
test
Values:
iso-8859-1
flowed
yes
file with spaces.txt
C:\\Program Files\\App
!#$%&'*+-.^_\`|~
Entries:
CHARSET: iso-8859-1
format: flowed
delsp: yes
filename: file with spaces.txt
empty:
path: C:\\Program Files\\App
test: !#$%&'*+-.^_\`|~
Direct iteration:
CHARSET: iso-8859-1
format: flowed
delsp: yes
filename: file with spaces.txt
empty:
path: C:\\Program Files\\App
test: !#$%&'*+-.^_\`|~
=== JSON SERIALIZATION ===
params.toJSON(): CHARSET=iso-8859-1;format=flowed;delsp=yes;filename="file with spaces.txt";empty="";path="C:\\\\Program Files\\\\App";test=!#$%&'*+-.^_\`|~
JSON.stringify(params): "CHARSET=iso-8859-1;format=flowed;delsp=yes;filename=\\"file with spaces.txt\\";empty=\\"\\";path=\\"C:\\\\\\\\Program Files\\\\\\\\App\\";test=!#$%&'*+-.^_\`|~"
=== CLONE AND MODIFY ===
Original params: charset=utf-8;boundary=boundary
Cloned params: charset=iso-8859-1;boundary=boundary
"
`);
});