|
| 1 | +import { expect } from 'chai'; |
| 2 | +import { type TopologyDescription } from 'mongodb-legacy'; |
| 3 | +import * as sinon from 'sinon'; |
| 4 | + |
| 5 | +import { |
| 6 | + MongoClient, |
| 7 | + ObjectId, |
| 8 | + Server, |
| 9 | + ServerDescription, |
| 10 | + Topology, |
| 11 | + type TopologyDescriptionChangedEvent |
| 12 | +} from '../../mongodb'; |
| 13 | + |
| 14 | +describe('Server Discovery and Monitoring', function () { |
| 15 | + let serverConnect: sinon.SinonStub; |
| 16 | + let topologySelectServer: sinon.SinonStub; |
| 17 | + let client: MongoClient; |
| 18 | + let events: TopologyDescriptionChangedEvent[]; |
| 19 | + |
| 20 | + function getNewDescription() { |
| 21 | + const topologyDescriptionChanged = events[events.length - 1]; |
| 22 | + return topologyDescriptionChanged.newDescription; |
| 23 | + } |
| 24 | + |
| 25 | + beforeEach(async function () { |
| 26 | + serverConnect = sinon.stub(Server.prototype, 'connect').callsFake(function () { |
| 27 | + this.s.state = 'connected'; |
| 28 | + this.emit('connect'); |
| 29 | + }); |
| 30 | + |
| 31 | + topologySelectServer = sinon |
| 32 | + .stub(Topology.prototype, 'selectServer') |
| 33 | + .callsFake(async function (_selector, _options) { |
| 34 | + topologySelectServer.restore(); |
| 35 | + |
| 36 | + const fakeServer = { s: { state: 'connected' }, removeListener: () => true }; |
| 37 | + return fakeServer; |
| 38 | + }); |
| 39 | + |
| 40 | + events = []; |
| 41 | + client = new MongoClient('mongodb://a/?replicaSet=rs'); |
| 42 | + client.on('topologyDescriptionChanged', event => events.push(event)); |
| 43 | + await client.connect(); |
| 44 | + |
| 45 | + // Start with a as primary |
| 46 | + client.topology.serverUpdateHandler( |
| 47 | + new ServerDescription('a:27017', { |
| 48 | + ok: 1, |
| 49 | + helloOk: true, |
| 50 | + isWritablePrimary: true, |
| 51 | + hosts: ['a:27017', 'b:27017'], |
| 52 | + setName: 'rs', |
| 53 | + setVersion: 1, |
| 54 | + electionId: ObjectId.createFromHexString('000000000000000000000001'), |
| 55 | + minWireVersion: 0, |
| 56 | + maxWireVersion: 21 |
| 57 | + }) |
| 58 | + ); |
| 59 | + |
| 60 | + // b is elected as primary, a gets marked stale |
| 61 | + client.topology.serverUpdateHandler( |
| 62 | + new ServerDescription('b:27017', { |
| 63 | + ok: 1, |
| 64 | + helloOk: true, |
| 65 | + isWritablePrimary: true, |
| 66 | + hosts: ['a:27017', 'b:27017'], |
| 67 | + setName: 'rs', |
| 68 | + setVersion: 2, |
| 69 | + electionId: ObjectId.createFromHexString('000000000000000000000001'), |
| 70 | + minWireVersion: 0, |
| 71 | + maxWireVersion: 21 |
| 72 | + }) |
| 73 | + ); |
| 74 | + }); |
| 75 | + |
| 76 | + afterEach(async function () { |
| 77 | + serverConnect.restore(); |
| 78 | + await client.close().catch(() => null); |
| 79 | + }); |
| 80 | + |
| 81 | + let newDescription: TopologyDescription; |
| 82 | + |
| 83 | + describe('when a newer primary is detected', function () { |
| 84 | + it('steps down original primary to unknown server description with appropriate error message', function () { |
| 85 | + newDescription = getNewDescription(); |
| 86 | + |
| 87 | + const aOutcome = newDescription.servers.get('a:27017'); |
| 88 | + const bOutcome = newDescription.servers.get('b:27017'); |
| 89 | + expect(aOutcome.type).to.equal('Unknown'); |
| 90 | + expect(aOutcome.error).to.match(/primary marked stale due to discovery of newer primary/); |
| 91 | + |
| 92 | + expect(bOutcome.type).to.equal('RSPrimary'); |
| 93 | + }); |
| 94 | + }); |
| 95 | + |
| 96 | + describe('when a stale primary still reports itself as primary', function () { |
| 97 | + it('gets marked as unknown with an error message with the new and old replicaSetVersion and electionId', function () { |
| 98 | + // a still incorrectly reports as primary |
| 99 | + client.topology.serverUpdateHandler( |
| 100 | + new ServerDescription('a:27017', { |
| 101 | + ok: 1, |
| 102 | + helloOk: true, |
| 103 | + isWritablePrimary: true, |
| 104 | + hosts: ['a:27017', 'b:27017'], |
| 105 | + setName: 'rs', |
| 106 | + setVersion: 1, |
| 107 | + electionId: ObjectId.createFromHexString('000000000000000000000001'), |
| 108 | + minWireVersion: 0, |
| 109 | + maxWireVersion: 21 |
| 110 | + }) |
| 111 | + ); |
| 112 | + |
| 113 | + newDescription = getNewDescription(); |
| 114 | + |
| 115 | + const aOutcome = newDescription.servers.get('a:27017'); |
| 116 | + |
| 117 | + expect(aOutcome.type).to.equal('Unknown'); |
| 118 | + expect(aOutcome.error).to.match( |
| 119 | + /primary marked stale due to electionId\/setVersion mismatch: server setVersion: \d+, server electionId: \d{24}, topology setVersion: \d+, topology electionId: \d{24}/ |
| 120 | + ); |
| 121 | + }); |
| 122 | + }); |
| 123 | +}); |
0 commit comments