-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
161 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
import { expect } from 'chai'; | ||
import { once } from 'events'; | ||
import * as sinon from 'sinon'; | ||
|
||
import { | ||
Connection, | ||
MongoError, | ||
MongoErrorLabel, | ||
MongoNetworkError, | ||
MongoNetworkTimeoutError, | ||
ObjectId, | ||
ServerType, | ||
TopologyType | ||
} from '../../../src'; | ||
import { Server } from '../../../src/sdam/server'; | ||
import { ServerDescription } from '../../../src/sdam/server_description'; | ||
import { Topology } from '../../../src/sdam/topology'; | ||
import { sleep } from '../../tools/utils'; | ||
|
||
const handledErrors = [ | ||
{ | ||
description: 'any non-timeout network error', | ||
errorClass: MongoNetworkError, | ||
errorArgs: ['TestError'] | ||
}, | ||
{ | ||
description: 'a network timeout error before handshake', | ||
errorClass: MongoNetworkTimeoutError, | ||
errorArgs: ['TestError', { beforeHandshake: true }] | ||
}, | ||
{ | ||
description: 'an auth handshake error', | ||
errorClass: MongoError, | ||
errorArgs: ['TestError'], | ||
errorLabel: MongoErrorLabel.HandshakeError | ||
} | ||
]; | ||
|
||
const unhandledErrors = [ | ||
{ | ||
description: 'a non-MongoError', | ||
errorClass: Error, | ||
errorArgs: ['TestError'] | ||
}, | ||
{ | ||
description: 'a network timeout error after handshake', | ||
errorClass: MongoNetworkTimeoutError, | ||
errorArgs: ['TestError', { beforeHandshake: false }] | ||
}, | ||
{ | ||
description: 'a non-network non-handshake MongoError', | ||
errorClass: MongoError, | ||
errorArgs: ['TestError'] | ||
} | ||
]; | ||
|
||
describe('Server', () => { | ||
describe('#handleError', () => { | ||
let server: Server, connection: Connection | undefined; | ||
beforeEach(() => { | ||
server = new Server(new Topology([], {} as any), new ServerDescription('a:1'), {} as any); | ||
}); | ||
for (const loadBalanced of [true, false]) { | ||
const mode = loadBalanced ? 'loadBalanced' : 'non-loadBalanced'; | ||
const contextSuffix = loadBalanced ? ' with connection provided' : ''; | ||
context(`in ${mode} mode${contextSuffix}`, () => { | ||
beforeEach(() => { | ||
if (loadBalanced) { | ||
server.s.topology.description.type = TopologyType.LoadBalanced; | ||
connection = { serviceId: new ObjectId() } as Connection; | ||
server.s.pool.clear = sinon.stub(); | ||
} else { | ||
connection = undefined; | ||
} | ||
}); | ||
for (const { description, errorClass, errorArgs, errorLabel } of handledErrors) { | ||
const handledDescription = loadBalanced | ||
? `should reset the pool but not attach a ResetPool label to the error or mark the server unknown on ${description}` | ||
: `should attach a ResetPool label to the error and mark the server unknown on ${description}`; | ||
it(`${handledDescription}`, async () => { | ||
// @ts-expect-error because of varied number of args | ||
const error = new errorClass(...errorArgs); | ||
if (errorLabel) { | ||
error.addErrorLabel(errorLabel); | ||
} | ||
const newDescriptionEvent = Promise.race([ | ||
once(server, Server.DESCRIPTION_RECEIVED), | ||
sleep(1000) | ||
]); | ||
server.handleError(error, connection); | ||
if (!loadBalanced) { | ||
expect( | ||
error.hasErrorLabel(MongoErrorLabel.ResetPool), | ||
'expected error to have a ResetPool label' | ||
).to.be.true; | ||
} else { | ||
expect( | ||
error.hasErrorLabel(MongoErrorLabel.ResetPool), | ||
'expected error NOT to have a ResetPool label' | ||
).to.be.false; | ||
} | ||
const newDescription = await newDescriptionEvent; | ||
if (!loadBalanced) { | ||
expect(newDescription).to.have.nested.property('[0].type', ServerType.Unknown); | ||
} else { | ||
expect(newDescription).to.be.undefined; | ||
expect(server.s.pool.clear).to.have.been.calledOnceWith(connection!.serviceId); | ||
} | ||
}); | ||
|
||
it(`should not attach a ResetPool label to the error or mark the server unknown on ${description} if it is stale`, async () => { | ||
// @ts-expect-error because of varied number of args | ||
const error = new errorClass(...errorArgs); | ||
if (errorLabel) { | ||
error.addErrorLabel(errorLabel); | ||
} | ||
|
||
error.connectionGeneration = -1; | ||
expect( | ||
server.s.pool.generation, | ||
'expected test server to have a pool of generation 0' | ||
).to.equal(0); // sanity check | ||
|
||
const newDescriptionEvent = Promise.race([ | ||
once(server, Server.DESCRIPTION_RECEIVED), | ||
sleep(1000) | ||
]); | ||
server.handleError(error, connection); | ||
expect( | ||
error.hasErrorLabel(MongoErrorLabel.ResetPool), | ||
'expected error NOT to have a ResetPool label' | ||
).to.be.false; | ||
const newDescription = await newDescriptionEvent; | ||
expect(newDescription).to.be.undefined; | ||
}); | ||
} | ||
|
||
for (const { description, errorClass, errorArgs } of unhandledErrors) { | ||
it(`should not attach a ResetPool label to the error or mark the server unknown on ${description}`, async () => { | ||
// @ts-expect-error because of varied number of args | ||
const error = new errorClass(...errorArgs); | ||
|
||
const newDescriptionEvent = Promise.race([ | ||
once(server, Server.DESCRIPTION_RECEIVED), | ||
sleep(1000) | ||
]); | ||
server.handleError(error, connection); | ||
if (error instanceof MongoError) { | ||
expect( | ||
error.hasErrorLabel(MongoErrorLabel.ResetPool), | ||
'expected error NOT to have a ResetPool label' | ||
).to.be.false; | ||
} | ||
const newDescription = await newDescriptionEvent; | ||
expect(newDescription).to.be.undefined; | ||
}); | ||
} | ||
}); | ||
} | ||
}); | ||
}); |