@dangao/bun-server 3.1.0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/database/connection-manager.d.ts +2 -2
- package/dist/database/connection-manager.d.ts.map +1 -1
- package/dist/database/connection-pool.d.ts +4 -4
- package/dist/database/connection-pool.d.ts.map +1 -1
- package/dist/database/database-module.d.ts.map +1 -1
- package/dist/database/db-proxy.d.ts.map +1 -1
- package/dist/database/driver.d.ts +83 -0
- package/dist/database/driver.d.ts.map +1 -0
- package/dist/database/index.d.ts +2 -1
- package/dist/database/index.d.ts.map +1 -1
- package/dist/database/service.d.ts +0 -10
- package/dist/database/service.d.ts.map +1 -1
- package/dist/database/sql-manager.d.ts.map +1 -1
- package/dist/database/types.d.ts +26 -0
- package/dist/database/types.d.ts.map +1 -1
- package/dist/index.js +5882 -5789
- package/dist/index.node.mjs +247 -114
- package/docs/database.md +44 -0
- package/docs/zh/database.md +44 -0
- package/package.json +1 -1
- package/src/database/connection-manager.ts +5 -46
- package/src/database/connection-pool.ts +26 -49
- package/src/database/database-module.ts +6 -0
- package/src/database/db-proxy.ts +3 -2
- package/src/database/driver.ts +368 -0
- package/src/database/index.ts +8 -0
- package/src/database/service.ts +3 -74
- package/src/database/sql-manager.ts +38 -24
- package/src/database/types.ts +27 -2
- package/tests/database/database-module.test.ts +4 -0
- package/tests/database/driver-mysql2.test.ts +95 -0
- package/tests/database/driver.test.ts +234 -0
- package/tests/database/orm.test.ts +4 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { BunSQLConfig } from './types';
|
|
2
2
|
import { getRuntime } from '../platform/runtime';
|
|
3
|
+
import { resolveDriver, tagConnection } from './driver';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* SQL 连接管理器
|
|
@@ -17,39 +18,52 @@ export class BunSQLManager {
|
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
const pool = config.pool ?? {};
|
|
21
|
+
const driver = resolveDriver(config.type, config.driver, getRuntime().engine);
|
|
20
22
|
let sql: unknown;
|
|
21
23
|
|
|
22
|
-
if (
|
|
24
|
+
if (driver === 'bun-sql') {
|
|
23
25
|
const { SQL } = require('bun') as typeof import('bun');
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
waitForConnections: true,
|
|
40
|
-
});
|
|
26
|
+
if (config.type === 'mysql') {
|
|
27
|
+
// 走 options-object 形式而非连接字符串,绕开 oven-sh/bun#26648
|
|
28
|
+
const parsed = new URL(config.url);
|
|
29
|
+
sql = new SQL({
|
|
30
|
+
adapter: 'mysql',
|
|
31
|
+
hostname: parsed.hostname,
|
|
32
|
+
port: parsed.port ? Number(parsed.port) : 3306,
|
|
33
|
+
username: decodeURIComponent(parsed.username),
|
|
34
|
+
password: decodeURIComponent(parsed.password),
|
|
35
|
+
database: parsed.pathname.replace(/^\//, ''),
|
|
36
|
+
max: pool.max ?? 10,
|
|
37
|
+
idleTimeout: pool.idleTimeout ?? 30,
|
|
38
|
+
maxLifetime: pool.maxLifetime ?? 0,
|
|
39
|
+
connectionTimeout: pool.connectionTimeout ?? 30000,
|
|
40
|
+
} as unknown as string);
|
|
41
41
|
} else {
|
|
42
|
-
|
|
43
|
-
const postgres = require('postgres') as typeof import('postgres');
|
|
44
|
-
sql = postgres(config.url, {
|
|
42
|
+
sql = new SQL(config.url, {
|
|
45
43
|
max: pool.max ?? 10,
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
44
|
+
idleTimeout: pool.idleTimeout ?? 30,
|
|
45
|
+
maxLifetime: pool.maxLifetime ?? 0,
|
|
46
|
+
connectionTimeout: pool.connectionTimeout ?? 30000,
|
|
49
47
|
});
|
|
50
48
|
}
|
|
49
|
+
} else if (driver === 'mysql2') {
|
|
50
|
+
const mysql2 = require('mysql2/promise') as typeof import('mysql2/promise');
|
|
51
|
+
sql = mysql2.createPool({
|
|
52
|
+
uri: config.url,
|
|
53
|
+
connectionLimit: pool.max ?? 10,
|
|
54
|
+
waitForConnections: true,
|
|
55
|
+
});
|
|
56
|
+
} else {
|
|
57
|
+
const postgres = require('postgres') as typeof import('postgres');
|
|
58
|
+
sql = postgres(config.url, {
|
|
59
|
+
max: pool.max ?? 10,
|
|
60
|
+
idle_timeout: pool.idleTimeout ?? 30,
|
|
61
|
+
max_lifetime: pool.maxLifetime ?? 0,
|
|
62
|
+
connect_timeout: (pool.connectionTimeout ?? 30000) / 1000,
|
|
63
|
+
});
|
|
51
64
|
}
|
|
52
65
|
|
|
66
|
+
tagConnection(sql, driver);
|
|
53
67
|
this.instances.set(tenantId, sql);
|
|
54
68
|
return sql;
|
|
55
69
|
}
|
package/src/database/types.ts
CHANGED
|
@@ -3,6 +3,19 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export type DatabaseType = 'sqlite' | 'postgres' | 'mysql';
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* SQL 驱动选择
|
|
8
|
+
*
|
|
9
|
+
* 与运行时平台(fs/crypto/http server 等)解耦,仅决定 Postgres/MySQL 连接走哪套底层驱动。
|
|
10
|
+
*
|
|
11
|
+
* - `'auto'`(默认):Bun 运行时使用内建 `Bun.SQL`,Node.js 运行时使用 `mysql2` / `postgres` 纯 JS 驱动(保持历史行为)。
|
|
12
|
+
* - `'mysql2'`:无论运行时是 Bun 还是 Node,都使用 `mysql2`(仅适用于 `type: 'mysql'`)。
|
|
13
|
+
* 适合 `bun build --compile` 场景,绕开编译二进制内焊死的 `Bun.SQL` MySQL 适配 bug。
|
|
14
|
+
* - `'postgres'`:无论运行时如何,都使用 `postgres` 纯 JS 驱动(仅适用于 `type: 'postgres'`)。
|
|
15
|
+
* - `'bun-sql'`:强制使用 `Bun.SQL`(仅在 Bun 运行时合法,Node.js 下会抛出清晰错误)。
|
|
16
|
+
*/
|
|
17
|
+
export type DatabaseDriver = 'auto' | 'bun-sql' | 'mysql2' | 'postgres';
|
|
18
|
+
|
|
6
19
|
/**
|
|
7
20
|
* SQLite 配置
|
|
8
21
|
*/
|
|
@@ -72,11 +85,13 @@ export interface MysqlConfig {
|
|
|
72
85
|
|
|
73
86
|
/**
|
|
74
87
|
* 数据库配置(联合类型)
|
|
88
|
+
*
|
|
89
|
+
* Postgres/MySQL 支持可选的 `driver` 字段,用于显式选择底层驱动(与运行时平台解耦)。
|
|
75
90
|
*/
|
|
76
91
|
export type DatabaseConfig =
|
|
77
92
|
| { type: 'sqlite'; config: SqliteConfig }
|
|
78
|
-
| { type: 'postgres'; config: PostgresConfig }
|
|
79
|
-
| { type: 'mysql'; config: MysqlConfig };
|
|
93
|
+
| { type: 'postgres'; config: PostgresConfig; driver?: DatabaseDriver }
|
|
94
|
+
| { type: 'mysql'; config: MysqlConfig; driver?: DatabaseDriver };
|
|
80
95
|
|
|
81
96
|
/**
|
|
82
97
|
* Bun.SQL 连接池配置
|
|
@@ -111,6 +126,11 @@ export interface BunSQLConfig {
|
|
|
111
126
|
type: 'postgres' | 'mysql';
|
|
112
127
|
url: string;
|
|
113
128
|
pool?: BunSQLPoolOptions;
|
|
129
|
+
/**
|
|
130
|
+
* 显式选择底层驱动(与运行时平台解耦)。
|
|
131
|
+
* @default 'auto'
|
|
132
|
+
*/
|
|
133
|
+
driver?: DatabaseDriver;
|
|
114
134
|
}
|
|
115
135
|
|
|
116
136
|
/**
|
|
@@ -183,6 +203,11 @@ export interface DatabaseModuleOptions {
|
|
|
183
203
|
* 单租户:数据库类型(V2)
|
|
184
204
|
*/
|
|
185
205
|
type?: DatabaseType;
|
|
206
|
+
/**
|
|
207
|
+
* 单租户:显式选择底层驱动(V2,与运行时平台解耦)
|
|
208
|
+
* @default 'auto'
|
|
209
|
+
*/
|
|
210
|
+
driver?: DatabaseDriver;
|
|
186
211
|
/**
|
|
187
212
|
* 单租户:Postgres/MySQL URL(V2)
|
|
188
213
|
*/
|
|
@@ -9,9 +9,13 @@ import {
|
|
|
9
9
|
SQLITE_MANAGER_TOKEN,
|
|
10
10
|
} from '../../src/database';
|
|
11
11
|
import { MODULE_METADATA_KEY } from '../../src/di/module';
|
|
12
|
+
import { initRuntime } from '../../src/platform/runtime';
|
|
12
13
|
|
|
13
14
|
describe('DatabaseModule V2', () => {
|
|
14
15
|
beforeEach(() => {
|
|
16
|
+
// forRoot() 会在创建 sqlite/sql 管理器时读取 getRuntime(),确保运行时已初始化
|
|
17
|
+
// (避免依赖其他测试文件的执行顺序)
|
|
18
|
+
initRuntime('bun');
|
|
15
19
|
Reflect.deleteMetadata(MODULE_METADATA_KEY, DatabaseModule);
|
|
16
20
|
});
|
|
17
21
|
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { describe, expect, test, mock, beforeAll } from 'bun:test';
|
|
2
|
+
|
|
3
|
+
import { initRuntime, getRuntime } from '../../src/platform/runtime';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 验证:在 Bun 运行时下显式选用 driver: 'mysql2' 时,
|
|
7
|
+
* 连接创建 / 参数化查询 / health check / close 全部走 mysql2(而非 Bun.SQL)。
|
|
8
|
+
*
|
|
9
|
+
* 通过 mock 'mysql2/promise' 模块注入假连接,无需真实 MySQL。
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const createdConnections: Array<Record<string, unknown>> = [];
|
|
13
|
+
const queryCalls: Array<{ sql: string; params: unknown[] }> = [];
|
|
14
|
+
let endCount = 0;
|
|
15
|
+
|
|
16
|
+
function makeFakeConnection() {
|
|
17
|
+
const conn = {
|
|
18
|
+
query: async (sql: string, params: unknown[]) => {
|
|
19
|
+
queryCalls.push({ sql, params });
|
|
20
|
+
// mysql2 返回 [rows, fields]
|
|
21
|
+
if (/select\s+1/i.test(sql)) {
|
|
22
|
+
return [[{ '1': 1 }], []];
|
|
23
|
+
}
|
|
24
|
+
return [[{ id: 1, name: 'alice' }], [{ name: 'id' }, { name: 'name' }]];
|
|
25
|
+
},
|
|
26
|
+
end: async () => {
|
|
27
|
+
endCount += 1;
|
|
28
|
+
},
|
|
29
|
+
close: async () => {
|
|
30
|
+
throw new Error('mysql2 connection should be closed via end(), not close()');
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
createdConnections.push(conn);
|
|
34
|
+
return conn;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
mock.module('mysql2/promise', () => {
|
|
38
|
+
const createConnection = async (_opts: unknown) => makeFakeConnection();
|
|
39
|
+
const createPool = (_opts: unknown) => makeFakeConnection();
|
|
40
|
+
return {
|
|
41
|
+
default: { createConnection, createPool },
|
|
42
|
+
createConnection,
|
|
43
|
+
createPool,
|
|
44
|
+
};
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
describe('DatabaseService with driver: mysql2 on Bun runtime', () => {
|
|
48
|
+
beforeAll(() => {
|
|
49
|
+
initRuntime('bun');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('runs the full lifecycle (create/query/health/close) through mysql2', async () => {
|
|
53
|
+
// 前置确认:当前进程运行在 Bun,但我们强制 mysql2
|
|
54
|
+
expect(getRuntime().engine).toBe('bun');
|
|
55
|
+
|
|
56
|
+
const { DatabaseService } = await import('../../src/database/service');
|
|
57
|
+
|
|
58
|
+
const svc = new DatabaseService({
|
|
59
|
+
database: {
|
|
60
|
+
type: 'mysql',
|
|
61
|
+
driver: 'mysql2',
|
|
62
|
+
config: {
|
|
63
|
+
host: 'localhost',
|
|
64
|
+
port: 3306,
|
|
65
|
+
database: 'testdb',
|
|
66
|
+
user: 'root',
|
|
67
|
+
password: 'secret',
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
enableHealthCheck: true,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
await svc.initialize();
|
|
74
|
+
|
|
75
|
+
// 连接由 mysql2.createConnection 创建
|
|
76
|
+
expect(createdConnections.length).toBeGreaterThan(0);
|
|
77
|
+
|
|
78
|
+
// 参数化查询走 mysql2.query(sql, params),并返回 rows(而非 [rows, fields])
|
|
79
|
+
const rows = await svc.query<{ id: number; name: string }>(
|
|
80
|
+
'SELECT * FROM users WHERE id = ?',
|
|
81
|
+
[1],
|
|
82
|
+
);
|
|
83
|
+
expect(rows).toEqual([{ id: 1, name: 'alice' }]);
|
|
84
|
+
expect(queryCalls.some((c) => c.sql === 'SELECT * FROM users WHERE id = ?' && c.params[0] === 1)).toBe(true);
|
|
85
|
+
|
|
86
|
+
// health check 走 mysql2.query('SELECT 1')
|
|
87
|
+
const healthy = await svc.healthCheck();
|
|
88
|
+
expect(healthy).toBe(true);
|
|
89
|
+
expect(queryCalls.some((c) => /select\s+1/i.test(c.sql))).toBe(true);
|
|
90
|
+
|
|
91
|
+
// close 走 mysql2 的 end()
|
|
92
|
+
await svc.closePool();
|
|
93
|
+
expect(endCount).toBeGreaterThan(0);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
closeViaDriver,
|
|
5
|
+
getConnectionDriver,
|
|
6
|
+
healthCheckViaDriver,
|
|
7
|
+
queryViaDriver,
|
|
8
|
+
resolveDriver,
|
|
9
|
+
tagConnection,
|
|
10
|
+
templateQueryViaDriver,
|
|
11
|
+
} from '../../src/database/driver';
|
|
12
|
+
|
|
13
|
+
describe('resolveDriver', () => {
|
|
14
|
+
test("'auto' on bun resolves to bun-sql for mysql and postgres", () => {
|
|
15
|
+
expect(resolveDriver('mysql', 'auto', 'bun')).toBe('bun-sql');
|
|
16
|
+
expect(resolveDriver('postgres', 'auto', 'bun')).toBe('bun-sql');
|
|
17
|
+
expect(resolveDriver('mysql', undefined, 'bun')).toBe('bun-sql');
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test("'auto' on node resolves to js drivers", () => {
|
|
21
|
+
expect(resolveDriver('mysql', 'auto', 'node')).toBe('mysql2');
|
|
22
|
+
expect(resolveDriver('postgres', 'auto', 'node')).toBe('postgres');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("'mysql2' forces mysql2 regardless of engine", () => {
|
|
26
|
+
expect(resolveDriver('mysql', 'mysql2', 'bun')).toBe('mysql2');
|
|
27
|
+
expect(resolveDriver('mysql', 'mysql2', 'node')).toBe('mysql2');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("'postgres' forces postgres regardless of engine", () => {
|
|
31
|
+
expect(resolveDriver('postgres', 'postgres', 'bun')).toBe('postgres');
|
|
32
|
+
expect(resolveDriver('postgres', 'postgres', 'node')).toBe('postgres');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("'bun-sql' is valid on bun, throws on node", () => {
|
|
36
|
+
expect(resolveDriver('mysql', 'bun-sql', 'bun')).toBe('bun-sql');
|
|
37
|
+
expect(() => resolveDriver('mysql', 'bun-sql', 'node')).toThrow();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("'mysql2' on postgres type throws", () => {
|
|
41
|
+
expect(() => resolveDriver('postgres', 'mysql2', 'bun')).toThrow();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("'postgres' on mysql type throws", () => {
|
|
45
|
+
expect(() => resolveDriver('mysql', 'postgres', 'bun')).toThrow();
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
describe('connection tagging', () => {
|
|
50
|
+
test('tag and read back driver on object connection', () => {
|
|
51
|
+
const conn = { query: () => undefined };
|
|
52
|
+
tagConnection(conn, 'mysql2');
|
|
53
|
+
expect(getConnectionDriver(conn)).toBe('mysql2');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('tag preserves identity', () => {
|
|
57
|
+
const conn = {};
|
|
58
|
+
expect(tagConnection(conn, 'mysql2')).toBe(conn);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('tag is non-enumerable', () => {
|
|
62
|
+
const conn = { query: () => undefined };
|
|
63
|
+
tagConnection(conn, 'mysql2');
|
|
64
|
+
expect(Object.keys(conn)).toEqual(['query']);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test('callable connection without tag falls back to bun-sql', () => {
|
|
68
|
+
const conn = (() => undefined) as unknown;
|
|
69
|
+
expect(getConnectionDriver(conn)).toBe('bun-sql');
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test('untagged plain object returns undefined', () => {
|
|
73
|
+
expect(getConnectionDriver({ query: () => undefined })).toBeUndefined();
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe('queryViaDriver', () => {
|
|
78
|
+
test('mysql2 connection uses .query and returns rows (not [rows, fields])', async () => {
|
|
79
|
+
const calls: Array<{ sql: string; params: unknown[] }> = [];
|
|
80
|
+
const conn = tagConnection(
|
|
81
|
+
{
|
|
82
|
+
query: async (sql: string, params: unknown[]) => {
|
|
83
|
+
calls.push({ sql, params });
|
|
84
|
+
return [[{ id: 1 }], [{ name: 'id' }]];
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
'mysql2',
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const rows = await queryViaDriver(conn, 'SELECT * FROM t WHERE id = ?', [1]);
|
|
91
|
+
expect(rows).toEqual([{ id: 1 }]);
|
|
92
|
+
expect(calls).toEqual([{ sql: 'SELECT * FROM t WHERE id = ?', params: [1] }]);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test('postgres connection uses .unsafe and returns rows', async () => {
|
|
96
|
+
const calls: Array<{ sql: string; params: unknown[] }> = [];
|
|
97
|
+
const conn = tagConnection(
|
|
98
|
+
{
|
|
99
|
+
unsafe: async (sql: string, params: unknown[]) => {
|
|
100
|
+
calls.push({ sql, params });
|
|
101
|
+
return [{ ok: true }];
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
'postgres',
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
const rows = await queryViaDriver(conn, 'SELECT 1 WHERE x = ?', ['y']);
|
|
108
|
+
expect(rows).toEqual([{ ok: true }]);
|
|
109
|
+
expect(calls).toEqual([{ sql: 'SELECT 1 WHERE x = ?', params: ['y'] }]);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test('bun-sql connection uses template string path', async () => {
|
|
113
|
+
let received: { strings: string[]; values: unknown[] } | null = null;
|
|
114
|
+
const conn = tagConnection(
|
|
115
|
+
Object.assign(
|
|
116
|
+
async (strings: TemplateStringsArray, ...values: unknown[]) => {
|
|
117
|
+
received = { strings: Array.from(strings), values };
|
|
118
|
+
return [{ via: 'bun-sql' }];
|
|
119
|
+
},
|
|
120
|
+
),
|
|
121
|
+
'bun-sql',
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
const rows = await queryViaDriver(conn, 'SELECT * FROM t WHERE id = ?', [42]);
|
|
125
|
+
expect(rows).toEqual([{ via: 'bun-sql' }]);
|
|
126
|
+
expect(received!.strings).toEqual(['SELECT * FROM t WHERE id = ', '']);
|
|
127
|
+
expect(received!.values).toEqual([42]);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
describe('templateQueryViaDriver', () => {
|
|
132
|
+
test('mysql2 transforms template into ? placeholders', async () => {
|
|
133
|
+
const calls: Array<{ sql: string; params: unknown[] }> = [];
|
|
134
|
+
const conn = tagConnection(
|
|
135
|
+
{
|
|
136
|
+
query: async (sql: string, params: unknown[]) => {
|
|
137
|
+
calls.push({ sql, params });
|
|
138
|
+
return [[{ id: 7 }], []];
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
'mysql2',
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
const strings = Object.assign(['SELECT * FROM t WHERE id = ', ''], {
|
|
145
|
+
raw: ['SELECT * FROM t WHERE id = ', ''],
|
|
146
|
+
}) as unknown as TemplateStringsArray;
|
|
147
|
+
|
|
148
|
+
const rows = await templateQueryViaDriver(conn, strings, [7]);
|
|
149
|
+
expect(rows).toEqual([{ id: 7 }]);
|
|
150
|
+
expect(calls).toEqual([
|
|
151
|
+
{ sql: 'SELECT * FROM t WHERE id = ?', params: [7] },
|
|
152
|
+
]);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test('callable connection invoked directly as tagged template', async () => {
|
|
156
|
+
let received: unknown[] = [];
|
|
157
|
+
const conn = tagConnection(
|
|
158
|
+
Object.assign(
|
|
159
|
+
async (_strings: TemplateStringsArray, ...values: unknown[]) => {
|
|
160
|
+
received = values;
|
|
161
|
+
return [{ ok: 1 }];
|
|
162
|
+
},
|
|
163
|
+
),
|
|
164
|
+
'bun-sql',
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
const strings = Object.assign(['SELECT ', ''], {
|
|
168
|
+
raw: ['SELECT ', ''],
|
|
169
|
+
}) as unknown as TemplateStringsArray;
|
|
170
|
+
|
|
171
|
+
const rows = await templateQueryViaDriver(conn, strings, [99]);
|
|
172
|
+
expect(rows).toEqual([{ ok: 1 }]);
|
|
173
|
+
expect(received).toEqual([99]);
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
describe('healthCheckViaDriver', () => {
|
|
178
|
+
test('mysql2 health check runs SELECT 1', async () => {
|
|
179
|
+
const conn = tagConnection(
|
|
180
|
+
{
|
|
181
|
+
query: async () => [[{ '1': 1 }], []],
|
|
182
|
+
},
|
|
183
|
+
'mysql2',
|
|
184
|
+
);
|
|
185
|
+
expect(await healthCheckViaDriver(conn)).toBe(true);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
test('returns false when query throws', async () => {
|
|
189
|
+
const conn = tagConnection(
|
|
190
|
+
{
|
|
191
|
+
query: async () => {
|
|
192
|
+
throw new Error('down');
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
'mysql2',
|
|
196
|
+
);
|
|
197
|
+
expect(await healthCheckViaDriver(conn)).toBe(false);
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
describe('closeViaDriver', () => {
|
|
202
|
+
test('mysql2 connection closed via .end()', async () => {
|
|
203
|
+
let ended = false;
|
|
204
|
+
let closed = false;
|
|
205
|
+
const conn = tagConnection(
|
|
206
|
+
{
|
|
207
|
+
end: async () => {
|
|
208
|
+
ended = true;
|
|
209
|
+
},
|
|
210
|
+
close: async () => {
|
|
211
|
+
closed = true;
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
'mysql2',
|
|
215
|
+
);
|
|
216
|
+
await closeViaDriver(conn);
|
|
217
|
+
expect(ended).toBe(true);
|
|
218
|
+
expect(closed).toBe(false);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
test('bun-sql connection closed via .close()', async () => {
|
|
222
|
+
let closed = false;
|
|
223
|
+
const conn = tagConnection(
|
|
224
|
+
{
|
|
225
|
+
close: async () => {
|
|
226
|
+
closed = true;
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
'bun-sql',
|
|
230
|
+
);
|
|
231
|
+
await closeViaDriver(conn);
|
|
232
|
+
expect(closed).toBe(true);
|
|
233
|
+
});
|
|
234
|
+
});
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
} from '../../src/database/orm';
|
|
12
12
|
import { DatabaseService, DATABASE_SERVICE_TOKEN } from '../../src/database';
|
|
13
13
|
import { Container } from '../../src/di/container';
|
|
14
|
+
import { initRuntime } from '../../src/platform/runtime';
|
|
14
15
|
|
|
15
16
|
// 测试实体
|
|
16
17
|
@Entity('test_users')
|
|
@@ -71,6 +72,9 @@ describe('BaseRepository', () => {
|
|
|
71
72
|
let databaseService: DatabaseService;
|
|
72
73
|
|
|
73
74
|
beforeEach(async () => {
|
|
75
|
+
// DatabaseService.initialize() 会读取 getRuntime(),确保运行时已初始化
|
|
76
|
+
// (避免依赖其他测试文件的执行顺序)
|
|
77
|
+
initRuntime('bun');
|
|
74
78
|
container = new Container();
|
|
75
79
|
databaseService = new DatabaseService({
|
|
76
80
|
database: {
|