mirror of
https://github.com/oven-sh/bun
synced 2026-03-02 21:41:03 +01:00
Compare commits
5 Commits
claude/rev
...
claude/fix
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e44246951d | ||
|
|
7ebfdf97a8 | ||
|
|
4cd3b241bc | ||
|
|
cae67a17e2 | ||
|
|
a394063a7d |
@@ -4,7 +4,7 @@ register_repository(
|
||||
REPOSITORY
|
||||
oven-sh/mimalloc
|
||||
COMMIT
|
||||
989115cefb6915baa13788cb8252d83aac5330ad
|
||||
ffa38ab8ac914f9eb7af75c1f8ad457643dc14f2
|
||||
)
|
||||
|
||||
set(MIMALLOC_CMAKE_ARGS
|
||||
|
||||
@@ -2,7 +2,7 @@ option(WEBKIT_VERSION "The version of WebKit to use")
|
||||
option(WEBKIT_LOCAL "If a local version of WebKit should be used instead of downloading")
|
||||
|
||||
if(NOT WEBKIT_VERSION)
|
||||
set(WEBKIT_VERSION 0e6527f24783ea832fa58f696437829cdcbc3c7c)
|
||||
set(WEBKIT_VERSION cc5e0bddf7eae1d820cf673158845fe9bd83c094)
|
||||
endif()
|
||||
|
||||
# Use preview build URL for Windows ARM64 until the fix is merged to main
|
||||
|
||||
@@ -73,9 +73,11 @@ async function buildRootModule(dryRun?: boolean) {
|
||||
});
|
||||
// Create placeholder scripts that print an error message if postinstall hasn't run.
|
||||
// On Unix, these are executed as shell scripts despite the .exe extension.
|
||||
// On Windows, npm creates .cmd wrappers that would fail anyway if the binary isn't valid.
|
||||
const placeholderScript = `#!/bin/sh
|
||||
echo "Error: Bun's postinstall script was not run." >&2
|
||||
// Do NOT add a shebang (#!/bin/sh) here — npm's cmd-shim reads shebangs to generate
|
||||
// .ps1/.cmd wrappers BEFORE postinstall runs, and bakes the interpreter path in.
|
||||
// A #!/bin/sh shebang breaks Windows because the wrappers reference /bin/sh which
|
||||
// doesn't exist, even after postinstall replaces the placeholder with the real binary.
|
||||
const placeholderScript = `echo "Error: Bun's postinstall script was not run." >&2
|
||||
echo "" >&2
|
||||
echo "This occurs when using --ignore-scripts during installation, or when using a" >&2
|
||||
echo "package manager like pnpm that does not run postinstall scripts by default." >&2
|
||||
|
||||
@@ -591,7 +591,7 @@ pub fn onProcessExit(this: *Subprocess, process: *Process, status: bun.spawn.Sta
|
||||
if (this_jsvalue != .zero) {
|
||||
if (jsc.Codegen.JSSubprocess.stdinGetCached(this_jsvalue)) |existing_value| {
|
||||
if (existing_value.isCell()) {
|
||||
if (stdin == null) {
|
||||
if (stdin == null and !this.flags.has_stdin_destructor_called) {
|
||||
// TODO: review this cast
|
||||
stdin = @ptrCast(@alignCast(jsc.WebCore.FileSink.JSSink.fromJS(existing_value)));
|
||||
}
|
||||
|
||||
@@ -21,20 +21,17 @@ services:
|
||||
start_period: 5s
|
||||
|
||||
postgres_tls:
|
||||
image: postgres:15
|
||||
environment:
|
||||
POSTGRES_HOST_AUTH_METHOD: trust
|
||||
POSTGRES_USER: postgres
|
||||
volumes:
|
||||
- ./init-scripts/postgres:/docker-entrypoint-initdb.d:ro
|
||||
- ../js/sql/docker-tls/server.crt:/etc/postgresql/ssl/server.crt:ro
|
||||
- ../js/sql/docker-tls/server.key:/etc/postgresql/ssl/server.key:ro
|
||||
build:
|
||||
context: ../js/sql/docker-tls
|
||||
dockerfile: Dockerfile
|
||||
image: bun-postgres-tls:local
|
||||
ports:
|
||||
- target: 5432
|
||||
published: 0
|
||||
protocol: tcp
|
||||
command: >
|
||||
postgres
|
||||
-c hba_file=/etc/postgresql/pg_hba.conf
|
||||
-c ssl=on
|
||||
-c ssl_cert_file=/etc/postgresql/ssl/server.crt
|
||||
-c ssl_key_file=/etc/postgresql/ssl/server.key
|
||||
@@ -47,7 +44,8 @@ services:
|
||||
interval: 1h # Effectively disable after startup
|
||||
timeout: 5s
|
||||
retries: 30
|
||||
start_period: 5s
|
||||
start_period: 60s
|
||||
start_interval: 1s
|
||||
|
||||
postgres_auth:
|
||||
image: postgres:15
|
||||
|
||||
@@ -109,8 +109,8 @@ class DockerComposeHelper {
|
||||
return;
|
||||
}
|
||||
|
||||
// Build the service if needed (for services like mysql_tls that need building)
|
||||
if (service === "mysql_tls" || service === "redis_unified") {
|
||||
// Build the service if needed (for services like mysql_tls, postgres_tls that need building)
|
||||
if (service === "mysql_tls" || service === "redis_unified" || service === "postgres_tls") {
|
||||
const buildResult = await this.exec(["build", service]);
|
||||
if (buildResult.exitCode !== 0) {
|
||||
throw new Error(`Failed to build service ${service}: ${buildResult.stderr}`);
|
||||
|
||||
@@ -51,16 +51,20 @@ RUN chmod +x /docker-entrypoint-initdb.d/init-users-db.sh
|
||||
|
||||
# Create pg_hba.conf with SSL requirements
|
||||
RUN mkdir -p /etc/postgresql && touch /etc/postgresql/pg_hba.conf && \
|
||||
echo "hostssl all postgres 127.0.0.1/32 trust" >> /etc/postgresql/pg_hba.conf && \
|
||||
echo "hostssl all bun_sql_test 127.0.0.1/32 trust" >> /etc/postgresql/pg_hba.conf && \
|
||||
echo "hostssl all bun_sql_test_md5 127.0.0.1/32 md5" >> /etc/postgresql/pg_hba.conf && \
|
||||
echo "hostssl all bun_sql_test_scram 127.0.0.1/32 scram-sha-256" >> /etc/postgresql/pg_hba.conf && \
|
||||
echo "hostssl all postgres ::1/128 trust" >> /etc/postgresql/pg_hba.conf && \
|
||||
echo "hostssl all bun_sql_test ::1/128 trust" >> /etc/postgresql/pg_hba.conf && \
|
||||
echo "hostssl all bun_sql_test_md5 ::1/128 md5" >> /etc/postgresql/pg_hba.conf && \
|
||||
echo "hostssl all bun_sql_test_scram ::1/128 scram-sha-256" >> /etc/postgresql/pg_hba.conf && \
|
||||
echo "hostssl replication all 127.0.0.1/32 trust" >> /etc/postgresql/pg_hba.conf && \
|
||||
echo "hostssl replication all ::1/128 trust" >> /etc/postgresql/pg_hba.conf && \
|
||||
echo "# Allow local socket connections for init scripts" >> /etc/postgresql/pg_hba.conf && \
|
||||
echo "local all postgres trust" >> /etc/postgresql/pg_hba.conf && \
|
||||
echo "local all all trust" >> /etc/postgresql/pg_hba.conf && \
|
||||
echo "# Remote TLS connections" >> /etc/postgresql/pg_hba.conf && \
|
||||
echo "hostssl all postgres 0.0.0.0/0 trust" >> /etc/postgresql/pg_hba.conf && \
|
||||
echo "hostssl all bun_sql_test 0.0.0.0/0 trust" >> /etc/postgresql/pg_hba.conf && \
|
||||
echo "hostssl all bun_sql_test_md5 0.0.0.0/0 md5" >> /etc/postgresql/pg_hba.conf && \
|
||||
echo "hostssl all bun_sql_test_scram 0.0.0.0/0 scram-sha-256" >> /etc/postgresql/pg_hba.conf && \
|
||||
echo "hostssl all postgres ::/0 trust" >> /etc/postgresql/pg_hba.conf && \
|
||||
echo "hostssl all bun_sql_test ::/0 trust" >> /etc/postgresql/pg_hba.conf && \
|
||||
echo "hostssl all bun_sql_test_md5 ::/0 md5" >> /etc/postgresql/pg_hba.conf && \
|
||||
echo "hostssl all bun_sql_test_scram ::/0 scram-sha-256" >> /etc/postgresql/pg_hba.conf && \
|
||||
echo "hostssl replication all 0.0.0.0/0 trust" >> /etc/postgresql/pg_hba.conf && \
|
||||
echo "hostssl replication all ::/0 trust" >> /etc/postgresql/pg_hba.conf && \
|
||||
echo "host all all all reject" >> /etc/postgresql/pg_hba.conf
|
||||
|
||||
# Configure PostgreSQL for SSL
|
||||
|
||||
@@ -1,280 +1,241 @@
|
||||
import { postgres, randomUUIDv7, SQL, sql } from "bun";
|
||||
import { SQL, randomUUIDv7 } from "bun";
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { getSecret } from "harness";
|
||||
import { describeWithContainer, isDockerEnabled } from "harness";
|
||||
|
||||
const TLS_POSTGRES_DATABASE_URL = getSecret("TLS_POSTGRES_DATABASE_URL");
|
||||
const PG_TRANSACTION_POOL_SUPABASE_URL = getSecret("PG_TRANSACTION_POOL_SUPABASE_URL");
|
||||
|
||||
for (const options of [
|
||||
{
|
||||
url: TLS_POSTGRES_DATABASE_URL,
|
||||
tls: true,
|
||||
adapter: "postgres",
|
||||
max: 1,
|
||||
bigint: true,
|
||||
prepare: true,
|
||||
transactionPool: false,
|
||||
},
|
||||
{
|
||||
url: PG_TRANSACTION_POOL_SUPABASE_URL,
|
||||
tls: true,
|
||||
adapter: "postgres",
|
||||
max: 1,
|
||||
bigint: true,
|
||||
prepare: false,
|
||||
transactionPool: true,
|
||||
},
|
||||
{
|
||||
url: TLS_POSTGRES_DATABASE_URL,
|
||||
tls: true,
|
||||
adapter: "postgres",
|
||||
max: 1,
|
||||
bigint: true,
|
||||
prepare: false,
|
||||
transactionPool: false,
|
||||
},
|
||||
] satisfies (Bun.SQL.Options & { transactionPool?: boolean })[]) {
|
||||
if (options.url === undefined) {
|
||||
console.log("SKIPPING TEST", JSON.stringify(options), "BECAUSE MISSING THE URL SECRET");
|
||||
continue;
|
||||
}
|
||||
|
||||
describe.concurrent(
|
||||
`${options.transactionPool ? "Transaction Pooling" : `Prepared Statements (${options.prepare ? "on" : "off"})`}`,
|
||||
() => {
|
||||
test("default sql", async () => {
|
||||
expect(sql.reserve).toBeDefined();
|
||||
expect(sql.options).toBeDefined();
|
||||
expect(sql[Symbol.asyncDispose]).toBeDefined();
|
||||
expect(sql.begin).toBeDefined();
|
||||
expect(sql.beginDistributed).toBeDefined();
|
||||
expect(sql.distributed).toBeDefined();
|
||||
expect(sql.unsafe).toBeDefined();
|
||||
expect(sql.end).toBeDefined();
|
||||
expect(sql.close).toBeDefined();
|
||||
expect(sql.transaction).toBeDefined();
|
||||
expect(sql.distributed).toBeDefined();
|
||||
expect(sql.unsafe).toBeDefined();
|
||||
expect(sql.commitDistributed).toBeDefined();
|
||||
expect(sql.rollbackDistributed).toBeDefined();
|
||||
});
|
||||
test("default postgres", async () => {
|
||||
expect(postgres.reserve).toBeDefined();
|
||||
expect(postgres.options).toBeDefined();
|
||||
expect(postgres[Symbol.asyncDispose]).toBeDefined();
|
||||
expect(postgres.begin).toBeDefined();
|
||||
expect(postgres.beginDistributed).toBeDefined();
|
||||
expect(postgres.distributed).toBeDefined();
|
||||
expect(postgres.unsafe).toBeDefined();
|
||||
expect(postgres.end).toBeDefined();
|
||||
expect(postgres.close).toBeDefined();
|
||||
expect(postgres.transaction).toBeDefined();
|
||||
expect(postgres.distributed).toBeDefined();
|
||||
expect(postgres.unsafe).toBeDefined();
|
||||
expect(postgres.commitDistributed).toBeDefined();
|
||||
expect(postgres.rollbackDistributed).toBeDefined();
|
||||
});
|
||||
test("tls (explicit)", async () => {
|
||||
await using sql = new SQL(options);
|
||||
const [{ one, two }] = await sql`SELECT 1 as one, '2' as two`;
|
||||
expect(one).toBe(1);
|
||||
expect(two).toBe("2");
|
||||
await sql.close();
|
||||
});
|
||||
|
||||
test("Throws on illegal transactions", async () => {
|
||||
await using sql = new SQL({ ...options, max: 2 });
|
||||
const error = await sql`BEGIN`.catch(e => e);
|
||||
expect(error).toBeInstanceOf(SQL.SQLError);
|
||||
expect(error).toBeInstanceOf(SQL.PostgresError);
|
||||
return expect(error.code).toBe("ERR_POSTGRES_UNSAFE_TRANSACTION");
|
||||
});
|
||||
|
||||
test.skipIf(options.transactionPool)("Transaction throws", async () => {
|
||||
await using sql = new SQL(options);
|
||||
const random_name = ("t_" + randomUUIDv7("hex").replaceAll("-", "")).toLowerCase();
|
||||
|
||||
await sql`CREATE TEMPORARY TABLE IF NOT EXISTS ${sql(random_name)} (a int)`;
|
||||
expect(
|
||||
await sql
|
||||
.begin(async sql => {
|
||||
await sql`insert into ${sql(random_name)} values(1)`;
|
||||
await sql`insert into ${sql(random_name)} values('hej')`;
|
||||
})
|
||||
.catch(e => e.errno),
|
||||
).toBe("22P02");
|
||||
});
|
||||
|
||||
test.skipIf(options.transactionPool)("Transaction rolls back", async () => {
|
||||
await using sql = new SQL(options);
|
||||
const random_name = ("t_" + randomUUIDv7("hex").replaceAll("-", "")).toLowerCase();
|
||||
|
||||
await sql`CREATE TEMPORARY TABLE IF NOT EXISTS ${sql(random_name)} (a int)`;
|
||||
|
||||
await sql
|
||||
.begin(async sql => {
|
||||
await sql`insert into ${sql(random_name)} values(1)`;
|
||||
await sql`insert into ${sql(random_name)} values('hej')`;
|
||||
})
|
||||
.catch(() => {
|
||||
/* ignore */
|
||||
if (!isDockerEnabled()) {
|
||||
test.skip("skipping TLS SQL tests - Docker is not available", () => {});
|
||||
} else {
|
||||
describeWithContainer(
|
||||
"PostgreSQL TLS",
|
||||
{
|
||||
image: "postgres_tls",
|
||||
},
|
||||
container => {
|
||||
// Test with prepared statements on and off
|
||||
for (const prepare of [true, false]) {
|
||||
describe(`prepared: ${prepare}`, () => {
|
||||
const getOptions = (): Bun.SQL.Options => ({
|
||||
url: `postgres://postgres@${container.host}:${container.port}/bun_sql_test`,
|
||||
tls: true,
|
||||
adapter: "postgres",
|
||||
max: 1,
|
||||
bigint: true,
|
||||
prepare,
|
||||
});
|
||||
|
||||
expect((await sql`select a from ${sql(random_name)}`).count).toBe(0);
|
||||
});
|
||||
test("tls (explicit)", async () => {
|
||||
await container.ready;
|
||||
await using sql = new SQL(getOptions());
|
||||
const [{ one, two }] = await sql`SELECT 1 as one, '2' as two`;
|
||||
expect(one).toBe(1);
|
||||
expect(two).toBe("2");
|
||||
});
|
||||
|
||||
test.skipIf(options.transactionPool)("Transaction throws on uncaught savepoint", async () => {
|
||||
await using sql = new SQL(options);
|
||||
const random_name = ("t_" + randomUUIDv7("hex").replaceAll("-", "")).toLowerCase();
|
||||
await sql`CREATE TEMPORARY TABLE IF NOT EXISTS ${sql(random_name)} (a int)`;
|
||||
expect(
|
||||
await sql
|
||||
.begin(async sql => {
|
||||
await sql`insert into ${sql(random_name)} values(1)`;
|
||||
await sql.savepoint(async sql => {
|
||||
await sql`insert into ${sql(random_name)} values(2)`;
|
||||
throw new Error("fail");
|
||||
});
|
||||
})
|
||||
.catch(err => err.message),
|
||||
).toBe("fail");
|
||||
});
|
||||
test("Throws on illegal transactions", async () => {
|
||||
await container.ready;
|
||||
await using sql = new SQL({ ...getOptions(), max: 2 });
|
||||
const error = await sql`BEGIN`.catch(e => e);
|
||||
expect(error).toBeInstanceOf(SQL.SQLError);
|
||||
expect(error).toBeInstanceOf(SQL.PostgresError);
|
||||
return expect(error.code).toBe("ERR_POSTGRES_UNSAFE_TRANSACTION");
|
||||
});
|
||||
|
||||
test.skipIf(options.transactionPool)("Transaction throws on uncaught named savepoint", async () => {
|
||||
await using sql = new SQL(options);
|
||||
const random_name = ("t_" + randomUUIDv7("hex").replaceAll("-", "")).toLowerCase();
|
||||
await sql`CREATE TEMPORARY TABLE IF NOT EXISTS ${sql(random_name)} (a int)`;
|
||||
expect(
|
||||
await sql
|
||||
.begin(async sql => {
|
||||
await sql`insert into ${sql(random_name)} values(1)`;
|
||||
await sql.savepoint("watpoint", async sql => {
|
||||
await sql`insert into ${sql(random_name)} values(2)`;
|
||||
throw new Error("fail");
|
||||
});
|
||||
})
|
||||
.catch(() => "fail"),
|
||||
).toBe("fail");
|
||||
});
|
||||
test("Transaction throws", async () => {
|
||||
await container.ready;
|
||||
await using sql = new SQL(getOptions());
|
||||
const random_name = ("t_" + randomUUIDv7("hex").replaceAll("-", "")).toLowerCase();
|
||||
|
||||
await sql`CREATE TEMPORARY TABLE IF NOT EXISTS ${sql(random_name)} (a int)`;
|
||||
expect(
|
||||
await sql
|
||||
.begin(async sql => {
|
||||
await sql`insert into ${sql(random_name)} values(1)`;
|
||||
await sql`insert into ${sql(random_name)} values('hej')`;
|
||||
})
|
||||
.catch(e => e.errno),
|
||||
).toBe("22P02");
|
||||
});
|
||||
|
||||
test("Transaction rolls back", async () => {
|
||||
await container.ready;
|
||||
await using sql = new SQL(getOptions());
|
||||
const random_name = ("t_" + randomUUIDv7("hex").replaceAll("-", "")).toLowerCase();
|
||||
|
||||
await sql`CREATE TEMPORARY TABLE IF NOT EXISTS ${sql(random_name)} (a int)`;
|
||||
|
||||
test("Transaction succeeds on caught savepoint", async () => {
|
||||
await using sql = new SQL(options);
|
||||
const random_name = ("t_" + randomUUIDv7("hex").replaceAll("-", "")).toLowerCase();
|
||||
await sql`CREATE TABLE IF NOT EXISTS ${sql(random_name)} (a int)`;
|
||||
try {
|
||||
await sql.begin(async sql => {
|
||||
await sql`insert into ${sql(random_name)} values(1)`;
|
||||
await sql
|
||||
.savepoint(async sql => {
|
||||
await sql`insert into ${sql(random_name)} values(2)`;
|
||||
throw new Error("please rollback");
|
||||
.begin(async sql => {
|
||||
await sql`insert into ${sql(random_name)} values(1)`;
|
||||
await sql`insert into ${sql(random_name)} values('hej')`;
|
||||
})
|
||||
.catch(() => {
|
||||
/* ignore */
|
||||
});
|
||||
await sql`insert into ${sql(random_name)} values(3)`;
|
||||
|
||||
expect((await sql`select a from ${sql(random_name)}`).count).toBe(0);
|
||||
});
|
||||
expect((await sql`select count(1) from ${sql(random_name)}`)[0].count).toBe(2n);
|
||||
} finally {
|
||||
await sql`DROP TABLE IF EXISTS ${sql(random_name)}`;
|
||||
}
|
||||
});
|
||||
|
||||
test("Savepoint returns Result", async () => {
|
||||
let result;
|
||||
await using sql = new SQL(options);
|
||||
await sql.begin(async t => {
|
||||
result = await t.savepoint(s => s`select 1 as x`);
|
||||
});
|
||||
expect(result[0]?.x).toBe(1);
|
||||
});
|
||||
test("Transaction throws on uncaught savepoint", async () => {
|
||||
await container.ready;
|
||||
await using sql = new SQL(getOptions());
|
||||
const random_name = ("t_" + randomUUIDv7("hex").replaceAll("-", "")).toLowerCase();
|
||||
await sql`CREATE TEMPORARY TABLE IF NOT EXISTS ${sql(random_name)} (a int)`;
|
||||
expect(
|
||||
await sql
|
||||
.begin(async sql => {
|
||||
await sql`insert into ${sql(random_name)} values(1)`;
|
||||
await sql.savepoint(async sql => {
|
||||
await sql`insert into ${sql(random_name)} values(2)`;
|
||||
throw new Error("fail");
|
||||
});
|
||||
})
|
||||
.catch(err => err.message),
|
||||
).toBe("fail");
|
||||
});
|
||||
|
||||
test("Transaction requests are executed implicitly", async () => {
|
||||
await using sql = new SQL(options);
|
||||
expect(
|
||||
(
|
||||
await sql.begin(sql => [
|
||||
sql`select set_config('bun_sql.test', 'testing', true)`,
|
||||
sql`select current_setting('bun_sql.test') as x`,
|
||||
])
|
||||
)[1][0].x,
|
||||
).toBe("testing");
|
||||
});
|
||||
test("Transaction throws on uncaught named savepoint", async () => {
|
||||
await container.ready;
|
||||
await using sql = new SQL(getOptions());
|
||||
const random_name = ("t_" + randomUUIDv7("hex").replaceAll("-", "")).toLowerCase();
|
||||
await sql`CREATE TEMPORARY TABLE IF NOT EXISTS ${sql(random_name)} (a int)`;
|
||||
expect(
|
||||
await sql
|
||||
.begin(async sql => {
|
||||
await sql`insert into ${sql(random_name)} values(1)`;
|
||||
await sql.savepoint("watpoint", async sql => {
|
||||
await sql`insert into ${sql(random_name)} values(2)`;
|
||||
throw new Error("fail");
|
||||
});
|
||||
})
|
||||
.catch(e => e.message),
|
||||
).toBe("fail");
|
||||
});
|
||||
|
||||
test("Uncaught transaction request errors bubbles to transaction", async () => {
|
||||
await using sql = new SQL(options);
|
||||
expect(
|
||||
await sql
|
||||
.begin(sql => [sql`select wat`, sql`select current_setting('bun_sql.test') as x, ${1} as a`])
|
||||
.catch(e => e.errno),
|
||||
).toBe("42703");
|
||||
});
|
||||
|
||||
test("Transaction rejects with rethrown error", async () => {
|
||||
await using sql = new SQL(options);
|
||||
expect(
|
||||
await sql
|
||||
.begin(async sql => {
|
||||
try {
|
||||
await sql`select exception`;
|
||||
} catch (ex) {
|
||||
throw new Error("WAT");
|
||||
}
|
||||
})
|
||||
.catch(e => e.message),
|
||||
).toBe("WAT");
|
||||
});
|
||||
|
||||
test("Parallel transactions", async () => {
|
||||
await using sql = new SQL({ ...options, max: 2 });
|
||||
|
||||
expect(
|
||||
(await Promise.all([sql.begin(sql => sql`select 1 as count`), sql.begin(sql => sql`select 1 as count`)]))
|
||||
.map(x => x[0].count)
|
||||
.join(""),
|
||||
).toBe("11");
|
||||
});
|
||||
|
||||
test("Many transactions at beginning of connection", async () => {
|
||||
await using sql = new SQL({ ...options, max: 2 });
|
||||
const xs = await Promise.all(Array.from({ length: 30 }, () => sql.begin(sql => sql`select 1`)));
|
||||
return expect(xs.length).toBe(30);
|
||||
});
|
||||
|
||||
test("Transactions array", async () => {
|
||||
await using sql = new SQL(options);
|
||||
expect(
|
||||
(await sql.begin(sql => [sql`select 1 as count`, sql`select 1 as count`])).map(x => x[0].count).join(""),
|
||||
).toBe("11");
|
||||
});
|
||||
|
||||
test.skipIf(options.transactionPool)("Transaction waits", async () => {
|
||||
await using sql = new SQL({ ...options, max: 2 });
|
||||
const random_name = ("t_" + randomUUIDv7("hex").replaceAll("-", "")).toLowerCase();
|
||||
await sql`CREATE TEMPORARY TABLE IF NOT EXISTS ${sql(random_name)} (a int)`;
|
||||
await sql.begin(async sql => {
|
||||
await sql`insert into ${sql(random_name)} values(1)`;
|
||||
await sql
|
||||
.savepoint(async sql => {
|
||||
await sql`insert into ${sql(random_name)} values(2)`;
|
||||
throw new Error("please rollback");
|
||||
})
|
||||
.catch(() => {
|
||||
/* ignore */
|
||||
test("Transaction succeeds on caught savepoint", async () => {
|
||||
await container.ready;
|
||||
await using sql = new SQL(getOptions());
|
||||
const random_name = ("t_" + randomUUIDv7("hex").replaceAll("-", "")).toLowerCase();
|
||||
await sql`CREATE TEMPORARY TABLE IF NOT EXISTS ${sql(random_name)} (a int)`;
|
||||
await sql.begin(async sql => {
|
||||
await sql`insert into ${sql(random_name)} values(1)`;
|
||||
await sql
|
||||
.savepoint(async sql => {
|
||||
await sql`insert into ${sql(random_name)} values(2)`;
|
||||
throw new Error("please rollback");
|
||||
})
|
||||
.catch(() => {
|
||||
/* ignore */
|
||||
});
|
||||
await sql`insert into ${sql(random_name)} values(3)`;
|
||||
});
|
||||
await sql`insert into ${sql(random_name)} values(3)`;
|
||||
expect((await sql`select count(1) from ${sql(random_name)}`)[0].count).toBe(2n);
|
||||
});
|
||||
|
||||
test("Savepoint returns Result", async () => {
|
||||
await container.ready;
|
||||
let result;
|
||||
await using sql = new SQL(getOptions());
|
||||
await sql.begin(async t => {
|
||||
result = await t.savepoint(s => s`select 1 as x`);
|
||||
});
|
||||
expect(result[0]?.x).toBe(1);
|
||||
});
|
||||
|
||||
test("Transaction requests are executed implicitly", async () => {
|
||||
await container.ready;
|
||||
await using sql = new SQL(getOptions());
|
||||
expect(
|
||||
(
|
||||
await sql.begin(sql => [
|
||||
sql`select set_config('bun_sql.test', 'testing', true)`,
|
||||
sql`select current_setting('bun_sql.test') as x`,
|
||||
])
|
||||
)[1][0].x,
|
||||
).toBe("testing");
|
||||
});
|
||||
|
||||
test("Uncaught transaction request errors bubbles to transaction", async () => {
|
||||
await container.ready;
|
||||
await using sql = new SQL(getOptions());
|
||||
expect(
|
||||
await sql
|
||||
.begin(sql => [sql`select wat`, sql`select current_setting('bun_sql.test') as x, ${1} as a`])
|
||||
.catch(e => e.errno),
|
||||
).toBe("42703");
|
||||
});
|
||||
|
||||
test("Transaction rejects with rethrown error", async () => {
|
||||
await container.ready;
|
||||
await using sql = new SQL(getOptions());
|
||||
expect(
|
||||
await sql
|
||||
.begin(async sql => {
|
||||
try {
|
||||
await sql`select exception`;
|
||||
} catch (ex) {
|
||||
throw new Error("WAT");
|
||||
}
|
||||
})
|
||||
.catch(e => e.message),
|
||||
).toBe("WAT");
|
||||
});
|
||||
|
||||
test("Parallel transactions", async () => {
|
||||
await container.ready;
|
||||
await using sql = new SQL({ ...getOptions(), max: 2 });
|
||||
|
||||
expect(
|
||||
(await Promise.all([sql.begin(sql => sql`select 1 as count`), sql.begin(sql => sql`select 1 as count`)]))
|
||||
.map(x => x[0].count)
|
||||
.join(""),
|
||||
).toBe("11");
|
||||
});
|
||||
|
||||
test("Many transactions at beginning of connection", async () => {
|
||||
await container.ready;
|
||||
await using sql = new SQL({ ...getOptions(), max: 2 });
|
||||
const xs = await Promise.all(Array.from({ length: 30 }, () => sql.begin(sql => sql`select 1`)));
|
||||
return expect(xs.length).toBe(30);
|
||||
});
|
||||
|
||||
test("Transactions array", async () => {
|
||||
await container.ready;
|
||||
await using sql = new SQL(getOptions());
|
||||
expect(
|
||||
(await sql.begin(sql => [sql`select 1 as count`, sql`select 1 as count`])).map(x => x[0].count).join(""),
|
||||
).toBe("11");
|
||||
});
|
||||
|
||||
test("Transaction waits", async () => {
|
||||
await container.ready;
|
||||
await using sql = new SQL({ ...getOptions(), max: 2 });
|
||||
const random_name = ("t_" + randomUUIDv7("hex").replaceAll("-", "")).toLowerCase();
|
||||
await sql`CREATE TEMPORARY TABLE IF NOT EXISTS ${sql(random_name)} (a int)`;
|
||||
await sql.begin(async sql => {
|
||||
await sql`insert into ${sql(random_name)} values(1)`;
|
||||
await sql
|
||||
.savepoint(async sql => {
|
||||
await sql`insert into ${sql(random_name)} values(2)`;
|
||||
throw new Error("please rollback");
|
||||
})
|
||||
.catch(() => {
|
||||
/* ignore */
|
||||
});
|
||||
await sql`insert into ${sql(random_name)} values(3)`;
|
||||
});
|
||||
expect(
|
||||
(
|
||||
await Promise.all([
|
||||
sql.begin(async sql => await sql`select 1 as count`),
|
||||
sql.begin(async sql => await sql`select 1 as count`),
|
||||
])
|
||||
)
|
||||
.map(x => x[0].count)
|
||||
.join(""),
|
||||
).toBe("11");
|
||||
});
|
||||
});
|
||||
expect(
|
||||
(
|
||||
await Promise.all([
|
||||
sql.begin(async sql => await sql`select 1 as count`),
|
||||
sql.begin(async sql => await sql`select 1 as count`),
|
||||
])
|
||||
)
|
||||
.map(x => x[0].count)
|
||||
.join(""),
|
||||
).toBe("11");
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -13,8 +13,9 @@ test("bun npm placeholder script should exit with error if postinstall hasn't ru
|
||||
|
||||
// This is the placeholder script content that gets written to bin/bun.exe
|
||||
// during npm package build (see packages/bun-release/scripts/upload-npm.ts)
|
||||
const placeholderScript = `#!/bin/sh
|
||||
echo "Error: Bun's postinstall script was not run." >&2
|
||||
// Note: no shebang — a #!/bin/sh shebang breaks Windows because npm's cmd-shim
|
||||
// bakes the interpreter path into .ps1/.cmd wrappers before postinstall runs.
|
||||
const placeholderScript = `echo "Error: Bun's postinstall script was not run." >&2
|
||||
echo "" >&2
|
||||
echo "This occurs when using --ignore-scripts during installation, or when using a" >&2
|
||||
echo "package manager like pnpm that does not run postinstall scripts by default." >&2
|
||||
@@ -38,9 +39,11 @@ exit 1
|
||||
});
|
||||
expect(chmodExitCode).toBe(0);
|
||||
|
||||
// Run the placeholder script
|
||||
// Run via sh explicitly — in real usage, bash/zsh automatically fall back to sh
|
||||
// interpretation when execve returns ENOEXEC for a shebang-less executable file.
|
||||
// Bun.spawn doesn't have that fallback, so we invoke sh directly here.
|
||||
await using proc = Bun.spawn({
|
||||
cmd: ["./bun-placeholder", "--version"],
|
||||
cmd: ["sh", "./bun-placeholder"],
|
||||
cwd: String(dir),
|
||||
env: bunEnv,
|
||||
stdout: "pipe",
|
||||
|
||||
Reference in New Issue
Block a user