Split a retried drop out of close, and range-check the backoff

`close` fires only where nothing will bring the session back; a drop the
reconnect loop will retry is `disconnected`, pairing with `reconnected`.
`minDelay: 0` never doubles, so the backoff never started — both delays
are range-checked now, and only `false` spells reconnect off.
This commit is contained in:
2026-08-31 21:11:19 +02:00
parent 18177053bc
commit 880454b4f4
6 changed files with 111 additions and 8 deletions
+56 -1
View File
@@ -334,10 +334,65 @@ describe('reconnect', () => {
const reconnected = once<true>(resolve => { session.on('reconnected', () => { resolve(true); }); });
const dropped = session.sock;
await peerOf(smpp).close();
await reconnected;
assert.ok(session.loggedIn);
assert.notEqual(session.sock, dropped, 'the session should be live on a fresh socket');
});
test('reports a drop it will retry as disconnected, keeping close for the end', async t => {
const smpp = await startServer(t);
const { session } = await connect(t, smpp);
assert.ok(session);
const events: string[] = [];
session.on('close', () => { events.push('close'); });
session.on('disconnected', () => { events.push('disconnected'); });
const reconnected = once<true>(resolve => { session.on('reconnected', () => { resolve(true); }); });
await peerOf(smpp).close();
await reconnected;
assert.deepEqual(events, ['disconnected'], 'a link the loop brings back is not the end');
await session.close();
assert.deepEqual(events, ['disconnected', 'close']);
});
test('reports a drop as close when nothing will retry it', async t => {
const smpp = await startServer(t);
const { session } = await connect(t, smpp, { reconnect: false });
assert.ok(session);
const events: string[] = [];
session.on('disconnected', () => { events.push('disconnected'); });
const closed = once<true>(resolve => { session.on('close', () => { resolve(true); }); });
await peerOf(smpp).close();
await closed;
assert.deepEqual(events, []);
});
test('refuses a backoff that would retry without pausing', () => {
assert.match(checkSessionOptions({ reconnect: { minDelay: 0 } }).err?.message ?? '', /minDelay/);
assert.match(checkSessionOptions({ reconnect: { maxDelay: -1 } }).err?.message ?? '', /maxDelay/);
assert.match(
checkSessionOptions({ reconnect: null }).err?.message ?? '',
/false/,
'off is spelled false, so nothing else may stand in for it',
);
assert.equal(checkSessionOptions({ reconnect: false }).err, undefined);
assert.equal(checkSessionOptions({ reconnect: { maxDelay: 60_000, minDelay: 500 } }).err, undefined);
});
test('schedules nothing after a drop when reconnect is false', async t => {
+5 -1
View File
@@ -1408,7 +1408,11 @@ describe('application hooks that throw or reject', () => {
describe('link timers', () => {
test('closes a client link the peer has stopped answering', async t => {
const peer = await bindOnlyPeer(t);
const { err, session } = await client({ enquireLinkInterval: 50, port: peer.port });
const { err, session } = await client({
enquireLinkInterval: 50,
port: peer.port,
reconnect: false,
});
assert.equal(err, undefined);
assert.ok(session);