mm_sql 1.4.1 → 1.4.3

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.
@@ -0,0 +1,304 @@
1
+ const { Sql } = require('./index.js');
2
+
3
+ /**
4
+ * 全面测试Sql类
5
+ */
6
+ class SqlTest {
7
+ constructor() {
8
+ console.log('全面测试Sql类开始...');
9
+ this.sql = null;
10
+ this.count = 0;
11
+ this.pass = 0;
12
+ this.fails = [];
13
+ }
14
+
15
+ /**
16
+ * 记录测试结果
17
+ * @param {string} name - 测试名称
18
+ * @param {boolean} result - 测试结果
19
+ * @param {string} error - 错误信息
20
+ */
21
+ _recordResult(name, result, error = '') {
22
+ this.count++;
23
+ if (result) {
24
+ this.pass++;
25
+ console.log(`✅ ${name} - 通过`);
26
+ } else {
27
+ this.fails.push({ name, error });
28
+ console.log(`❌ ${name} - 失败: ${error}`);
29
+ }
30
+ }
31
+
32
+ /**
33
+ * 初始化Sql实例
34
+ * @returns {Promise<boolean>} 初始化结果
35
+ */
36
+ async init() {
37
+ try {
38
+ this.sql = new Sql({
39
+ db_type: 'sqlite',
40
+ dir: './db',
41
+ host: 'localhost',
42
+ user: 'root',
43
+ password: 'Asd159357',
44
+ database: 'mm'
45
+ });
46
+ return true;
47
+ } catch (error) {
48
+ console.error('初始化失败:', error.message);
49
+ return false;
50
+ }
51
+ }
52
+
53
+ /**
54
+ * 测试构造函数和配置
55
+ */
56
+ testConfig() {
57
+ console.log('\n=== 测试构造函数和配置 ===');
58
+
59
+ // 测试默认配置
60
+ try {
61
+ const default_sql = new Sql();
62
+ this._recordResult('默认构造函数', default_sql.config !== undefined);
63
+ } catch (error) {
64
+ this._recordResult('默认构造函数', false, error.message);
65
+ }
66
+
67
+ // 测试自定义配置
68
+ try {
69
+ const custom_sql = new Sql({ db_type: 'sqlite', database: 'test_db' });
70
+ const result = custom_sql.config.db_type === 'sqlite' && custom_sql.config.database === 'test_db';
71
+ this._recordResult('自定义配置', result);
72
+ } catch (error) {
73
+ this._recordResult('自定义配置', false, error.message);
74
+ }
75
+
76
+ // 测试setConfig方法
77
+ try {
78
+ this.sql.setConfig({ size: 100, timeout: 5000 });
79
+ const result = this.sql.config.size === 100 && this.sql.config.timeout === 5000;
80
+ this._recordResult('setConfig方法', result);
81
+ } catch (error) {
82
+ this._recordResult('setConfig方法', false, error.message);
83
+ }
84
+ }
85
+
86
+ /**
87
+ * 测试模板方法
88
+ */
89
+ testTpl() {
90
+ console.log('\n=== 测试模板查询方法 ===');
91
+
92
+ // 测试tplQuery基本功能
93
+ try {
94
+ const param = { name: '张三', age: 25 };
95
+ const sql_tpl = { name: "`name` like '%{0}%'", age: '`age` = {0}' };
96
+ const result = this.sql.tplQuery(param, sql_tpl);
97
+ const expected = "`name` like '%张三%' AND `age` = 25";
98
+ this._recordResult('tplQuery基本功能', result === expected);
99
+ } catch (error) {
100
+ this._recordResult('tplQuery基本功能', false, error.message);
101
+ }
102
+
103
+ // 测试tplQuery空参数
104
+ try {
105
+ const result = this.sql.tplQuery({}, {});
106
+ this._recordResult('tplQuery空参数', result === '');
107
+ } catch (error) {
108
+ this._recordResult('tplQuery空参数', false, error.message);
109
+ }
110
+
111
+ // 测试tplBody基本功能
112
+ try {
113
+ const param = { name: '李四', email: 'lisi@example.com' };
114
+ const sql_tpl = { name: "`name` = '{0}'" };
115
+ const result = this.sql.tplBody(param, sql_tpl);
116
+ const expected = "`name` = '李四' , `email` = 'lisi@example.com'";
117
+ this._recordResult('tplBody基本功能', result === expected);
118
+ } catch (error) {
119
+ this._recordResult('tplBody基本功能', false, error.message);
120
+ }
121
+
122
+ // 测试filter方法
123
+ try {
124
+ const param = { name: '王五', age: 30, unused: 'test' };
125
+ const arr = ['name', 'age'];
126
+ const result = this.sql.filter(param, arr);
127
+ const expected = { name: '王五', age: 30 };
128
+ this._recordResult('filter方法', JSON.stringify(result) === JSON.stringify(expected));
129
+ } catch (error) {
130
+ this._recordResult('filter方法', false, error.message);
131
+ }
132
+ }
133
+
134
+ /**
135
+ * 测试连接方法
136
+ */
137
+ testConn() {
138
+ console.log('\n=== 测试数据库连接方法 ===');
139
+
140
+ // 测试open方法
141
+ try {
142
+ const result = this.sql.open();
143
+ this._recordResult('open方法', result !== undefined);
144
+ } catch (error) {
145
+ this._recordResult('open方法', false, error.message);
146
+ }
147
+
148
+ // 测试close方法
149
+ try {
150
+ const result = this.sql.close();
151
+ this._recordResult('close方法', result !== undefined);
152
+ } catch (error) {
153
+ this._recordResult('close方法', false, error.message);
154
+ }
155
+
156
+ // 测试db方法
157
+ try {
158
+ const db = this.sql.db();
159
+ this._recordResult('db方法', db !== null && typeof db === 'object');
160
+ } catch (error) {
161
+ this._recordResult('db方法', false, error.message);
162
+ }
163
+ }
164
+
165
+ /**
166
+ * 测试执行方法
167
+ */
168
+ async testExec() {
169
+ console.log('\n=== 测试SQL执行方法 ===');
170
+
171
+ // 测试run方法存在性
172
+ try {
173
+ const result = typeof this.sql.run === 'function';
174
+ this._recordResult('run方法存在性', result);
175
+ } catch (error) {
176
+ this._recordResult('run方法存在性', false, error.message);
177
+ }
178
+
179
+ // 测试exec方法存在性
180
+ try {
181
+ const result = typeof this.sql.exec === 'function';
182
+ this._recordResult('exec方法存在性', result);
183
+ } catch (error) {
184
+ this._recordResult('exec方法存在性', false, error.message);
185
+ }
186
+
187
+ // 测试数据库操作
188
+ try {
189
+ const db = this.sql.db();
190
+ if (db) {
191
+ // 创建测试表
192
+ await db.createTable('test_table', {
193
+ id: 1,
194
+ name: '',
195
+ value: 0
196
+ }, 'id');
197
+
198
+ // 查询字段
199
+ const fields = await db.fields('test_table');
200
+ const has_fields = Array.isArray(fields) && fields.length > 0;
201
+ this._recordResult('数据库字段查询', has_fields);
202
+
203
+ // 设置表名并查询
204
+ const db_inst = db;
205
+ db_inst.table = 'test_table';
206
+ const result = await db_inst.get({ id: '1' });
207
+ this._recordResult('数据库查询操作', Array.isArray(result));
208
+ } else {
209
+ this._recordResult('数据库操作', false, 'db方法返回null');
210
+ }
211
+ } catch (error) {
212
+ this._recordResult('数据库操作', false, error.message);
213
+ }
214
+ }
215
+
216
+ /**
217
+ * 测试边界条件
218
+ */
219
+ testEdge() {
220
+ console.log('\n=== 测试边界条件和错误处理 ===');
221
+
222
+ // 测试无效参数处理
223
+ try {
224
+ this.sql.tplQuery(null, {});
225
+ this._recordResult('tplQuery无效参数', false, '应该抛出异常');
226
+ } catch {
227
+ this._recordResult('tplQuery无效参数', true);
228
+ }
229
+
230
+ // 测试空模板
231
+ try {
232
+ const result = this.sql.tplQuery({ name: 'test' }, null);
233
+ this._recordResult('tplQuery空模板', result === "`name` = 'test'");
234
+ } catch (error) {
235
+ this._recordResult('tplQuery空模板', false, error.message);
236
+ }
237
+
238
+ // 测试filter空数组
239
+ try {
240
+ const result = this.sql.filter({ name: 'test', age: 20 }, []);
241
+ this._recordResult('filter空数组', JSON.stringify(result) === '{}');
242
+ } catch (error) {
243
+ this._recordResult('filter空数组', false, error.message);
244
+ }
245
+
246
+ // 测试filter不存在的字段
247
+ try {
248
+ const result = this.sql.filter({ name: 'test' }, ['name', 'nonexistent']);
249
+ const expected = { name: 'test' };
250
+ this._recordResult('filter不存在的字段', JSON.stringify(result) === JSON.stringify(expected));
251
+ } catch (error) {
252
+ this._recordResult('filter不存在的字段', false, error.message);
253
+ }
254
+ }
255
+
256
+ /**
257
+ * 运行所有测试
258
+ * @returns {Promise<boolean>} 测试结果
259
+ */
260
+ async runAll() {
261
+ if (!await this.init()) {
262
+ console.error('初始化失败,无法继续测试');
263
+ return false;
264
+ }
265
+
266
+ // 运行所有测试组
267
+ this.testConfig();
268
+ this.testTpl();
269
+ this.testConn();
270
+ await this.testExec();
271
+ this.testEdge();
272
+
273
+ // 输出测试总结
274
+ console.log('\n' + '='.repeat(50));
275
+ console.log('测试总结:');
276
+ console.log(`总测试数: ${this.count}`);
277
+ console.log(`通过数: ${this.pass}`);
278
+ console.log(`失败数: ${this.fails.length}`);
279
+
280
+ if (this.fails.length > 0) {
281
+ console.log('\n失败的测试:');
282
+ this.fails.forEach(test => {
283
+ console.log(` - ${test.name}: ${test.error}`);
284
+ });
285
+ }
286
+
287
+ const rate = (this.pass / this.count * 100).toFixed(2);
288
+ console.log(`\n测试通过率: ${rate}%`);
289
+
290
+ return this.fails.length === 0;
291
+ }
292
+ }
293
+
294
+ // 运行全面测试
295
+ const test = new SqlTest();
296
+ test.runAll().then(success => {
297
+ if (success) {
298
+ console.log('\n🎉 所有测试通过!');
299
+ } else {
300
+ console.log('\n⚠️ 部分测试失败,请检查代码');
301
+ }
302
+ }).catch(error => {
303
+ console.error('测试执行出错:', error);
304
+ });
package/db/mm.db CHANGED
Binary file
@@ -0,0 +1,165 @@
1
+ const { Sql } = require('./index.js');
2
+
3
+ /**
4
+ * 测试适配器直接调用
5
+ * @param {Object} mysql_sql - MySQL实例
6
+ */
7
+ async function testAdapterDirect(mysql_sql) {
8
+ console.log('\n测试1: 直接调用适配器run方法...');
9
+ try {
10
+ await mysql_sql._adapter.run('SELECT * FROM non_existent_table_xyz');
11
+ console.log('❌ 适配器应该对无效SQL抛出错误,但没有抛出');
12
+ } catch (error) {
13
+ console.log('✅ 适配器正确抛出错误:', error.message);
14
+ }
15
+ }
16
+
17
+ /**
18
+ * 测试mm_sql的run方法调用
19
+ * @param {Object} mysql_sql - MySQL实例
20
+ */
21
+ async function testSqlRun(mysql_sql) {
22
+ console.log('\n测试2: 通过mm_sql的run方法调用...');
23
+ try {
24
+ await mysql_sql.run('SELECT * FROM non_existent_table_xyz');
25
+ console.log('❌ mm_sql应该对无效SQL抛出错误,但没有抛出');
26
+ } catch (error) {
27
+ console.log('✅ mm_sql正确抛出错误:', error.message);
28
+ }
29
+ }
30
+
31
+ /**
32
+ * 测试参数验证
33
+ * @param {Object} mysql_sql - MySQL实例
34
+ */
35
+ async function testParamValid(mysql_sql) {
36
+ console.log('\n测试3: 测试参数验证...');
37
+ try {
38
+ await mysql_sql.run(123); // 无效参数类型
39
+ console.log('❌ mm_sql应该对无效参数抛出TypeError,但没有抛出');
40
+ } catch (error) {
41
+ if (error instanceof TypeError) {
42
+ console.log('✅ mm_sql正确抛出TypeError:', error.message);
43
+ } else {
44
+ console.log('❌ mm_sql抛出错误但不是TypeError:', error.message);
45
+ }
46
+ }
47
+ }
48
+
49
+ /**
50
+ * 调试MySQL错误传播
51
+ */
52
+ async function debugMysqlError() {
53
+ console.log('=== 调试MySQL错误传播 ===');
54
+
55
+ try {
56
+ // 创建mm_sql的MySQL实例
57
+ const mysql_sql = new Sql({
58
+ db_type: 'mysql',
59
+ dir: './db',
60
+ host: 'localhost',
61
+ user: 'root',
62
+ password: 'Asd159357',
63
+ database: 'mm'
64
+ });
65
+
66
+ console.log('✅ mm_sql MySQL实例创建成功');
67
+ console.log('适配器类型:', mysql_sql._adapter?.constructor?.name);
68
+
69
+ await testAdapterDirect(mysql_sql);
70
+ await testSqlRun(mysql_sql);
71
+ await testParamValid(mysql_sql);
72
+
73
+ } catch (error) {
74
+ console.log('❌ mm_sql实例创建失败:', error.message);
75
+ }
76
+ }
77
+
78
+ /**
79
+ * 测试SQLite适配器直接调用
80
+ * @param {Object} sqlite_sql - SQLite实例
81
+ */
82
+ async function testSqliteAdapter(sqlite_sql) {
83
+ console.log('\n测试1: 直接调用适配器run方法...');
84
+ try {
85
+ await sqlite_sql._adapter.run('SELECT * FROM non_existent_table_xyz');
86
+ console.log('❌ 适配器应该对无效SQL抛出错误,但没有抛出');
87
+ } catch (error) {
88
+ console.log('✅ 适配器正确抛出错误:', error.message);
89
+ }
90
+ }
91
+
92
+ /**
93
+ * 测试SQLite的run方法调用
94
+ * @param {Object} sqlite_sql - SQLite实例
95
+ */
96
+ async function testSqliteRun(sqlite_sql) {
97
+ console.log('\n测试2: 通过mm_sql的run方法调用...');
98
+ try {
99
+ await sqlite_sql.run('SELECT * FROM non_existent_table_xyz');
100
+ console.log('❌ mm_sql应该对无效SQL抛出错误,但没有抛出');
101
+ } catch (error) {
102
+ console.log('✅ mm_sql正确抛出错误:', error.message);
103
+ }
104
+ }
105
+
106
+ /**
107
+ * 测试SQLite参数验证
108
+ * @param {Object} sqlite_sql - SQLite实例
109
+ */
110
+ async function testSqliteParam(sqlite_sql) {
111
+ console.log('\n测试3: 测试参数验证...');
112
+ try {
113
+ await sqlite_sql.run(123); // 无效参数类型
114
+ console.log('❌ mm_sql应该对无效参数抛出TypeError,但没有抛出');
115
+ } catch (error) {
116
+ if (error instanceof TypeError) {
117
+ console.log('✅ mm_sql正确抛出TypeError:', error.message);
118
+ } else {
119
+ console.log('❌ mm_sql抛出错误但不是TypeError:', error.message);
120
+ }
121
+ }
122
+ }
123
+
124
+ /**
125
+ * 调试SQLite错误传播
126
+ */
127
+ async function debugSqliteError() {
128
+ console.log('\n=== 调试SQLite错误传播 ===');
129
+
130
+ try {
131
+ // 创建mm_sql的SQLite实例
132
+ const sqlite_sql = new Sql({
133
+ db_type: 'sqlite',
134
+ dir: './db',
135
+ database: 'mm'
136
+ });
137
+
138
+ console.log('✅ mm_sql SQLite实例创建成功');
139
+ console.log('适配器类型:', sqlite_sql._adapter?.constructor?.name);
140
+
141
+ await testSqliteAdapter(sqlite_sql);
142
+ await testSqliteRun(sqlite_sql);
143
+ await testSqliteParam(sqlite_sql);
144
+
145
+ } catch (error) {
146
+ console.log('❌ mm_sql实例创建失败:', error.message);
147
+ }
148
+ }
149
+
150
+ /**
151
+ * 主测试函数
152
+ */
153
+ async function main() {
154
+ console.log('开始调试mm_sql模块错误传播问题...\n');
155
+
156
+ await debugMysqlError();
157
+ await debugSqliteError();
158
+
159
+ console.log('\n调试完成');
160
+ }
161
+
162
+ // 运行测试
163
+ if (require.main === module) {
164
+ main().catch(console.error);
165
+ }
package/debug_test.js ADDED
@@ -0,0 +1,35 @@
1
+ const { Sql } = require('./index.js');
2
+
3
+ // 创建Sql实例
4
+ const sql = new Sql({
5
+ db_type: 'sqlite',
6
+ dir: './db',
7
+ host: 'localhost',
8
+ user: 'root',
9
+ password: 'Asd159357',
10
+ database: 'mm'
11
+ });
12
+
13
+ console.log('=== 调试tplQuery基本功能 ===');
14
+ const param = { name: '张三', age: 25 };
15
+ const sql_tpl = { name: "`name` like '%{0}%'", age: '`age` = {0}' };
16
+ const result = sql.tplQuery(param, sql_tpl);
17
+ const expected = "`name` like '%张三%' AND `age` = 25";
18
+
19
+ console.log('参数:', param);
20
+ console.log('模板:', sql_tpl);
21
+ console.log('实际输出:', result);
22
+ console.log('期望输出:', expected);
23
+ console.log('是否匹配:', result === expected);
24
+
25
+ console.log('\n=== 调试tplBody基本功能 ===');
26
+ const param_body = { name: '李四', email: 'lisi@example.com' };
27
+ const sql_tpl_body = { name: '`name` = {0}' };
28
+ const res_body = sql.tplBody(param_body, sql_tpl_body);
29
+ const exp_body = "`name` = '李四' , `email` = 'lisi@example.com'";
30
+
31
+ console.log('参数:', param_body);
32
+ console.log('模板:', sql_tpl_body);
33
+ console.log('实际输出:', res_body);
34
+ console.log('期望输出:', exp_body);
35
+ console.log('是否匹配:', res_body === exp_body);
package/debug_test2.js ADDED
@@ -0,0 +1,45 @@
1
+ const { Sql } = require('./index.js');
2
+
3
+ // 创建Sql实例
4
+ const sql = new Sql({
5
+ db_type: 'sqlite',
6
+ dir: './db',
7
+ host: 'localhost',
8
+ user: 'root',
9
+ password: 'Asd159357',
10
+ database: 'mm'
11
+ });
12
+
13
+ console.log('=== 详细调试tplQuery基本功能 ===');
14
+ const param = { name: '张三', age: 25 };
15
+ const sql_tpl = { name: "`name` like '%{0}%'", age: '`age` = {0}' };
16
+ const result = sql.tplQuery(param, sql_tpl);
17
+ const expected = "`name` like '%张三%' AND `age` = 25";
18
+
19
+ console.log('实际输出长度:', result.length);
20
+ console.log('期望输出长度:', expected.length);
21
+ console.log('实际输出字符:');
22
+ for (let i = 0; i < result.length; i++) {
23
+ console.log(` ${i}: '${result[i]}' (${result.charCodeAt(i)})`);
24
+ }
25
+ console.log('期望输出字符:');
26
+ for (let i = 0; i < expected.length; i++) {
27
+ console.log(` ${i}: '${expected[i]}' (${expected.charCodeAt(i)})`);
28
+ }
29
+
30
+ console.log('\n=== 详细调试tplBody基本功能 ===');
31
+ const param_body = { name: '李四', email: 'lisi@example.com' };
32
+ const sql_tpl_body = { name: '`name` = {0}' };
33
+ const res_body = sql.tplBody(param_body, sql_tpl_body);
34
+ const exp_body = "`name` = '李四' , `email` = 'lisi@example.com'";
35
+
36
+ console.log('实际输出长度:', res_body.length);
37
+ console.log('期望输出长度:', exp_body.length);
38
+ console.log('实际输出字符:');
39
+ for (let i = 0; i < res_body.length; i++) {
40
+ console.log(` ${i}: '${res_body[i]}' (${res_body.charCodeAt(i)})`);
41
+ }
42
+ console.log('期望输出字符:');
43
+ for (let i = 0; i < exp_body.length; i++) {
44
+ console.log(` ${i}: '${exp_body[i]}' (${exp_body.charCodeAt(i)})`);
45
+ }
package/debug_test3.js ADDED
@@ -0,0 +1,54 @@
1
+ // 模板替换调试
2
+
3
+ console.log('=== 详细调试tplBody模板替换 ===');
4
+ const param = { name: '李四', email: 'lisi@example.com' };
5
+ const sql_tpl = { name: '`name` = {0}' };
6
+
7
+ console.log('参数:', param);
8
+ console.log('模板:', sql_tpl);
9
+
10
+ // 手动模拟模板替换
11
+ const template = sql_tpl || {};
12
+ const segments = [];
13
+
14
+ for (const key in param) {
15
+ if (template[key]) {
16
+ // 替换占位符{0}为参数值
17
+ const seg = template[key].replace(/\{0\}/g, param[key]);
18
+ console.log(`模板替换 ${key}: "${template[key]}" -> "${seg}"`);
19
+ segments.push(seg);
20
+ }
21
+ else {
22
+ const seg = '`' + key + "` = '" + param[key] + "'";
23
+ console.log(`默认处理 ${key}: "${seg}"`);
24
+ segments.push(seg);
25
+ }
26
+ }
27
+
28
+ const result = segments.join(' , ');
29
+ console.log('最终结果:', result);
30
+
31
+ // 期望的结果应该是:`name` = '李四' , `email` = 'lisi@example.com'
32
+ // 但实际是:`name` = 李四 , `email` = 'lisi@example.com'
33
+ // 问题在于模板替换时没有添加单引号
34
+
35
+ console.log('\n=== 修复方案 ===');
36
+ // 正确的模板应该包含单引号
37
+ const right_tpl = { name: "`name` = '{0}'" };
38
+ const right_seg = [];
39
+
40
+ for (const key in param) {
41
+ if (right_tpl[key]) {
42
+ const seg = right_tpl[key].replace(/\{0\}/g, param[key]);
43
+ console.log(`正确模板替换 ${key}: "${right_tpl[key]}" -> "${seg}"`);
44
+ right_seg.push(seg);
45
+ }
46
+ else {
47
+ const seg = '`' + key + "` = '" + param[key] + "'";
48
+ console.log(`默认处理 ${key}: "${seg}"`);
49
+ right_seg.push(seg);
50
+ }
51
+ }
52
+
53
+ const right_res = right_seg.join(' , ');
54
+ console.log('正确结果:', right_res);