diff --git a/src/admin.ts b/src/admin.ts index 0ec2e47763..28efb5cd43 100644 --- a/src/admin.ts +++ b/src/admin.ts @@ -1,6 +1,5 @@ import { type Document, resolveBSONOptions } from './bson'; import type { Db } from './db'; -import { AddUserOperation, type AddUserOptions } from './operations/add_user'; import type { CommandOperationOptions } from './operations/command'; import { executeOperation } from './operations/execute_operation'; import { @@ -120,33 +119,6 @@ export class Admin { return this.command({ ping: 1 }, options); } - /** - * Add a user to the database - * - * @param username - The username for the new user - * @param passwordOrOptions - An optional password for the new user, or the options for the command - * @param options - Optional settings for the command - * @deprecated Use the createUser command in `db.command()` instead. - * @see https://www.mongodb.com/docs/manual/reference/command/createUser/ - */ - async addUser( - username: string, - passwordOrOptions?: string | AddUserOptions, - options?: AddUserOptions - ): Promise { - options = - options != null && typeof options === 'object' - ? options - : passwordOrOptions != null && typeof passwordOrOptions === 'object' - ? passwordOrOptions - : undefined; - const password = typeof passwordOrOptions === 'string' ? passwordOrOptions : undefined; - return executeOperation( - this.s.db.client, - new AddUserOperation(this.s.db, username, password, { dbName: 'admin', ...options }) - ); - } - /** * Remove a user from a database * diff --git a/src/collection.ts b/src/collection.ts index 941618f4e7..6b4a97031c 100644 --- a/src/collection.ts +++ b/src/collection.ts @@ -80,7 +80,6 @@ import { } from './operations/search_indexes/create'; import { DropSearchIndexOperation } from './operations/search_indexes/drop'; import { UpdateSearchIndexOperation } from './operations/search_indexes/update'; -import { type CollStats, CollStatsOperation, type CollStatsOptions } from './operations/stats'; import { ReplaceOneOperation, type ReplaceOptions, @@ -799,21 +798,6 @@ export class Collection { ); } - /** - * Get all the collection statistics. - * - * @deprecated the `collStats` operation will be removed in the next major release. Please - * use an aggregation pipeline with the [`$collStats`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/collStats/) stage instead - * - * @param options - Optional settings for the command - */ - async stats(options?: CollStatsOptions): Promise { - return executeOperation( - this.client, - new CollStatsOperation(this as TODO_NODE_3286, options) as TODO_NODE_3286 - ); - } - /** * Find a document and delete it in one atomic operation. Requires a write lock for the duration of the operation. * diff --git a/src/db.ts b/src/db.ts index 08b9195444..3c710fbab0 100644 --- a/src/db.ts +++ b/src/db.ts @@ -9,7 +9,6 @@ import { RunCommandCursor, type RunCursorCommandOptions } from './cursor/run_com import { MongoAPIError, MongoInvalidArgumentError } from './error'; import type { MongoClient, PkFactory } from './mongo_client'; import type { TODO_NODE_3286 } from './mongo_types'; -import { AddUserOperation, type AddUserOptions } from './operations/add_user'; import type { AggregateOptions } from './operations/aggregate'; import { CollectionsOperation } from './operations/collections'; import type { IndexInformationOptions } from './operations/common_functions'; @@ -425,33 +424,6 @@ export class Db { ); } - /** - * Add a user to the database - * - * @param username - The username for the new user - * @param passwordOrOptions - An optional password for the new user, or the options for the command - * @param options - Optional settings for the command - * @deprecated Use the createUser command in `db.command()` instead. - * @see https://www.mongodb.com/docs/manual/reference/command/createUser/ - */ - async addUser( - username: string, - passwordOrOptions?: string | AddUserOptions, - options?: AddUserOptions - ): Promise { - options = - options != null && typeof options === 'object' - ? options - : passwordOrOptions != null && typeof passwordOrOptions === 'object' - ? passwordOrOptions - : undefined; - const password = typeof passwordOrOptions === 'string' ? passwordOrOptions : undefined; - return executeOperation( - this.client, - new AddUserOperation(this, username, password, resolveOptions(this, options)) - ); - } - /** * Remove a user from a database * diff --git a/src/index.ts b/src/index.ts index 26282013f3..9e94d18dca 100644 --- a/src/index.ts +++ b/src/index.ts @@ -378,7 +378,6 @@ export type { WithId, WithoutId } from './mongo_types'; -export type { AddUserOptions, RoleSpecification } from './operations/add_user'; export type { AggregateOperation, AggregateOptions, @@ -434,12 +433,7 @@ export type { RenameOptions } from './operations/rename'; export type { RunCommandOptions } from './operations/run_command'; export type { SearchIndexDescription } from './operations/search_indexes/create'; export type { SetProfilingLevelOptions } from './operations/set_profiling_level'; -export type { - CollStats, - CollStatsOptions, - DbStatsOptions, - WiredTigerData -} from './operations/stats'; +export type { DbStatsOptions } from './operations/stats'; export type { ReplaceOptions, UpdateOptions, diff --git a/src/operations/add_user.ts b/src/operations/add_user.ts deleted file mode 100644 index 40268e241c..0000000000 --- a/src/operations/add_user.ts +++ /dev/null @@ -1,117 +0,0 @@ -import * as crypto from 'crypto'; - -import type { Document } from '../bson'; -import type { Db } from '../db'; -import { MongoInvalidArgumentError } from '../error'; -import type { Server } from '../sdam/server'; -import type { ClientSession } from '../sessions'; -import { type Callback, emitWarningOnce, getTopology } from '../utils'; -import { CommandOperation, type CommandOperationOptions } from './command'; -import { Aspect, defineAspects } from './operation'; - -/** - * @public - * @deprecated Use the createUser command directly instead. - */ -export interface RoleSpecification { - /** - * A role grants privileges to perform sets of actions on defined resources. - * A given role applies to the database on which it is defined and can grant access down to a collection level of granularity. - */ - role: string; - /** The database this user's role should effect. */ - db: string; -} - -/** - * @public - * @deprecated Use the createUser command directly instead. - */ -export interface AddUserOptions extends CommandOperationOptions { - /** Roles associated with the created user */ - roles?: string | string[] | RoleSpecification | RoleSpecification[]; - /** Custom data associated with the user (only Mongodb 2.6 or higher) */ - customData?: Document; -} - -/** @internal */ -export class AddUserOperation extends CommandOperation { - override options: AddUserOptions; - db: Db; - username: string; - password?: string; - - constructor(db: Db, username: string, password: string | undefined, options?: AddUserOptions) { - super(db, options); - - this.db = db; - this.username = username; - this.password = password; - this.options = options ?? {}; - } - - override execute(server: Server, session: ClientSession | undefined): Promise { - const db = this.db; - const username = this.username; - const password = this.password; - const options = this.options; - - // Error out if digestPassword set - // v5 removed the digestPassword option from AddUserOptions but we still want to throw - // an error when digestPassword is provided. - if ('digestPassword' in options && options.digestPassword != null) { - throw new MongoInvalidArgumentError( - 'Option "digestPassword" not supported via addUser, use db.command(...) instead' - ); - } - - let roles; - if (!options.roles || (Array.isArray(options.roles) && options.roles.length === 0)) { - emitWarningOnce( - 'Creating a user without roles is deprecated. Defaults to "root" if db is "admin" or "dbOwner" otherwise' - ); - if (db.databaseName.toLowerCase() === 'admin') { - roles = ['root']; - } else { - roles = ['dbOwner']; - } - } else { - roles = Array.isArray(options.roles) ? options.roles : [options.roles]; - } - - const topology = getTopology(db); - - const digestPassword = topology.lastHello().maxWireVersion >= 7; - - let userPassword = password; - - if (!digestPassword) { - // Use node md5 generator - const md5 = crypto.createHash('md5'); - // Generate keys used for authentication - md5.update(`${username}:mongo:${password}`); - userPassword = md5.digest('hex'); - } - - // Build the command to execute - const command: Document = { - createUser: username, - customData: options.customData || {}, - roles: roles, - digestPassword - }; - - // No password - if (typeof password === 'string') { - command.pwd = userPassword; - } - - return super.executeCommand(server, session, command); - } - - executeCallback(_server: Server, _session: ClientSession | undefined, _callback: Callback): void { - throw new Error('Method not implemented'); - } -} - -defineAspects(AddUserOperation, [Aspect.WRITE_OPERATION]); diff --git a/src/operations/stats.ts b/src/operations/stats.ts index bb73d447d6..d538460582 100644 --- a/src/operations/stats.ts +++ b/src/operations/stats.ts @@ -1,60 +1,11 @@ import type { Document } from '../bson'; -import type { Collection } from '../collection'; import type { Db } from '../db'; import type { Server } from '../sdam/server'; import type { ClientSession } from '../sessions'; import type { Callback } from '../utils'; -import { - CommandCallbackOperation, - CommandOperation, - type CommandOperationOptions -} from './command'; +import { CommandOperation, type CommandOperationOptions } from './command'; import { Aspect, defineAspects } from './operation'; -/** - * @public - * @deprecated the `collStats` operation will be removed in the next major release. Please - * use an aggregation pipeline with the [`$collStats`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/collStats/) stage instead - */ -export interface CollStatsOptions extends CommandOperationOptions { - /** Divide the returned sizes by scale value. */ - scale?: number; -} - -/** - * Get all the collection statistics. - * @internal - */ -export class CollStatsOperation extends CommandCallbackOperation { - override options: CollStatsOptions; - collectionName: string; - - /** - * Construct a Stats operation. - * - * @param collection - Collection instance - * @param options - Optional settings. See Collection.prototype.stats for a list of options. - */ - constructor(collection: Collection, options?: CollStatsOptions) { - super(collection, options); - this.options = options ?? {}; - this.collectionName = collection.collectionName; - } - - override executeCallback( - server: Server, - session: ClientSession | undefined, - callback: Callback - ): void { - const command: Document = { collStats: this.collectionName }; - if (this.options.scale != null) { - command.scale = this.options.scale; - } - - super.executeCommandCallback(server, session, command, callback); - } -} - /** @public */ export interface DbStatsOptions extends CommandOperationOptions { /** Divide the returned sizes by scale value. */ @@ -88,201 +39,4 @@ export class DbStatsOperation extends CommandOperation { } } -/** - * @deprecated the `collStats` operation will be removed in the next major release. Please - * use an aggregation pipeline with the [`$collStats`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/collStats/) stage instead - * @public - * @see https://www.mongodb.com/docs/manual/reference/command/collStats/ - */ -export interface CollStats extends Document { - /** Namespace */ - ns: string; - /** Number of documents */ - count: number; - /** Collection size in bytes */ - size: number; - /** Average object size in bytes */ - avgObjSize: number; - /** (Pre)allocated space for the collection in bytes */ - storageSize: number; - /** Number of extents (contiguously allocated chunks of datafile space) */ - numExtents: number; - /** Number of indexes */ - nindexes: number; - /** Size of the most recently created extent in bytes */ - lastExtentSize: number; - /** Padding can speed up updates if documents grow */ - paddingFactor: number; - /** A number that indicates the user-set flags on the collection. userFlags only appears when using the mmapv1 storage engine */ - userFlags?: number; - /** Total index size in bytes */ - totalIndexSize: number; - /** Size of specific indexes in bytes */ - indexSizes: { - _id_: number; - [index: string]: number; - }; - /** `true` if the collection is capped */ - capped: boolean; - /** The maximum number of documents that may be present in a capped collection */ - max: number; - /** The maximum size of a capped collection */ - maxSize: number; - /** This document contains data reported directly by the WiredTiger engine and other data for internal diagnostic use */ - wiredTiger?: WiredTigerData; - /** The fields in this document are the names of the indexes, while the values themselves are documents that contain statistics for the index provided by the storage engine */ - indexDetails?: any; - ok: number; - - /** The amount of storage available for reuse. The scale argument affects this value. */ - freeStorageSize?: number; - /** An array that contains the names of the indexes that are currently being built on the collection */ - indexBuilds?: number; - /** The sum of the storageSize and totalIndexSize. The scale argument affects this value */ - totalSize: number; - /** The scale value used by the command. */ - scaleFactor: number; -} - -/** - * @public - * @deprecated This type is only used for the deprecated `collStats` operation and will be removed in the next major release. - */ -export interface WiredTigerData extends Document { - LSM: { - 'bloom filter false positives': number; - 'bloom filter hits': number; - 'bloom filter misses': number; - 'bloom filter pages evicted from cache': number; - 'bloom filter pages read into cache': number; - 'bloom filters in the LSM tree': number; - 'chunks in the LSM tree': number; - 'highest merge generation in the LSM tree': number; - 'queries that could have benefited from a Bloom filter that did not exist': number; - 'sleep for LSM checkpoint throttle': number; - 'sleep for LSM merge throttle': number; - 'total size of bloom filters': number; - } & Document; - 'block-manager': { - 'allocations requiring file extension': number; - 'blocks allocated': number; - 'blocks freed': number; - 'checkpoint size': number; - 'file allocation unit size': number; - 'file bytes available for reuse': number; - 'file magic number': number; - 'file major version number': number; - 'file size in bytes': number; - 'minor version number': number; - }; - btree: { - 'btree checkpoint generation': number; - 'column-store fixed-size leaf pages': number; - 'column-store internal pages': number; - 'column-store variable-size RLE encoded values': number; - 'column-store variable-size deleted values': number; - 'column-store variable-size leaf pages': number; - 'fixed-record size': number; - 'maximum internal page key size': number; - 'maximum internal page size': number; - 'maximum leaf page key size': number; - 'maximum leaf page size': number; - 'maximum leaf page value size': number; - 'maximum tree depth': number; - 'number of key/value pairs': number; - 'overflow pages': number; - 'pages rewritten by compaction': number; - 'row-store internal pages': number; - 'row-store leaf pages': number; - } & Document; - cache: { - 'bytes currently in the cache': number; - 'bytes read into cache': number; - 'bytes written from cache': number; - 'checkpoint blocked page eviction': number; - 'data source pages selected for eviction unable to be evicted': number; - 'hazard pointer blocked page eviction': number; - 'in-memory page passed criteria to be split': number; - 'in-memory page splits': number; - 'internal pages evicted': number; - 'internal pages split during eviction': number; - 'leaf pages split during eviction': number; - 'modified pages evicted': number; - 'overflow pages read into cache': number; - 'overflow values cached in memory': number; - 'page split during eviction deepened the tree': number; - 'page written requiring lookaside records': number; - 'pages read into cache': number; - 'pages read into cache requiring lookaside entries': number; - 'pages requested from the cache': number; - 'pages written from cache': number; - 'pages written requiring in-memory restoration': number; - 'tracked dirty bytes in the cache': number; - 'unmodified pages evicted': number; - } & Document; - cache_walk: { - 'Average difference between current eviction generation when the page was last considered': number; - 'Average on-disk page image size seen': number; - 'Clean pages currently in cache': number; - 'Current eviction generation': number; - 'Dirty pages currently in cache': number; - 'Entries in the root page': number; - 'Internal pages currently in cache': number; - 'Leaf pages currently in cache': number; - 'Maximum difference between current eviction generation when the page was last considered': number; - 'Maximum page size seen': number; - 'Minimum on-disk page image size seen': number; - 'On-disk page image sizes smaller than a single allocation unit': number; - 'Pages created in memory and never written': number; - 'Pages currently queued for eviction': number; - 'Pages that could not be queued for eviction': number; - 'Refs skipped during cache traversal': number; - 'Size of the root page': number; - 'Total number of pages currently in cache': number; - } & Document; - compression: { - 'compressed pages read': number; - 'compressed pages written': number; - 'page written failed to compress': number; - 'page written was too small to compress': number; - 'raw compression call failed, additional data available': number; - 'raw compression call failed, no additional data available': number; - 'raw compression call succeeded': number; - } & Document; - cursor: { - 'bulk-loaded cursor-insert calls': number; - 'create calls': number; - 'cursor-insert key and value bytes inserted': number; - 'cursor-remove key bytes removed': number; - 'cursor-update value bytes updated': number; - 'insert calls': number; - 'next calls': number; - 'prev calls': number; - 'remove calls': number; - 'reset calls': number; - 'restarted searches': number; - 'search calls': number; - 'search near calls': number; - 'truncate calls': number; - 'update calls': number; - }; - reconciliation: { - 'dictionary matches': number; - 'fast-path pages deleted': number; - 'internal page key bytes discarded using suffix compression': number; - 'internal page multi-block writes': number; - 'internal-page overflow keys': number; - 'leaf page key bytes discarded using prefix compression': number; - 'leaf page multi-block writes': number; - 'leaf-page overflow keys': number; - 'maximum blocks required for a page': number; - 'overflow values written': number; - 'page checksum matches': number; - 'page reconciliation calls': number; - 'page reconciliation calls for eviction': number; - 'pages deleted': number; - } & Document; -} - -defineAspects(CollStatsOperation, [Aspect.READ_OPERATION]); defineAspects(DbStatsOperation, [Aspect.READ_OPERATION]); diff --git a/test/integration/connection-monitoring-and-pooling/connection.test.ts b/test/integration/connection-monitoring-and-pooling/connection.test.ts index 731a141550..d340cad8a3 100644 --- a/test/integration/connection-monitoring-and-pooling/connection.test.ts +++ b/test/integration/connection-monitoring-and-pooling/connection.test.ts @@ -247,123 +247,33 @@ describe('Connection', function () { client.connect(); }); - function connectionTester(configuration, testName, callback) { - return function (err, client) { - expect(err).to.not.exist; - const db = client.db(configuration.db); - - db.createCollection(testName, function (err, collection) { - expect(err).to.not.exist; - - collection.insert({ foo: 123 }, { writeConcern: { w: 1 } }, function (err) { - expect(err).to.not.exist; - - db.dropDatabase(function (err, dropped) { - expect(err).to.not.exist; - test.ok(dropped); - if (callback) return callback(client); - }); - }); - }); - }; - } - - it('test connect no options', { - metadata: { requires: { topology: 'single' } }, - - test: function (done) { - const configuration = this.configuration; - client = configuration.newClient(); - - client.connect( - connectionTester(configuration, 'testConnectNoOptions', function () { - done(); - }) - ); - } - }); - - it('test connect good auth', { - metadata: { requires: { topology: 'single' } }, - - test: function (done) { - const configuration = this.configuration; - const username = 'testConnectGoodAuth'; - const password = 'password'; - - client = configuration.newClient(); - - // First add a user. - const db = client.db(configuration.db); - - db.addUser(username, password, function (err) { - expect(err).to.not.exist; - restOfTest(); - }); - - function restOfTest() { - testClient = configuration.newClient(configuration.url({ username, password })); - testClient.connect( - connectionTester(configuration, 'testConnectGoodAuth', function () { - done(); - }) - ); - } - } - }); - - it('test connect good auth as option', { - metadata: { requires: { topology: 'single' } }, - - test: function (done) { - const configuration = this.configuration; - const username = 'testConnectGoodAuthAsOption'; - const password = 'password'; - - // First add a user. - client = configuration.newClient(); - const db = client.db(configuration.db); - - db.addUser(username, password, { roles: ['readWrite', 'dbAdmin'] }, function (err) { - expect(err).to.not.exist; - restOfTest(); - }); - - function restOfTest() { - const opts = { auth: { username, password }, authSource: configuration.db }; - - testClient = configuration.newClient(opts); - - testClient.connect( - connectionTester(configuration, 'testConnectGoodAuthAsOption', function () { - done(); - }) - ); - } - } - }); + context('when connecting with a username and password', () => { + let utilClient: MongoClient; + let client: MongoClient; + const username = 'spot'; + const password = 'dogsRCool'; + + beforeEach(async function () { + utilClient = this.configuration.newClient(); + await utilClient.db().admin().command({ createUser: username, pwd: password, roles: [] }); + }); - it('test connect bad auth', async function () { - client = this.configuration.newClient({ - auth: { - username: 'slithy', - password: 'toves' - } + afterEach(async () => { + await utilClient.db().admin().command({ dropUser: username }); + await client?.close(); + await utilClient?.close(); }); - const error = await client.connect().catch(error => error); - expect(error).to.be.instanceOf(MongoServerError); - await client.close(); - }); - it('test connect bad url', { - metadata: { requires: { topology: 'single' } }, + it('accepts a client that provides the correct username and password', async function () { + client = this.configuration.newClient({ auth: { username, password } }); + await client.connect(); + }); - test: function () { - const configuration = this.configuration; - expect(() => - configuration.newClient('mangodb://localhost:27017/test?safe=false') - ).to.throw(); - } + it('rejects a client that provides the incorrect username and password', async function () { + client = this.configuration.newClient({ auth: { username: 'u', password: 'p' } }); + const error = await client.connect().catch(error => error); + expect(error).to.be.instanceOf(MongoServerError); + }); }); }); }); diff --git a/test/integration/enumerate_databases.test.ts b/test/integration/enumerate_databases.test.ts index decb0ac892..0f7f03db9a 100644 --- a/test/integration/enumerate_databases.test.ts +++ b/test/integration/enumerate_databases.test.ts @@ -1,7 +1,7 @@ import { expect } from 'chai'; import { once } from 'events'; -import { type AddUserOptions, type MongoClient, MongoServerError } from '../mongodb'; +import { type MongoClient, MongoServerError } from '../mongodb'; import { TestBuilder, UnifiedTestSuiteBuilder } from '../tools/utils'; const metadata: MongoDBMetadataUI = { @@ -25,10 +25,6 @@ describe('listDatabases()', function () { let adminClient: MongoClient; let authorizedClient: MongoClient; - const authorizedUserOptions: AddUserOptions = { - roles: [{ role: 'read', db: mockAuthorizedDb }] - }; - beforeEach(async function () { adminClient = this.configuration.newClient(); @@ -37,7 +33,11 @@ describe('listDatabases()', function () { .createCollection(mockAuthorizedCollection) .catch(() => null); - await adminClient.db('admin').addUser(username, password, authorizedUserOptions); + await adminClient.db('admin').command({ + createUser: username, + pwd: password, + roles: [{ role: 'read', db: mockAuthorizedDb }] + }); authorizedClient = this.configuration.newClient({ auth: { username: username, password: password } @@ -189,6 +189,7 @@ describe('listDatabases()', function () { }); } }); + UnifiedTestSuiteBuilder.describe('comment option') .createEntities(UnifiedTestSuiteBuilder.defaultEntities) .initialData({ diff --git a/test/integration/node-specific/auto_connect.test.ts b/test/integration/node-specific/auto_connect.test.ts index 73823beb7e..1459eb5db4 100644 --- a/test/integration/node-specific/auto_connect.test.ts +++ b/test/integration/node-specific/auto_connect.test.ts @@ -36,14 +36,6 @@ describe('When executing an operation for the first time', () => { }); describe(`class Admin`, () => { - describe(`#addUser()`, () => { - it('should connect the client', async () => { - const admin = client.db().admin(); - await admin.addUser('neal', 'iLoveJavaScript', { roles: ['root'] }).catch(() => null); - expect(client).to.have.property('topology').that.is.instanceOf(Topology); - }); - }); - describe(`#buildInfo()`, () => { it('should connect the client', async () => { const admin = client.db().admin(); @@ -531,14 +523,6 @@ describe('When executing an operation for the first time', () => { }); }); - describe(`#stats()`, () => { - it('should connect the client', async () => { - const c = client.db().collection('test'); - await c.stats(); - expect(client).to.have.property('topology').that.is.instanceOf(Topology); - }); - }); - describe(`#updateMany()`, () => { it('should connect the client', async () => { const c = client.db().collection('test'); @@ -557,14 +541,6 @@ describe('When executing an operation for the first time', () => { }); describe(`class Db`, () => { - describe(`#addUser()`, () => { - it('should connect the client', async () => { - const db = client.db(); - await db.addUser('neal', 'iLoveJavaScript', { roles: ['dbAdmin'] }).catch(() => null); - expect(client).to.have.property('topology').that.is.instanceOf(Topology); - }); - }); - describe(`#collections()`, () => { it('should connect the client', async () => { const db = client.db(); diff --git a/test/integration/node-specific/operation_examples.test.ts b/test/integration/node-specific/operation_examples.test.ts index 3e2f7c1f0b..8c06adaca7 100644 --- a/test/integration/node-specific/operation_examples.test.ts +++ b/test/integration/node-specific/operation_examples.test.ts @@ -1920,50 +1920,6 @@ describe('Operations', function () { } }); - /** - * Example of retrieving a collections stats using a Promise. - * - * example-class Collection - * example-method stats - */ - it('shouldCorrectlyReturnACollectionsStatsWithPromises', { - metadata: { requires: { topology: ['single'] } }, - - test: function () { - const configuration = this.configuration; - const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - - return client.connect().then(function (client) { - const db = client.db(configuration.db); - // LINE var MongoClient = require('mongodb').MongoClient, - // LINE test = require('assert'); - // LINE const client = new MongoClient('mongodb://localhost:27017/test'); - // LINE client.connect().then(() => { - // LINE var db = client.db('test); - // REPLACE configuration.writeConcernMax() WITH {w:1} - // REMOVE-LINE done(); - // BEGIN - - // Crete the collection for the distinct example - const collection = db.collection('collection_stats_test_with_promise'); - - // Insert some documents - return collection - .insertMany([{ a: 1 }, { hello: 'world' }], configuration.writeConcernMax()) - .then(function (result) { - expect(result).to.exist; - // Retrieve the statistics for the collection - return collection.stats(); - }) - .then(function (stats) { - expect(stats.count).to.equal(2); - return client.close(); - }); - }); - // END - } - }); - /** * An examples showing the creation and dropping of an index using Promises. * @@ -2209,118 +2165,6 @@ describe('Operations', function () { } }); - /** - * An example of adding a user to the database using a Promise. - * - * example-class Db - * example-method addUser - */ - it('shouldCorrectlyAddUserToDbWithPromises', { - metadata: { requires: { topology: 'single' } }, - - test: function () { - const configuration = this.configuration; - const client = configuration.newClient(configuration.writeConcernMax(), { - maxPoolSize: 1 - }); - - return client.connect().then(function (client) { - const db = client.db(configuration.db); - // LINE var MongoClient = require('mongodb').MongoClient, - // LINE test = require('assert'); - // LINE const client = new MongoClient('mongodb://localhost:27017/test'); - // LINE client.connect().then(() => { - // LINE var db = client.db('test); - // REPLACE configuration.writeConcernMax() WITH {w:1} - // REMOVE-LINE done(); - // BEGIN - - // Add a user to the database - return db - .addUser('user', 'name') - .then(function (result) { - expect(result).to.exist; - // Remove the user from the db - return db.removeUser('user'); - }) - .then(function (result) { - expect(result).to.exist; - return client.close(); - }); - }); - // END - } - }); - - /** - * An example of removing a user using a Promise. - * - * example-class Db - * example-method removeUser - */ - it.skip('shouldCorrectlyAddAndRemoveUserWithPromises', { - metadata: { requires: { topology: 'single', mongodb: '<=3.4.x' } }, - - test: function () { - const configuration = this.configuration; - - const client = configuration.newClient(); - return client.connect().then(function (client) { - const db = client.db(configuration.db); - // LINE var MongoClient = require('mongodb').MongoClient, - // LINE test = require('assert'); - // LINE const client = new MongoClient('mongodb://localhost:27017/test'); - // LINE client.connect().then(() => { - // LINE var db = client.db('test); - // REPLACE configuration.writeConcernMax() WITH {w:1} - // REMOVE-LINE done(); - // BEGIN - // Add a user to the database - - return db - .addUser('user3', 'name') - .then(function (result) { - expect(result).to.exist; - return client.close(); - }) - .then(() => { - const secondClient = configuration.newClient( - 'mongodb://user3:name@localhost:27017/integration_tests' - ); - - return secondClient.connect(); - }) - .then(function (client) { - // Logout the db - return client.logout().then(function () { - return client; - }); - }) - .then(function (client) { - // Remove the user - const db = client.db(configuration.db); - return db.removeUser('user3'); - }) - .then(function (result) { - expect(result).to.equal(true); - - // Should error out due to user no longer existing - const thirdClient = configuration.newClient( - 'mongodb://user3:name@localhost:27017/integration_tests', - { serverSelectionTimeoutMS: 10 } - ); - - return thirdClient.connect(); - }) - .catch(function (err) { - expect(err).to.exist; - return client.close(); - }); - }); - // END - } - }); - /** * A simple example showing the creation of a collection using a Promise. * @@ -3144,96 +2988,6 @@ describe('Operations', function () { } }); - /** - * An example of how to add a user to the admin database using a Promise. - * - * example-class Admin - * example-method addUser - */ - it('shouldCorrectlyAddAUserToAdminDbWithPromises', { - metadata: { requires: { topology: 'single' } }, - - test: function () { - const configuration = this.configuration; - const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - - return client.connect().then(function (client) { - const db = client.db(configuration.db); - // LINE var MongoClient = require('mongodb').MongoClient, - // LINE test = require('assert'); - // LINE const client = new MongoClient('mongodb://localhost:27017/test'); - // LINE client.connect().then(() => { - // LINE var db = client.db('test); - // REPLACE configuration.writeConcernMax() WITH {w:1} - // REMOVE-LINE restartAndDone - // REMOVE-LINE done(); - // BEGIN - - // Use the admin database for the operation - const adminDb = db.admin(); - - // Add the new user to the admin database - return adminDb - .addUser('admin11', 'admin11') - .then(function (result) { - expect(result).to.exist; - - return adminDb.removeUser('admin11'); - }) - .then(function (result) { - expect(result).to.exist; - return client.close(); - }); - }); - } - }); - - /** - * An example of how to remove a user from the admin database using a Promise. - * - * example-class Admin - * example-method removeUser - */ - it('shouldCorrectlyAddAUserAndRemoveItFromAdminDbWithPromises', { - metadata: { requires: { topology: 'single' } }, - - test: function () { - const configuration = this.configuration; - const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - - return client.connect().then(function (client) { - const db = client.db(configuration.db); - // LINE var MongoClient = require('mongodb').MongoClient, - // LINE test = require('assert'); - // LINE const client = new MongoClient('mongodb://localhost:27017/test'); - // LINE client.connect().then(() => { - // LINE var db = client.db('test); - // REPLACE configuration.writeConcernMax() WITH {w:1} - // REMOVE-LINE restartAndDone - // REMOVE-LINE done(); - // BEGIN - - // Use the admin database for the operation - const adminDb = db.admin(); - - // Add the new user to the admin database - return adminDb - .addUser('admin12', 'admin12') - .then(function (result) { - expect(result).to.exist; - - // Remove the user - return adminDb.removeUser('admin12'); - }) - .then(function (result) { - expect(result).to.equal(true); - return client.close(); - }); - }); - // END - } - }); - /** * An example of listing all available databases. using a Promise. * @@ -3307,21 +3061,11 @@ describe('Operations', function () { // Collections are not created until the first document is inserted return collection .insertOne({ a: 1 }, { writeConcern: { w: 1 } }) - .then(function (doc) { - expect(doc).to.exist; - // Add the new user to the admin database - return adminDb.addUser('admin13', 'admin13'); - }) .then(function (result) { expect(result).to.exist; // Retrieve the server Info return adminDb.serverStatus(); }) - .then(function (info) { - expect(info != null).to.exist; - - return adminDb.removeUser('admin13'); - }) .then(function (result) { expect(result).to.exist; return client.close(); diff --git a/test/integration/server-selection/readpreference.test.js b/test/integration/server-selection/readpreference.test.js index 41e8f05284..ba1e8602a8 100644 --- a/test/integration/server-selection/readpreference.test.js +++ b/test/integration/server-selection/readpreference.test.js @@ -110,41 +110,6 @@ describe('ReadPreference', function () { } }); - it('Should correctly apply collection level read Preference to stats', { - metadata: { requires: { mongodb: '>=2.6.0', topology: ['single', 'ssl'] } }, - - test: function (done) { - var configuration = this.configuration; - var client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - client.connect(function (err, client) { - var db = client.db(configuration.db); - expect(err).to.not.exist; - // Set read preference - var collection = db.collection('read_pref_1', { - readPreference: ReadPreference.SECONDARY_PREFERRED - }); - // Save checkout function - var command = client.topology.command; - // Set up our checker method - client.topology.command = function () { - var args = Array.prototype.slice.call(arguments, 0); - if (args[0] === 'integration_tests.$cmd') { - test.equal(ReadPreference.SECONDARY_PREFERRED, args[2].readPreference.mode); - } - - return command.apply(db.s.topology, args); - }; - - // Perform the map reduce - collection.stats(function (/* err */) { - // expect(err).to.not.exist; - client.topology.command = command; - client.close(done); - }); - }); - } - }); - it('Should correctly honor the readPreferences at DB and individual command level', { metadata: { requires: { mongodb: '>=2.6.0', topology: ['single', 'ssl'] } }, @@ -414,7 +379,14 @@ describe('ReadPreference', function () { }); const db = utilClient.db(configuration.db); - await db.addUser('default', 'pass', { roles: 'readWrite' }).catch(() => null); + await db + .command({ + createUser: 'default', + pwd: 'pass', + roles: [{ role: 'readWrite', db: configuration.db }] + }) + .catch(() => null); + await db.createCollection('before_collection').catch(() => null); await db.createIndex(collectionName, { aloha: 1 }).catch(() => null); @@ -430,7 +402,6 @@ describe('ReadPreference', function () { const methods = { 'Collection#createIndex': [{ quote: 'text' }], 'Db#createIndex': [collectionName, { quote: 'text' }], - 'Db#addUser': ['thomas', 'pass', { roles: 'readWrite' }], 'Db#removeUser': ['default'], 'Db#createCollection': ['created_collection'], 'Db#dropCollection': ['before_collection'], diff --git a/test/integration/uri-options/uri.test.js b/test/integration/uri-options/uri.test.js index ef62c3408b..a7c64b9c6d 100644 --- a/test/integration/uri-options/uri.test.js +++ b/test/integration/uri-options/uri.test.js @@ -5,6 +5,15 @@ const sinon = require('sinon'); const { Topology } = require('../../mongodb'); describe('URI', function () { + let client; + beforeEach(async function () { + client = this.configuration.newClient(); + }); + + afterEach(async function () { + await client.close(); + }); + it('should correctly allow for w:0 overriding on the connect url', { // Add a tag that our runner can trigger on // in this case we are setting that node needs to be higher than 0.10.X to run @@ -72,39 +81,34 @@ describe('URI', function () { } }); - it('should correctly connect using uri encoded username and password', { - // Add a tag that our runner can trigger on - // in this case we are setting that node needs to be higher than 0.10.X to run - metadata: { requires: { topology: 'single' } }, - - test: function (done) { - var self = this; - const configuration = this.configuration; - const client = configuration.newClient(); - - client.connect(function (err, client) { - expect(err).to.not.exist; - var user = 'u$ser', - pass = '$specialch@rs'; - var db = client.db(self.configuration.db); - - db.addUser(user, pass, function (err) { - expect(err).to.not.exist; - var uri = - 'mongodb://' + - encodeURIComponent(user) + - ':' + - encodeURIComponent(pass) + - '@localhost:27017/integration_tests'; - - configuration.newClient(uri).connect(function (err, c) { - expect(err).to.not.exist; - - c.close(() => client.close(done)); - }); - }); - }); - } + context('when connecting with a username and password that have URI escapable characters', () => { + let utilClient; + let client; + const username = 'u$ser'; + const password = '$specialch@rs'; + + beforeEach(async function () { + utilClient = this.configuration.newClient(); + await utilClient.db().admin().command({ createUser: username, pwd: password, roles: [] }); + }); + + afterEach(async () => { + await utilClient.db().admin().command({ dropUser: username }); + await client?.close(); + await utilClient?.close(); + }); + + it( + 'accepts a client that provides the correct username and password', + { requires: { topology: 'single' } }, + async function () { + const mongodbUri = `mongodb://${encodeURIComponent(username)}:${encodeURIComponent( + password + )}@${this.configuration.options.host}`; + client = this.configuration.newClient(mongodbUri); + await client.connect(); + } + ); }); it('should correctly translate uri options', { diff --git a/test/mongodb.ts b/test/mongodb.ts index 53ea38256c..4a8f21214b 100644 --- a/test/mongodb.ts +++ b/test/mongodb.ts @@ -152,7 +152,6 @@ export * from '../src/gridfs/upload'; export * from '../src/mongo_client'; export * from '../src/mongo_logger'; export * from '../src/mongo_types'; -export * from '../src/operations/add_user'; export * from '../src/operations/aggregate'; export * from '../src/operations/bulk_write'; export * from '../src/operations/collections'; diff --git a/test/types/community/stats.test-d.ts b/test/types/community/stats.test-d.ts deleted file mode 100644 index a2904b5cfd..0000000000 --- a/test/types/community/stats.test-d.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { expectType } from 'tsd'; - -import { type CollStats, MongoClient } from '../../mongodb'; - -const client = new MongoClient(''); -const db = client.db('test'); -const collection = db.collection('test.find'); - -expectType(await collection.stats()); - -const stats = await collection.stats(); -if (stats.wiredTiger) { - expectType(stats.wiredTiger.cache['bytes currently in the cache']); -} diff --git a/test/unit/assorted/write_concern.test.js b/test/unit/assorted/write_concern.test.js index 46a33bdc59..6e45ca69dc 100644 --- a/test/unit/assorted/write_concern.test.js +++ b/test/unit/assorted/write_concern.test.js @@ -157,11 +157,6 @@ describe('Command Write Concern', function () { db.collection('test').dropIndexes(writeConcernTestOptions) )); - it('successfully pass through writeConcern to createUser command', () => - writeConcernTest('createUser', (db, writeConcernTestOptions) => - db.admin().addUser('kay:kay', 'abc123', writeConcernTestOptions) - )); - it('successfully pass through writeConcern to dropUser command', () => writeConcernTest('dropUser', (db, writeConcernTestOptions) => db.admin().removeUser('kay:kay', writeConcernTestOptions) diff --git a/test/unit/connection_string.test.ts b/test/unit/connection_string.test.ts index b0ca1e8a42..9659fe04a2 100644 --- a/test/unit/connection_string.test.ts +++ b/test/unit/connection_string.test.ts @@ -725,4 +725,9 @@ describe('Connection String', function () { ]); }); }); + + it('rejects a connection string with an unsupported scheme', () => { + expect(() => new MongoClient('mango://localhost:23')).to.throw(/Invalid scheme/i); + expect(() => new MongoClient('mango+srv://localhost:23')).to.throw(/Invalid scheme/i); + }); });