-
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.
Browse files
Browse the repository at this point in the history
- Loading branch information
1 parent
68074f9
commit f1c1ca0
Showing
4 changed files
with
196 additions
and
78 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
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,36 @@ | ||
import { join } from 'path'; | ||
|
||
import { | ||
collectServerSelectionLogicTests, | ||
runServerSelectionLogicTest | ||
} from './server_selection_logic_spec_utils'; | ||
|
||
describe('Server Selection Logic (spec)', function () { | ||
beforeEach(function () { | ||
if (this.currentTest.title.match(/Possible/)) { | ||
this.currentTest.skipReason = 'Nodejs driver does not support PossiblePrimary'; | ||
this.skip(); | ||
} | ||
|
||
if (this.currentTest.title.match(/nearest_multiple/i)) { | ||
this.currentTest.skipReason = 'TODO(NODE-4188): localThresholdMS should default to 15ms'; | ||
this.skip(); | ||
} | ||
}); | ||
|
||
const selectionSpecDir = join(__dirname, '../../spec/server-selection/server_selection'); | ||
const serverSelectionLogicTests = collectServerSelectionLogicTests(selectionSpecDir); | ||
for (const topologyType of Object.keys(serverSelectionLogicTests)) { | ||
describe(topologyType, function () { | ||
for (const subType of Object.keys(serverSelectionLogicTests[topologyType])) { | ||
describe(subType, function () { | ||
for (const test of serverSelectionLogicTests[topologyType][subType]) { | ||
it(test.name, function () { | ||
runServerSelectionLogicTest(test); | ||
}); | ||
} | ||
}); | ||
} | ||
}); | ||
} | ||
}); |
155 changes: 155 additions & 0 deletions
155
test/unit/assorted/server_selection_logic_spec_utils.ts
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,155 @@ | ||
import { Document, EJSON } from 'bson'; | ||
import { expect } from 'chai'; | ||
import { readdirSync, readFileSync, statSync } from 'fs'; | ||
import { basename, extname, join } from 'path'; | ||
|
||
import { | ||
ReadPreference, | ||
ReadPreferenceMode, | ||
ReadPreferenceOptions | ||
} from '../../../src/read_preference'; | ||
import { ServerType, TopologyType } from '../../../src/sdam/common'; | ||
import { ServerDescription, TagSet } from '../../../src/sdam/server_description'; | ||
import { | ||
readPreferenceServerSelector, | ||
writableServerSelector | ||
} from '../../../src/sdam/server_selection'; | ||
import { TopologyDescription } from '../../../src/sdam/topology_description'; | ||
import { serverDescriptionFromDefinition } from './server_selection_spec_helper'; | ||
|
||
interface ServerSelectionLogicTestServer { | ||
address: string; | ||
avg_rtt_ms: number; | ||
type: ServerType; | ||
tags?: TagSet; | ||
} | ||
interface ServerSelectionLogicTest { | ||
topology_description: { | ||
type: TopologyType; | ||
servers: ServerSelectionLogicTestServer[]; | ||
}; | ||
operation: 'read' | 'write'; | ||
read_preference: { | ||
mode: ReadPreferenceMode; | ||
tag_sets?: TagSet[]; | ||
}; | ||
/** | ||
* The spec says we should confirm the list of suitable servers in addition to the list of | ||
* servers in the latency window, if possible. We apply the latency window inside the | ||
* selector so for Node this is not possible. | ||
* https://github.com/mongodb/specifications/tree/master/source/server-selection/tests#server-selection-logic-tests | ||
*/ | ||
suitable_servers: never; | ||
in_latency_window: ServerSelectionLogicTestServer[]; | ||
} | ||
|
||
function readPreferenceFromDefinition(definition) { | ||
const mode = definition.mode | ||
? definition.mode.charAt(0).toLowerCase() + definition.mode.slice(1) | ||
: 'primary'; | ||
|
||
const options: ReadPreferenceOptions = {}; | ||
if (typeof definition.maxStalenessSeconds !== 'undefined') | ||
options.maxStalenessSeconds = definition.maxStalenessSeconds; | ||
const tags = definition.tag_sets ?? []; | ||
|
||
return new ReadPreference(mode, tags, options); | ||
} | ||
|
||
/** | ||
* Compares two server descriptions and compares all fields that are present | ||
* in the yaml spec tests. | ||
*/ | ||
function compareServerDescriptions(s1: ServerDescription, s2: ServerDescription) { | ||
expect(s1.address).to.equal(s2.address); | ||
expect(s1.roundTripTime).to.equal(s2.roundTripTime); | ||
expect(s1.type).to.equal(s2.type); | ||
expect(s1.tags).to.deep.equal(s2.tags); | ||
} | ||
|
||
function serverDescriptionsToMap( | ||
descriptions: ServerDescription[] | ||
): Map<string, ServerDescription> { | ||
const descriptionMap = new Map<string, ServerDescription>(); | ||
|
||
for (const description of descriptions) { | ||
descriptionMap.set(description.address, description); | ||
} | ||
|
||
return descriptionMap; | ||
} | ||
|
||
/** | ||
* Executes a server selection logic test | ||
* @see https://github.com/mongodb/specifications/tree/master/source/server-selection/tests#server-selection-logic-tests | ||
*/ | ||
export function runServerSelectionLogicTest(testDefinition: ServerSelectionLogicTest) { | ||
const allHosts = testDefinition.topology_description.servers.map(({ address }) => address); | ||
const serversInTopology = testDefinition.topology_description.servers.map(s => | ||
serverDescriptionFromDefinition(s, allHosts) | ||
); | ||
const serverDescriptions = serverDescriptionsToMap(serversInTopology); | ||
const topologyDescription = new TopologyDescription( | ||
testDefinition.topology_description.type, | ||
serverDescriptions | ||
); | ||
const expectedServers = serverDescriptionsToMap( | ||
testDefinition.in_latency_window.map(s => serverDescriptionFromDefinition(s)) | ||
); | ||
|
||
let selector; | ||
if (testDefinition.operation === 'write') { | ||
selector = writableServerSelector(); | ||
} else if (testDefinition.operation === 'read' || testDefinition.read_preference) { | ||
const readPreference = readPreferenceFromDefinition(testDefinition.read_preference); | ||
selector = readPreferenceServerSelector(readPreference); | ||
} else { | ||
expect.fail('test operation was neither read nor write, and no read preference was provided.'); | ||
} | ||
|
||
const result = selector(topologyDescription, serversInTopology); | ||
|
||
expect(result.length).to.equal(expectedServers.size); | ||
|
||
for (const server of result) { | ||
const expectedServer = expectedServers.get(server.address); | ||
expect(expectedServer).to.exist; | ||
compareServerDescriptions(server, expectedServer); | ||
expectedServers.delete(server.address); | ||
} | ||
|
||
expect(expectedServers.size).to.equal(0); | ||
} | ||
|
||
/** | ||
* reads in the server selection logic tests from the provided directory | ||
*/ | ||
export function collectServerSelectionLogicTests(specDir) { | ||
const testTypes = readdirSync(specDir).filter(d => statSync(join(specDir, d)).isDirectory()); | ||
|
||
const tests = {}; | ||
for (const testType of testTypes) { | ||
const testsOfType = readdirSync(join(specDir, testType)).filter(d => | ||
statSync(join(specDir, testType, d)).isDirectory() | ||
); | ||
const result = {}; | ||
for (const subType of testsOfType) { | ||
result[subType] = readdirSync(join(specDir, testType, subType)) | ||
.filter(f => extname(f) === '.json') | ||
.map(f => { | ||
const fileContents = readFileSync(join(specDir, testType, subType, f), { | ||
encoding: 'utf-8' | ||
}); | ||
const test = EJSON.parse(fileContents, { relaxed: true }) as unknown as Document; | ||
test.name = basename(f, '.json'); | ||
test.type = testType; | ||
test.subType = subType; | ||
return test; | ||
}); | ||
} | ||
|
||
tests[testType] = result; | ||
} | ||
|
||
return tests; | ||
} |
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