├── .eslintrc.js ├── .github ├── dependabot.yml └── workflows │ └── tests.yml ├── .gitignore ├── LICENSE ├── README.md ├── lib └── index.js ├── package-lock.json ├── package.json └── test └── index.js /.eslintrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | env: { 5 | es6: true, 6 | node: true 7 | }, 8 | plugins: ['node'], 9 | extends: ['plugin:node/recommended', 'eslint-config-salesflare'], 10 | rules: {} 11 | }; 12 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | allow: 9 | - dependency-type: "production" 10 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Test CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | strategy: 15 | matrix: 16 | node-version: [12, 14, 16] 17 | 18 | steps: 19 | - uses: actions/checkout@v2 20 | - name: Use Node.js ${{ matrix.node-version }} 21 | uses: actions/setup-node@v2 22 | with: 23 | node-version: ${{ matrix.node-version }} 24 | - name: Install dependencies 25 | run: npm ci 26 | - name: Set up MySQL 27 | run: | 28 | sudo systemctl start mysql.service 29 | mysql -e 'CREATE DATABASE test' -uroot -proot 30 | mysql -e 'USE `test`; CREATE TABLE IF NOT EXISTS `test` (`id` int(11) NOT NULL AUTO_INCREMENT, primary key (id)) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;' -uroot -proot 31 | - run: npm test -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | build/Release 21 | 22 | # Dependency directory 23 | # Commenting this out is preferred by some people, see 24 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 25 | node_modules 26 | 27 | # Users Environment Variables 28 | .lock-wscript 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Salesflare 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hapi-plugin-mysql [![Build Status](https://travis-ci.org/Salesflare/hapi-plugin-mysql.svg?branch=master)](https://travis-ci.org/Salesflare/hapi-plugin-mysql) 2 | 3 | > hapi plugin for MySQL 4 | 5 | ## What 6 | 7 | Attaches a MySQL connection from a pool to every request. 8 | 9 | ## How 10 | 11 | Via `request.app.db`. 12 | You can also manually get a connection from the server via `server.getDb(function (err, connection) {})`. 13 | 14 | ```javascript 15 | await server.register({ 16 | plugin: require('hapi-plugin-mysql'), 17 | options: { 18 | host: "localhost", 19 | user: "root", 20 | password: "" 21 | } 22 | }); 23 | 24 | server.route({ 25 | method: 'GET', 26 | path: '/', 27 | handler: async (request, h) => { 28 | 29 | request.app.db.query(...); 30 | return 'ok'; 31 | } 32 | }); 33 | ``` 34 | 35 | The options are the same options you can pass onto the `mysql` lib for making a connection. See for more info on the `mysql` lib itself. 36 | 37 | The keyword `db` is used because `connection` was used by `<= hapi@16` and could have caused confusion/collision. 38 | 39 | If you want more manual control or you want to use the same pool outside of the hapi part of your server 40 | you can initialize the pool before the plugin registration by calling `await HapiPluginMysql.init(options)` and then call `require('hapi-plugin-mysql').getConnection` to get a connection from the pool. 41 | If you still want to register the plugin (to get all the goodies) just don't pass any options to the plugin registration 42 | and it will use the same pool as first created. 43 | To manually stop the pool call `await HapiPluginMySQL.stop()`. 44 | See the tests for more granular use cases. 45 | 46 | ## Catches 47 | 48 | - Transactions are no longer a part of this plugin and should be handled (with care) in your code 49 | 50 | ## Testing 51 | 52 | - almost 100% code coverage! If you know how to test these last case please do let me know or PR :O 53 | - The tests requires you to have a `test` db with a table `test` and `{user: root, password: ""}`. 54 | - See `.travis.yml` and the tests for more info. 55 | 56 | ## Changelog 57 | 58 | See the [releases](https://github.com/Salesflare/hapi-plugin-mysql/releases) page 59 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Util = require('util'); 4 | 5 | const MySQL = require('mysql'); 6 | const Hoek = require('@hapi/hoek'); 7 | 8 | const internals = { 9 | pool: null 10 | }; 11 | 12 | internals.attachConnection = async (request, h) => { 13 | 14 | const connection = await internals.getConnection(); 15 | 16 | request.app.db = connection; 17 | request.app.connection = connection; 18 | 19 | return h.continue; 20 | }; 21 | 22 | /** 23 | * Returns a promise if no callback is provided 24 | * Promise will resolve with a promisified connection.query 25 | * 26 | * @param {function(Error, Object):void} callback 27 | * @returns {Promise | void} 28 | */ 29 | exports.getConnection = async (callback) => { 30 | 31 | let connection; 32 | try { 33 | connection = await internals.getConnection(); 34 | } 35 | catch (err) { 36 | if (callback) { 37 | return callback(err); 38 | } 39 | 40 | throw err; 41 | } 42 | 43 | if (callback) { 44 | return callback(null, connection); 45 | } 46 | 47 | return connection; 48 | }; 49 | 50 | internals.getConnection = () => { 51 | 52 | Hoek.assert(internals.pool, 'No mysql pool found'); 53 | 54 | return new Promise((resolve, reject) => { 55 | 56 | return internals.pool.getConnection((err, connection) => { 57 | 58 | if (err) { 59 | return reject(err); 60 | } 61 | 62 | // Since commit/rollback/beginTransaction uses the .query it will auto promisify them 63 | // Node's `util.promisify` adds a symbol with the promisified version of the function 64 | // After promisifying `connection.query` also still works with callbacks 65 | connection.query = Util.promisify(connection.query); 66 | 67 | return resolve(connection); 68 | }); 69 | }); 70 | }; 71 | 72 | internals.response = (request) => { 73 | 74 | // Since db and connection is the same connection we only need to release once here 75 | if (request.app.db) { 76 | request.app.db.release(); 77 | } 78 | }; 79 | 80 | exports.stop = internals.stop = () => { 81 | 82 | return new Promise((resolve, reject) => { 83 | 84 | return internals.pool.end((err) => { 85 | 86 | delete internals.pool; 87 | 88 | if (err) { 89 | return reject(err); 90 | } 91 | 92 | return resolve(); 93 | }); 94 | }); 95 | }; 96 | 97 | exports.init = internals.init = async (baseOptions = {}) => { 98 | 99 | const hasOptions = Object.keys(baseOptions).length > 0; 100 | 101 | if (!internals.pool && !hasOptions) { 102 | throw new Error('No pool and no options to create one found, call `init` or `register` with options first'); 103 | } 104 | 105 | if (internals.pool) { 106 | // Calling init and then register with no options should work 107 | if (!hasOptions) { 108 | return; 109 | } 110 | 111 | // Error on trying to init multiple times 112 | throw new Error('There is already a pool configured'); 113 | } 114 | 115 | if (!Object.prototype.hasOwnProperty.call(baseOptions, 'host') && !Object.prototype.hasOwnProperty.call(baseOptions, 'socketPath')) { 116 | throw new Error('Options must include `host` or `socketPath` property'); 117 | } 118 | 119 | const options = Hoek.clone(baseOptions); 120 | internals.pool = MySQL.createPool(options); 121 | 122 | // Test connection 123 | let connection; 124 | try { 125 | connection = await internals.getConnection(); 126 | } 127 | catch (err) { 128 | delete internals.pool; 129 | throw err; 130 | } 131 | finally { 132 | // Release test connection 133 | if (connection) { 134 | connection.release(); 135 | } 136 | } 137 | }; 138 | 139 | exports.plugin = { 140 | pkg: require('../package.json'), 141 | register: async function (server, baseOptions) { 142 | 143 | await internals.init(baseOptions); 144 | 145 | // Add connection to request object 146 | server.ext('onPreAuth', internals.attachConnection); 147 | 148 | // End connection after request finishes 149 | server.events.on('response', internals.response); 150 | 151 | // Try to close pool on server end 152 | server.ext('onPostStop', internals.stop); 153 | 154 | // Add getDb() function to `server` 155 | server.decorate('server', 'getDb', exports.getConnection); 156 | server.decorate('server', 'getConnection', exports.getConnection); 157 | 158 | server.log(['hapi-plugin-mysql', 'database'], 'Connection to the database successful'); 159 | } 160 | }; 161 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hapi-plugin-mysql", 3 | "version": "7.2.7", 4 | "description": "Hapi plugin for MySQL", 5 | "main": "lib/index.js", 6 | "private": false, 7 | "scripts": { 8 | "lint": "eslint --fix .", 9 | "test": "lab -m 5000 -a @hapi/code -vL --lint-fix -t 95" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/Salesflare/hapi-plugin-mysql.git" 14 | }, 15 | "keywords": [ 16 | "hapi", 17 | "mysql", 18 | "plugin" 19 | ], 20 | "author": "Adri Van Houdt ", 21 | "license": "MIT", 22 | "bugs": { 23 | "url": "https://github.com/Salesflare/hapi-plugin-mysql/issues" 24 | }, 25 | "homepage": "https://github.com/Salesflare/hapi-plugin-mysql", 26 | "dependencies": { 27 | "@hapi/hoek": "^9.2.0", 28 | "mysql": "^2.18.1" 29 | }, 30 | "peerDependencies": { 31 | "@hapi/hapi": ">=19.0.0" 32 | }, 33 | "engines": { 34 | "node": ">=12.18.0" 35 | }, 36 | "devDependencies": { 37 | "@hapi/code": "^8.0.5", 38 | "@hapi/hapi": "^20.2.1", 39 | "@hapi/lab": "^24.5.0", 40 | "eslint": "^7.32.0", 41 | "eslint-config-salesflare": "^5.4.0", 42 | "eslint-plugin-node": "^11.1.0" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Lab = require('@hapi/lab'); // eslint-disable-line node/no-unpublished-require 4 | const Code = require('@hapi/code'); // eslint-disable-line node/no-unpublished-require 5 | const Hapi = require('@hapi/hapi'); 6 | const Hoek = require('@hapi/hoek'); 7 | 8 | const lab = exports.lab = Lab.script(); 9 | const describe = lab.experiment; 10 | const it = lab.it; 11 | const expect = Code.expect; 12 | 13 | 14 | const internals = { 15 | dbOptions: { 16 | host: 'localhost', 17 | user: 'root', 18 | password: 'root', 19 | database: 'test' 20 | } 21 | }; 22 | 23 | internals.insertHandler = (request) => { 24 | 25 | const sql = 'INSERT INTO test SET id = null'; 26 | 27 | expect(request.app.db, 'db connection').to.exist(); 28 | 29 | return new Promise((resolve) => { 30 | 31 | return request.app.db.query(sql, (err, results) => { 32 | 33 | expect(err, 'error').to.not.exist(); 34 | expect(results.insertId, 'insert Id').to.exist(); 35 | 36 | return resolve(results.affectedRows); 37 | }); 38 | }); 39 | }; 40 | 41 | internals.selectHandler = (request) => { 42 | 43 | const sql = 'SELECT * FROM test'; 44 | 45 | expect(request.app.db, 'db connection').to.exist(); 46 | 47 | return new Promise((resolve) => { 48 | 49 | return request.app.db.query(sql, (err, results) => { 50 | 51 | expect(err, 'error').to.not.exist(); 52 | 53 | return resolve(results); 54 | }); 55 | }); 56 | }; 57 | 58 | describe('Hapi MySQL', () => { 59 | 60 | describe('Basics', () => { 61 | 62 | it('Makes a db connection that works', async () => { 63 | 64 | const options = Hoek.clone(internals.dbOptions); 65 | 66 | const server = Hapi.Server(); 67 | 68 | await server.register({ 69 | plugin: require('..'), 70 | options 71 | }); 72 | 73 | server.route([{ 74 | method: 'POST', 75 | path: '/test', 76 | config: { 77 | handler: internals.insertHandler 78 | } 79 | }, { 80 | method: 'GET', 81 | path: '/test', 82 | config: { 83 | handler: internals.selectHandler 84 | } 85 | }]); 86 | 87 | const response = await server.inject({ 88 | method: 'POST', 89 | url: '/test' 90 | }); 91 | 92 | expect(response.statusCode, 'post status code').to.equal(200); 93 | expect(response.result, 'post result').to.be.above(0); 94 | 95 | const getResponse = await server.inject({ 96 | method: 'GET', 97 | url: '/test' 98 | }); 99 | 100 | expect(getResponse.statusCode, 'get status code').to.equal(200); 101 | expect(getResponse.result.length, 'get result').to.be.above(0); 102 | 103 | return server.stop(); 104 | }); 105 | 106 | it('Makes a promisified db connection that works', async () => { 107 | 108 | const options = Hoek.clone(internals.dbOptions); 109 | 110 | const server = Hapi.Server(); 111 | 112 | await server.register({ 113 | plugin: require('..'), 114 | options 115 | }); 116 | 117 | server.route([{ 118 | method: 'GET', 119 | path: '/test', 120 | config: { 121 | handler: (request) => { 122 | 123 | return request.app.connection.query('SELECT * FROM test'); 124 | } 125 | } 126 | }]); 127 | 128 | const getResponse = await server.inject({ 129 | method: 'GET', 130 | url: '/test' 131 | }); 132 | 133 | expect(getResponse.statusCode, 'get status code').to.equal(200); 134 | expect(getResponse.result.length, 'get result').to.be.above(0); 135 | 136 | return server.stop(); 137 | }); 138 | 139 | it('Returns a promisified connection on server.getConnection', async () => { 140 | 141 | const options = Hoek.clone(internals.dbOptions); 142 | 143 | const server = Hapi.Server(); 144 | 145 | await server.register({ 146 | plugin: require('..'), 147 | options 148 | }); 149 | 150 | server.route([{ 151 | method: 'GET', 152 | path: '/test', 153 | config: { 154 | handler: async (request) => { 155 | 156 | const connection = await request.server.getConnection(); 157 | return connection.query('SELECT * FROM test'); 158 | } 159 | } 160 | }]); 161 | 162 | const getResponse = await server.inject({ 163 | method: 'GET', 164 | url: '/test' 165 | }); 166 | 167 | expect(getResponse.statusCode, 'get status code').to.equal(200); 168 | expect(getResponse.result.length, 'get result').to.be.above(0); 169 | 170 | return server.stop(); 171 | }); 172 | 173 | it('Quite fail when connection is deleted', async () => { 174 | 175 | const options = Hoek.clone(internals.dbOptions); 176 | 177 | const server = Hapi.Server(); 178 | 179 | await server.register({ 180 | plugin: require('..'), 181 | options 182 | }); 183 | 184 | server.route([{ 185 | method: 'GET', 186 | path: '/test', 187 | config: { 188 | handler: (request) => { 189 | 190 | request.app.db = undefined; 191 | return 'ok'; 192 | } 193 | } 194 | }]); 195 | 196 | const response = await server.inject({ 197 | method: 'GET', 198 | url: '/test' 199 | }); 200 | 201 | expect(response.statusCode, 'post status code').to.equal(200); 202 | expect(response.result, 'post result').to.equal('ok'); 203 | 204 | return server.stop(); 205 | }); 206 | 207 | it('Pool is ended on Server.stop()', async () => { 208 | 209 | const options = Hoek.clone(internals.dbOptions); 210 | 211 | const server = Hapi.Server(); 212 | 213 | await server.register({ 214 | plugin: require('..'), 215 | options 216 | }); 217 | 218 | await server.start(); 219 | 220 | return server.stop(); 221 | }); 222 | }); 223 | 224 | describe('Init', () => { 225 | 226 | it('Registers using `init`', async () => { 227 | 228 | const options = Hoek.clone(internals.dbOptions); 229 | 230 | const MySQLPlugin = require('..'); 231 | 232 | await MySQLPlugin.init(options); 233 | return MySQLPlugin.stop(); 234 | }); 235 | 236 | it('Registers with calling `init` and then using it as a plugin with no options', async () => { 237 | 238 | const options = Hoek.clone(internals.dbOptions); 239 | 240 | const MySQLPlugin = require('..'); 241 | 242 | await MySQLPlugin.init(options); 243 | 244 | const server = Hapi.Server(); 245 | 246 | await server.register({ 247 | plugin: MySQLPlugin 248 | }); 249 | 250 | return server.stop(); 251 | }); 252 | 253 | it('Registers using `socketPath`', async () => { 254 | 255 | const options = Hoek.clone(internals.dbOptions); 256 | delete options.host; 257 | options.socketPath = '/test.db'; 258 | 259 | const MySQLPlugin = require('..'); 260 | 261 | let threw = false; 262 | 263 | try { 264 | await MySQLPlugin.init(options); 265 | } 266 | catch (err) { 267 | // We expect it to throw ENOENT as we don't setup a socket path for testing. 268 | // The test will fail if we would block init just because there is no host. 269 | expect(err).to.be.an.error(); 270 | expect(err.message).to.include('ENOENT'); 271 | 272 | threw = true; 273 | } 274 | 275 | expect(threw).to.be.true(); 276 | }); 277 | 278 | it('Errors on registering twice', async () => { 279 | 280 | const options = Hoek.clone(internals.dbOptions); 281 | 282 | const MySQLPlugin = require('..'); 283 | 284 | await MySQLPlugin.init(options); 285 | 286 | let threw = false; 287 | 288 | try { 289 | await MySQLPlugin.init(options); 290 | } 291 | catch (err) { 292 | expect(err).to.be.an.error('There is already a pool configured'); 293 | threw = true; 294 | } 295 | 296 | expect(threw).to.be.true(); 297 | 298 | return MySQLPlugin.stop(); 299 | }); 300 | 301 | it('Errors on registering with no options', async () => { 302 | 303 | const MySQLPlugin = require('..'); 304 | 305 | let threw = false; 306 | 307 | try { 308 | await MySQLPlugin.init({}); 309 | } 310 | catch (err) { 311 | expect(err).to.be.an.error('No pool and no options to create one found, call `init` or `register` with options first'); 312 | threw = true; 313 | } 314 | 315 | expect(threw).to.be.true(); 316 | }); 317 | 318 | it('Errors on registering with no host or socketPath options', async () => { 319 | 320 | const options = Hoek.clone(internals.dbOptions); 321 | delete options.host; 322 | delete options.socketPath; 323 | 324 | const MySQLPlugin = require('..'); 325 | 326 | let threw = false; 327 | 328 | try { 329 | await MySQLPlugin.init(options); 330 | } 331 | catch (err) { 332 | expect(err).to.be.an.error(); 333 | expect(err).to.be.an.error('Options must include `host` or `socketPath` property'); 334 | threw = true; 335 | } 336 | 337 | expect(threw).to.be.true(); 338 | }); 339 | 340 | it('Errors when options are wrong', async () => { 341 | 342 | const options = Hoek.clone(internals.dbOptions); 343 | options.host = 'test'; 344 | 345 | const MySQLPlugin = require('..'); 346 | 347 | let threw = false; 348 | 349 | try { 350 | await MySQLPlugin.init(options); 351 | } 352 | catch (err) { 353 | expect(err).to.be.an.error(); 354 | expect(err.message).to.contain('getaddrinfo'); 355 | 356 | threw = true; 357 | } 358 | 359 | expect(threw).to.be.true(); 360 | }); 361 | }); 362 | 363 | describe('Extras', () => { 364 | 365 | it('Exposes getDb on the server', async () => { 366 | 367 | const options = Hoek.clone(internals.dbOptions); 368 | 369 | const server = Hapi.Server(); 370 | 371 | await server.register({ 372 | plugin: require('..'), 373 | options 374 | }); 375 | 376 | expect(server.getDb, 'getDb').to.exist(); 377 | 378 | await new Promise((resolve) => { 379 | 380 | return server.getDb((err, db) => { 381 | 382 | expect(err).to.not.exist(); 383 | expect(db, 'db').to.exist(); 384 | 385 | return resolve(); 386 | }); 387 | }); 388 | 389 | return server.stop(); 390 | }); 391 | 392 | it('Exposes getConnection on the server', async () => { 393 | 394 | const options = Hoek.clone(internals.dbOptions); 395 | 396 | const server = Hapi.Server(); 397 | 398 | await server.register({ 399 | plugin: require('..'), 400 | options 401 | }); 402 | 403 | expect(server.getConnection, 'getConnection').to.exist(); 404 | 405 | const connection = await server.getConnection(); 406 | 407 | expect(connection, 'connection').to.exist(); 408 | 409 | return server.stop(); 410 | }); 411 | 412 | it('Exposes `getConnection` on the module', async () => { 413 | 414 | const MySQLPlugin = require('..'); 415 | 416 | await MySQLPlugin.init(internals.dbOptions); 417 | expect(MySQLPlugin.getConnection).to.be.a.function(); 418 | expect(await MySQLPlugin.getConnection()).to.exist(); 419 | 420 | return MySQLPlugin.stop(); 421 | }); 422 | 423 | it('Exposes `getConnection` on the module with a callback', async () => { 424 | 425 | const MySQLPlugin = require('..'); 426 | 427 | await MySQLPlugin.init(internals.dbOptions); 428 | expect(MySQLPlugin.getConnection).to.be.a.function(); 429 | 430 | // By stopping we test both that getConnection takes a callback and that it returns errors properly 431 | await MySQLPlugin.stop(); 432 | return new Promise((resolve) => { 433 | 434 | return MySQLPlugin.getConnection((err) => { 435 | 436 | expect(err).to.exist(); 437 | return resolve(); 438 | }); 439 | }); 440 | }); 441 | 442 | it('Promisified commit/rollback/beginTransaction', async () => { 443 | 444 | const MySQLPlugin = require('..'); 445 | 446 | await MySQLPlugin.init(internals.dbOptions); 447 | expect(MySQLPlugin.getConnection).to.be.a.function(); 448 | const connection = await MySQLPlugin.getConnection(); 449 | 450 | await connection.beginTransaction(); 451 | await connection.commit(); 452 | await connection.rollback(); 453 | 454 | return MySQLPlugin.stop(); 455 | }); 456 | 457 | it('Promisified `.query` usage with callbacks', async () => { 458 | 459 | const MySQLPlugin = require('..'); 460 | 461 | await MySQLPlugin.init(internals.dbOptions); 462 | 463 | const connection = await MySQLPlugin.getConnection(); 464 | 465 | return new Promise((resolve) => { 466 | 467 | return connection.query('INSERT INTO test SET id = null', (err, results) => { 468 | 469 | expect(err, 'error').to.not.exist(); 470 | expect(results.insertId, 'insert Id').to.exist(); 471 | 472 | return resolve(); 473 | }); 474 | }); 475 | }); 476 | }); 477 | }); 478 | --------------------------------------------------------------------------------