Skip to content
Open
Show file tree
Hide file tree
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
11 changes: 9 additions & 2 deletions lib/base/connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
constructor(opts) {
super();
this.config = opts.config;
this.state = 'disconnected';
// TODO: fill defaults
// if no params, connect to /var/lib/mysql/mysql.sock ( /tmp/mysql.sock on OSX )
// if host is given, connect to host:3306
Expand Down Expand Up @@ -134,10 +135,12 @@
}
this._handshakePacket = handshakeCommand.handshake;
this.threadId = handshakeCommand.handshake.connectionId;
this.state = "connected";

Check failure on line 138 in lib/base/connection.js

View workflow job for this annotation

GitHub Actions / lint-js

Replace `"connected"` with `'connected'`
this.emit('connect', handshakeCommand.handshake);
});
handshakeCommand.on('error', (err) => {
this._closing = true;
this.state = "disconnected";

Check failure on line 143 in lib/base/connection.js

View workflow job for this annotation

GitHub Actions / lint-js

Replace `"disconnected"` with `'disconnected'`
this._notifyError(err);
});
this.addCommand(handshakeCommand);
Expand Down Expand Up @@ -207,6 +210,7 @@
// notify all commands in the queue and bubble error as connection "error"
// called on stream error or unexpected termination
_notifyError(err) {
this.state = "protocol_error";

Check failure on line 213 in lib/base/connection.js

View workflow job for this annotation

GitHub Actions / lint-js

Replace `"protocol_error"` with `'protocol_error'`
if (this.connectTimeout) {
Timers.clearTimeout(this.connectTimeout);
this.connectTimeout = null;
Expand Down Expand Up @@ -358,8 +362,8 @@
checkServerIdentity: verifyIdentity
? Tls.checkServerIdentity
: function () {
return undefined;
},
return undefined;

Check failure on line 365 in lib/base/connection.js

View workflow job for this annotation

GitHub Actions / lint-js

Insert `··`
},

Check failure on line 366 in lib/base/connection.js

View workflow job for this annotation

GitHub Actions / lint-js

Insert `··`
secureContext,
isServer: false,
socket: this.stream,
Expand Down Expand Up @@ -409,6 +413,7 @@
const err = new Error(message);
err.fatal = true;
err.code = code || 'PROTOCOL_ERROR';
this.state = "protocol_error";

Check failure on line 416 in lib/base/connection.js

View workflow job for this annotation

GitHub Actions / lint-js

Replace `"protocol_error"` with `'protocol_error'`
this.emit('error', err);
}

Expand Down Expand Up @@ -769,6 +774,7 @@
this.connectTimeout = null;
}
this._closing = true;
this.state = 'disconnected';
this.stream.end();
this.addCommand = this._addCommandClosedState;
}
Expand Down Expand Up @@ -893,6 +899,7 @@

// ===============================================================
end(callback) {
this.state = 'disconnected';
if (this.config.isServer) {
this._closing = true;
const quitCmd = new EventEmitter();
Expand Down
98 changes: 98 additions & 0 deletions test/unit/connection/test-connection-state.test.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
'use strict';
const { assert, describe, it, beforeEach, afterEach } = require('poku');
const common = require('../../common.test.cjs');

(async () => {
await describe('Connection state', async () => {
let connection;

beforeEach(() => (connection = common.createConnection().promise()));

afterEach(async () => {
if (!connection) return;
try {
await connection.end();
} catch (e) {
// ignore teardown errors
}
});

await it('connects and sets state to connected', async () => {
await connection?.connect();
assert.equal(connection.connection.state, 'connected');
});

await it('connects and disconnect: sets state to connected and disconnected close() is called.', async () => {
await connection?.connect();
assert.equal(connection.connection.state, 'connected');
await connection.close();
assert.equal(connection.connection.state, 'disconnected');
});

await it('connects and disconnect: sets state to connected and disconnected when end() is called.', async () => {
await connection?.connect();
assert.equal(connection.connection.state, 'connected');
await connection.end();
assert.equal(connection.connection.state, 'disconnected');
});

await it('state is protocol_error if creds are wrong.', async () => {
const badConnection = common.createConnection({ password: "WR0NG", user: 'wrong' }).promise();

Check failure on line 40 in test/unit/connection/test-connection-state.test.cjs

View workflow job for this annotation

GitHub Actions / lint-js

Replace `.createConnection({·password:·"WR0NG",·user:·'wrong'·})` with `⏎········.createConnection({·password:·'WR0NG',·user:·'wrong'·})⏎········`
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
const badConnection = common.createConnection({ password: "WR0NG", user: 'wrong' }).promise();
const badConnection = common.createConnection({ password: "WR0NG_PASSWORD", user: 'WRONG_USER' }).promise();

try {
await badConnection.connect();
assert.fail('expected connect() to throw for bad credentials');
} catch (err) {
assert.equal(badConnection.connection.state, 'protocol_error');
} finally {
try {
await bad.end();

Check failure on line 48 in test/unit/connection/test-connection-state.test.cjs

View workflow job for this annotation

GitHub Actions / lint-js

'bad' is not defined
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
await bad.end();
await badConnection.end();

} catch (e) { }

Check failure on line 49 in test/unit/connection/test-connection-state.test.cjs

View workflow job for this annotation

GitHub Actions / lint-js

Delete `·`

Check failure on line 49 in test/unit/connection/test-connection-state.test.cjs

View workflow job for this annotation

GitHub Actions / lint-js

Empty block statement
}
});

await it('ping keeps connection operational', async () => {
await connection?.connect();
await connection.ping();
assert.equal(connection.connection.state, 'connected');
});

await it('simple query keeps state operational and returns rows', async () => {
await connection?.connect();
const [rows] = await connection.query('SELECT 1 AS x');
assert.equal(rows[0].x, 1);
assert.equal(connection.connection.state, 'connected');
});

await it('socket destroy produces disconnected', async () => {
await connection?.connect();
const raw = connection.connection;
// attach error listener early to avoid uncaught exceptions
let sawError = null;
raw.once('error', (e) => { sawError = e; });
// Force a network-style drop
raw.stream.destroy(new Error('simulate network drop'));
// wait a bit for event propagation
await new Promise((r) => setTimeout(r, 50));
assert.ok(sawError instanceof Error, 'expected an error emitted');
assert.equal(connection.connection.state, 'disconnected');
});

await it('changeUser to invalid account yields disconnected', async () => {
await connection?.connect();
const raw = connection.connection;
// use changeUser to induce auth error (assuming user 'nope' doesn't exist)
let gotErr = null;
assert.equal(raw.state, "connected");
raw.on('error', (e) => { gotErr = e; });
try {
await connection.changeUser({ user: 'nope', password: 'bad' });
} catch (err) {
Comment on lines +82 to +89
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
const raw = connection.connection;
// use changeUser to induce auth error (assuming user 'nope' doesn't exist)
let gotErr = null;
assert.equal(raw.state, "connected");
raw.on('error', (e) => { gotErr = e; });
try {
await connection.changeUser({ user: 'nope', password: 'bad' });
} catch (err) {
const raw = connection.connection;
let gotErr = null;
assert.equal(raw.state, "connected");
raw.on('error', (e) => { gotErr = e; });
try {
await connection.changeUser({ user: 'NON_EXISTENT_USER', password: 'BAD_PASSWORD' });
} catch (err) {

gotErr = err;
}
// allow propagation
await new Promise((r) => setTimeout(r, 20));
assert.ok(gotErr instanceof Error, 'expected changeUser to emit error');
assert.equal(raw.state, 'disconnected');
});
});
})();
Loading