Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 116 additions & 0 deletions test/integration/test-pool-queue-event.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
'use strict';

const assert = require('assert');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there a specific limitation or motivation for using Node.js assert in this test? For example:

const { assert, test } = require('poku');

// Corrected path based on file structure: ../common.test.cjs
// const {test} = require('../common.test.cjs');
const { test } = require('poku');
const mysql = require('../..');
const process = require('process'); // Ensure process is available

// Helper function to create a pool using test environment variables
function createTestPool(options) {
const config = {
host: process.env.MYSQL_HOST,
user: process.env.MYSQL_USER,
password: process.env.MYSQL_PASSWORD,
database: process.env.MYSQL_DATABASE,
...options,
};
return mysql.createPool(config);
}
Comment on lines +11 to +20
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use the connection from const { createPool } = require('../common.test.cjs'), for example:

const { createPool } = require('../common.test.cjs');
const { assert } = require('poku');
const pool = createPool();


// --- Test Case ---

test('Pool emits connectionQueueAcquired event and reports correct queue depth', function (done) {
Copy link
Collaborator

@wellwelwel wellwelwel Nov 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no done parameter in the tests, I think this is due to the callbacks. If you have any questions about how to wait the callbacks, please feel free to ask 🙋🏻‍♂️

// 1. Setup Pool: Small limit to force queuing
const connectionLimit = 2;
const pool = createTestPool({
connectionLimit: connectionLimit,
waitForConnections: true,
queueLimit: 0, // No queue limit
});

let acquiredConnections = [];
let queuedAcquiredEvents = [];
const expectedQueueDepths = [1, 0]; // Expected depth after 1st dequeue, then 2nd

// 2. Listen for the new event
pool.on('connectionQueueAcquired', (event) => {
queuedAcquiredEvents.push(event);
});

// Helper to get a connection and wrap it in a Promise for control flow
const getConnectionPromise = () => new Promise((resolve, reject) => {
pool.getConnection((err, conn) => {
if (err) return reject(err);

// Store initial connections (1 and 2) for later release
// We rely on the order of acquisition for this.
acquiredConnections.push(conn);

resolve(conn);
});
});

// --- Execution Flow using Promises for reliability ---

let initialConnection1;
let initialConnection2;

// 1 & 2. Saturate the pool (Acquire 2 connections)
Promise.all([
getConnectionPromise().then(c => initialConnection1 = c),
getConnectionPromise().then(c => initialConnection2 = c)
])
.then(() => {
// 3 & 4. Queue requests (Request 2 more connections, they must wait)
// These will use the general callback logic below
pool.getConnection(queuedConnectionCallback);
pool.getConnection(queuedConnectionCallback);

// Give time for the requests to enter the queue before releasing connections
return new Promise(r => setTimeout(r, 50));
})
.then(() => {
// 5. Trigger Dequeue: Release the initial active connections
// This is safe because the Promise.all ensures initialConnection1/2 are defined.

// Release 1: serves Queued Request 3 (event fired, depth 1)
initialConnection1.release();

// Release 2: serves Queued Request 4 (event fired, depth 0)
initialConnection2.release();

// Wait for the final two queued connections to be acquired and their callbacks to run
return new Promise(r => setTimeout(r, 100));
})
.then(() => {
// Final Assertions and Cleanup (after all acquisitions are complete)
Copy link
Collaborator

@wellwelwel wellwelwel Nov 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An interesting approach to testing is to use the test descriptions themselves to "comment" (describe) on what is happening. Not as a rule, but by reserving comments within the code for specific or complex behaviors.

You can, for example, combine describe and it to make it easier to describe the tests, as well as the messages in the assertions themselves 💡

assert.strictEqual(queuedAcquiredEvents.length, 2, 'Should have emitted connectionQueueAcquired exactly twice.');

queuedAcquiredEvents.forEach((event, index) => {
const expectedDepth = expectedQueueDepths[index];
assert.strictEqual(
event.queueDepth,
expectedDepth,
`Event ${index + 1} reported queueDepth ${event.queueDepth}, expected ${expectedDepth}.`
);
assert.ok(event.connection, `Event ${index + 1} is missing the connection object.`);

// Release connections acquired from the queue
event.connection.release();
});

pool.end(done);
})
.catch(err => {
pool.end(() => done(err));
});
});

// For the purposes of this test, we don't need the queued connections to be stored,
// as they are handled and released inside the final Promise block via the event payload.
function queuedConnectionCallback(err, conn) {
if (err) throw err
}
Comment on lines +113 to +115
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recommend declaring functions/variables before they are used 🙋🏻‍♂️