Skip to content

Commit 2734d49

Browse files
authored
Merge branch 'main' into ali/test-tls-set-ciphers-error
2 parents e85fdf5 + 500199f commit 2734d49

File tree

15 files changed

+1387
-10
lines changed

15 files changed

+1387
-10
lines changed

scripts/runner.node.mjs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -748,7 +748,8 @@ async function spawnSafe(options) {
748748
(error = /(Internal assertion failure)/i.exec(buffer)) ||
749749
(error = /(Illegal instruction) at address/i.exec(buffer)) ||
750750
(error = /panic: (.*) at address/i.exec(buffer)) ||
751-
(error = /oh no: Bun has crashed/i.exec(buffer))
751+
(error = /oh no: Bun has crashed/i.exec(buffer)) ||
752+
(error = /(ERROR: AddressSanitizer)/.exec(buffer))
752753
) {
753754
const [, message] = error || [];
754755
error = message ? message.split("\n")[0].toLowerCase() : "crash";
@@ -1864,9 +1865,15 @@ function getExitCode(outcome) {
18641865

18651866
// A flaky segfault, sigtrap, or sigill must never be ignored.
18661867
// If it happens in CI, it will happen to our users.
1868+
// Flaky AddressSanitizer errors cannot be ignored since they still represent real bugs.
18671869
function isAlwaysFailure(error) {
18681870
error = ((error || "") + "").toLowerCase().trim();
1869-
return error.includes("segmentation fault") || error.includes("sigill") || error.includes("sigtrap");
1871+
return (
1872+
error.includes("segmentation fault") ||
1873+
error.includes("illegal instruction") ||
1874+
error.includes("sigtrap") ||
1875+
error.includes("error: addresssanitizer")
1876+
);
18701877
}
18711878

18721879
/**

src/bun.js/api/bun/dns_resolver.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2536,7 +2536,7 @@ pub const DNSResolver = struct {
25362536
}
25372537

25382538
break :brk RecordType.map.getWithEql(record_type_str.getZigString(globalThis), JSC.ZigString.eqlComptime) orelse {
2539-
return globalThis.throwInvalidArgumentType("resolve", "record", "one of: A, AAAA, ANY, CAA, CNAME, MX, NS, PTR, SOA, SRV, TXT");
2539+
return globalThis.throwInvalidArgumentPropertyValue("record", "one of: A, AAAA, ANY, CAA, CNAME, MX, NS, PTR, SOA, SRV, TXT", record_type_value);
25402540
};
25412541
};
25422542

src/bun.js/bindings/webcore/JSDOMException.cpp

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,30 @@ template<> JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSDOMExceptionDOMConstru
129129
EnsureStillAliveScope argument0 = callFrame->argument(0);
130130
auto message = argument0.value().isUndefined() ? emptyString() : convert<IDLDOMString>(*lexicalGlobalObject, argument0.value());
131131
RETURN_IF_EXCEPTION(throwScope, {});
132+
133+
String name = "Error"_s;
134+
JSValue cause = {};
135+
132136
EnsureStillAliveScope argument1 = callFrame->argument(1);
133-
auto name = argument1.value().isUndefined() ? "Error"_s : convert<IDLDOMString>(*lexicalGlobalObject, argument1.value());
134-
RETURN_IF_EXCEPTION(throwScope, {});
137+
if (JSObject* optionsObj = argument1.value().getObject()) {
138+
// Get name from options
139+
JSValue nameValue = optionsObj->get(lexicalGlobalObject, vm.propertyNames->name);
140+
RETURN_IF_EXCEPTION(throwScope, {});
141+
if (!nameValue.isUndefined())
142+
name = convert<IDLDOMString>(*lexicalGlobalObject, nameValue);
143+
RETURN_IF_EXCEPTION(throwScope, {});
144+
145+
// Get cause from options
146+
auto causeValue = optionsObj->getIfPropertyExists(lexicalGlobalObject, vm.propertyNames->cause);
147+
RETURN_IF_EXCEPTION(throwScope, {});
148+
if (causeValue) {
149+
cause = causeValue;
150+
}
151+
} else if (!argument1.value().isUndefined()) {
152+
name = convert<IDLDOMString>(*lexicalGlobalObject, argument1.value());
153+
RETURN_IF_EXCEPTION(throwScope, {});
154+
}
155+
135156
auto object = DOMException::create(WTFMove(message), WTFMove(name));
136157
if constexpr (IsExceptionOr<decltype(object)>)
137158
RETURN_IF_EXCEPTION(throwScope, {});
@@ -141,6 +162,9 @@ template<> JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSDOMExceptionDOMConstru
141162
RETURN_IF_EXCEPTION(throwScope, {});
142163
setSubclassStructureIfNeeded<DOMException>(lexicalGlobalObject, callFrame, asObject(jsValue));
143164
RETURN_IF_EXCEPTION(throwScope, {});
165+
if (!cause.isEmpty()) {
166+
jsValue.getObject()->putDirect(vm, vm.propertyNames->cause, cause, JSC::PropertyAttribute::DontEnum | 0);
167+
}
144168
return JSValue::encode(jsValue);
145169
}
146170
JSC_ANNOTATE_HOST_FUNCTION(JSDOMExceptionDOMConstructorConstruct, JSDOMExceptionDOMConstructor::construct);

src/bun.js/node/node_fs.zig

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4007,7 +4007,7 @@ pub const NodeFS = struct {
40074007
return .{ .result = .{ .none = {} } };
40084008
}
40094009
return .{
4010-
.result = .{ .string = bun.String.createFromOSPath(strings.withoutNTPrefix(bun.OSPathChar, path)) },
4010+
.result = .{ .string = bun.String.createFromOSPath(path) },
40114011
};
40124012
},
40134013
}
@@ -4029,6 +4029,20 @@ pub const NodeFS = struct {
40294029
working_mem[i] = std.fs.path.sep;
40304030
switch (err.getErrno()) {
40314031
.EXIST => {
4032+
// On Windows, this may happen if trying to mkdir replacing a file
4033+
if (bun.Environment.isWindows) {
4034+
switch (bun.sys.directoryExistsAt(bun.invalid_fd, parent)) {
4035+
.err => {},
4036+
.result => |res| {
4037+
// is a directory. break.
4038+
if (!res) return .{ .err = .{
4039+
.errno = @intFromEnum(bun.sys.E.NOTDIR),
4040+
.syscall = .mkdir,
4041+
.path = this.osPathIntoSyncErrorBuf(strings.withoutNTPrefix(bun.OSPathChar, path[0..len])),
4042+
} };
4043+
},
4044+
}
4045+
}
40324046
// Handle race condition
40334047
break;
40344048
},
@@ -4106,7 +4120,7 @@ pub const NodeFS = struct {
41064120
return .{ .result = .{ .none = {} } };
41074121
}
41084122
return .{
4109-
.result = .{ .string = bun.String.createFromOSPath(strings.withoutNTPrefix(bun.OSPathChar, working_mem[0..first_match])) },
4123+
.result = .{ .string = bun.String.createFromOSPath(working_mem[0..first_match]) },
41104124
};
41114125
}
41124126

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import { describe, it, expect } from "bun:test";
2+
import assert, { AssertionError } from "assert";
3+
4+
describe("assert.rejects", () => {
5+
it("accepts a rejecting function", async () => {
6+
const rejectingFn = async () => {
7+
throw new AssertionError({
8+
message: "Failed",
9+
operator: "fail",
10+
});
11+
};
12+
13+
await expect(
14+
assert.rejects(rejectingFn, {
15+
name: "AssertionError",
16+
message: "Failed",
17+
}),
18+
).resolves.toBeUndefined();
19+
});
20+
21+
it("accepts a rejecting promise", async () => {
22+
const rejectingPromise = Promise.reject(
23+
new AssertionError({
24+
message: "Failed",
25+
operator: "fail",
26+
}),
27+
);
28+
29+
await expect(
30+
assert.rejects(rejectingPromise, {
31+
name: "AssertionError",
32+
message: "Failed",
33+
}),
34+
).resolves.toBeUndefined();
35+
});
36+
37+
it("handles thenable objects when cast to Promise", async () => {
38+
// Create a Promise from a thenable to make TypeScript happy
39+
const thenablePromise = Promise.resolve().then(() => {
40+
return Promise.reject({ name: "CustomError" });
41+
});
42+
43+
await expect(assert.rejects(thenablePromise, { name: "CustomError" })).resolves.toBeUndefined();
44+
});
45+
46+
it("rejects when promise resolves instead of rejecting", async () => {
47+
await expect(assert.rejects(Promise.resolve())).rejects.toMatchObject({
48+
message: "Missing expected rejection.",
49+
});
50+
});
51+
52+
it("rejects with correct error when validation function returns non-boolean", async () => {
53+
const err = new Error("foobar");
54+
const validate = () => "baz";
55+
56+
await expect(assert.rejects(Promise.reject(err), validate)).rejects.toMatchObject({
57+
message: expect.stringContaining(
58+
'The "validate" validation function is expected to return "true". Received \'baz\'',
59+
),
60+
actual: err,
61+
expected: validate,
62+
name: "AssertionError",
63+
operator: "rejects",
64+
});
65+
});
66+
});
67+
68+
describe("assert.doesNotReject", () => {
69+
it("resolves when promise resolves", async () => {
70+
await expect(assert.doesNotReject(Promise.resolve())).resolves.toBeUndefined();
71+
});
72+
73+
it("resolves when async function resolves", async () => {
74+
await expect(assert.doesNotReject(async () => {})).resolves.toBeUndefined();
75+
});
76+
77+
it("handles thenable objects with proper Promise cast", async () => {
78+
// Create a proper Promise from a thenable pattern
79+
const thenablePromise = Promise.resolve().then(() => {
80+
return "success";
81+
});
82+
83+
await expect(assert.doesNotReject(thenablePromise)).resolves.toBeUndefined();
84+
});
85+
86+
it("documents Node.js behavior with invalid thenables", async () => {
87+
const invalidThenable = {
88+
then: (fulfill, reject) => {
89+
fulfill();
90+
},
91+
};
92+
93+
await expect(assert.doesNotReject(invalidThenable as any)).rejects.toMatchObject({
94+
message: expect.stringContaining('The "promiseFn" argument must be of type function or an instance of Promise'),
95+
});
96+
});
97+
98+
it("rejects when promise rejects", async () => {
99+
await expect(assert.doesNotReject(Promise.reject(new Error("Failed")))).rejects.toMatchObject({
100+
message: expect.stringContaining("Got unwanted rejection"),
101+
operator: "doesNotReject",
102+
});
103+
});
104+
105+
it("rejects when async function rejects", async () => {
106+
const rejectingFn = async () => {
107+
throw new Error("Failed");
108+
};
109+
110+
await expect(assert.doesNotReject(rejectingFn)).rejects.toMatchObject({
111+
message: expect.stringContaining("Got unwanted rejection"),
112+
operator: "doesNotReject",
113+
});
114+
});
115+
116+
it("rejects with invalid argument types", async () => {
117+
await expect(assert.doesNotReject(123 as any)).rejects.toMatchObject({
118+
message: expect.stringContaining('The "promiseFn" argument must be of type function or an instance of Promise'),
119+
});
120+
});
121+
});
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { describe, expect, it } from "bun:test";
2+
3+
describe("DOMException in Node.js environment", () => {
4+
it("exists globally", () => {
5+
expect(typeof DOMException).toBe("function");
6+
});
7+
8+
it("creates instance with message and name", () => {
9+
const error = new DOMException("Error message", "TestError");
10+
expect(error).toBeInstanceOf(DOMException);
11+
expect(error.message).toBe("Error message");
12+
expect(error.name).toBe("TestError");
13+
expect(error instanceof Error).toBe(true);
14+
});
15+
16+
it("uses default name when only message is provided", () => {
17+
const error = new DOMException("Error message");
18+
expect(error.message).toBe("Error message");
19+
expect(error.name).toBe("Error");
20+
});
21+
22+
it("creates instance with options object", () => {
23+
const cause = { reason: "test reason" };
24+
const error = new DOMException("Error with cause", { name: "CauseError", cause });
25+
26+
expect(error.message).toBe("Error with cause");
27+
expect(error.name).toBe("CauseError");
28+
expect(error.cause).toBe(cause);
29+
});
30+
31+
it("has standard error constants", () => {
32+
expect(DOMException.INDEX_SIZE_ERR).toBe(1);
33+
expect(DOMException.DOMSTRING_SIZE_ERR).toBe(2);
34+
expect(DOMException.HIERARCHY_REQUEST_ERR).toBe(3);
35+
expect(DOMException.WRONG_DOCUMENT_ERR).toBe(4);
36+
expect(DOMException.INVALID_CHARACTER_ERR).toBe(5);
37+
expect(DOMException.NO_DATA_ALLOWED_ERR).toBe(6);
38+
expect(DOMException.NO_MODIFICATION_ALLOWED_ERR).toBe(7);
39+
expect(DOMException.NOT_FOUND_ERR).toBe(8);
40+
expect(DOMException.NOT_SUPPORTED_ERR).toBe(9);
41+
expect(DOMException.INUSE_ATTRIBUTE_ERR).toBe(10);
42+
expect(DOMException.INVALID_STATE_ERR).toBe(11);
43+
expect(DOMException.SYNTAX_ERR).toBe(12);
44+
expect(DOMException.INVALID_MODIFICATION_ERR).toBe(13);
45+
expect(DOMException.NAMESPACE_ERR).toBe(14);
46+
expect(DOMException.INVALID_ACCESS_ERR).toBe(15);
47+
expect(DOMException.VALIDATION_ERR).toBe(16);
48+
expect(DOMException.TYPE_MISMATCH_ERR).toBe(17);
49+
expect(DOMException.SECURITY_ERR).toBe(18);
50+
expect(DOMException.NETWORK_ERR).toBe(19);
51+
expect(DOMException.ABORT_ERR).toBe(20);
52+
expect(DOMException.URL_MISMATCH_ERR).toBe(21);
53+
expect(DOMException.QUOTA_EXCEEDED_ERR).toBe(22);
54+
expect(DOMException.TIMEOUT_ERR).toBe(23);
55+
expect(DOMException.INVALID_NODE_TYPE_ERR).toBe(24);
56+
expect(DOMException.DATA_CLONE_ERR).toBe(25);
57+
});
58+
59+
// TODO: missing stack trace on DOMException
60+
it.failing("inherits prototype properties from Error", () => {
61+
const error = new DOMException("Test error");
62+
expect(error.toString()).toBe("Error: Test error");
63+
expect(error.stack).toBeDefined();
64+
});
65+
66+
it("has proper instance properties", () => {
67+
const error = new DOMException("Test error", "TestName");
68+
expect(error.code).toBe(0); // Default code for custom names
69+
70+
// Create an exception with known code
71+
const abortError = new DOMException("Aborted", "AbortError");
72+
expect(abortError.code).toBe(20); // ABORT_ERR
73+
});
74+
});

0 commit comments

Comments
 (0)