1- import { beforeEach , afterEach , describe , expect , it } from "vitest" ;
1+ import { beforeEach , afterEach , describe , expect , it , vi , type MockInstance } from "vitest" ;
22import {
33 databaseCollectionInvalidArgs ,
44 databaseCollectionParameters ,
@@ -10,8 +10,9 @@ import {
1010 validateToolMetadata ,
1111 waitUntilSearchManagementServiceIsReady ,
1212 waitUntilSearchIndexIsListed ,
13+ getDataFromUntrustedContent ,
1314} from "../../../helpers.js" ;
14- import { describeWithMongoDB } from "../mongodbHelpers.js" ;
15+ import { describeWithMongoDB , setupMongoDBIntegrationTest } from "../mongodbHelpers.js" ;
1516import type { Collection } from "mongodb" ;
1617import { createMockElicitInput } from "../../../../utils/elicitationMocks.js" ;
1718import { Elicitation } from "../../../../../src/elicitation.js" ;
@@ -77,9 +78,10 @@ describeWithMongoDB(
7778 } ) ;
7879 expect ( response . isError ) . toBe ( true ) ;
7980 const content = getResponseContent ( response . content ) ;
80- expect ( content ) . toEqual (
81- 'Index with name "non-existent" does not exist in the provided namespace "any.foo".'
82- ) ;
81+ expect ( content ) . toContain ( "Index does not exist in the provided namespace." ) ;
82+
83+ const data = getDataFromUntrustedContent ( content ) ;
84+ expect ( JSON . parse ( data ) ) . toMatchObject ( { indexName : "non-existent" , namespace : "any.foo" } ) ;
8385 } ) ;
8486 } ) ;
8587
@@ -91,7 +93,7 @@ describeWithMongoDB(
9193 await moviesCollection . insertMany ( [
9294 {
9395 name : "Movie1" ,
94- plot : "This is a horrible movie about a database called BongoDB and how it tried to copy the original MangoDB." ,
96+ plot : "This is a horrible movie about a database called BongoDB and how it tried to copy the OG MangoDB." ,
9597 } ,
9698 ] ) ;
9799 await waitUntilSearchManagementServiceIsReady ( moviesCollection , signal ) ;
@@ -103,13 +105,7 @@ describeWithMongoDB(
103105 } ) ;
104106
105107 afterEach ( async ( ) => {
106- try {
107- await moviesCollection . dropSearchIndex ( "searchIdx" ) ;
108- } catch ( error ) {
109- if ( error instanceof Error && ! error . message . includes ( "not found in" ) ) {
110- throw error ;
111- }
112- }
108+ // dropping collection also drops the associated search indexes
113109 await moviesCollection . drop ( ) ;
114110 } ) ;
115111
@@ -119,9 +115,10 @@ describeWithMongoDB(
119115 arguments : { database : "mflix" , collection : "movies" , indexName : "searchIdx" } ,
120116 } ) ;
121117 const content = getResponseContent ( response . content ) ;
122- expect ( content ) . toEqual (
123- 'Successfully dropped the index with name "searchIdx" from the provided namespace "mflix.movies".'
124- ) ;
118+ expect ( content ) . toContain ( "Successfully dropped the index from the provided namespace." ) ;
119+
120+ const data = getDataFromUntrustedContent ( content ) ;
121+ expect ( JSON . parse ( data ) ) . toMatchObject ( { indexName : "searchIdx" , namespace : "mflix.movies" } ) ;
125122 } ) ;
126123 } ) ;
127124 } ,
@@ -132,23 +129,82 @@ describeWithMongoDB(
132129
133130describe ( "drop-search-index tool - when invoked via an elicitation enabled client" , ( ) => {
134131 const mockElicitInput = createMockElicitInput ( ) ;
132+ const mdbIntegration = setupMongoDBIntegrationTest ( { search : true } ) ;
135133 const integration = setupIntegrationTest (
136134 ( ) => defaultTestConfig ,
137135 ( ) => defaultDriverOptions ,
138136 { elicitInput : mockElicitInput }
139137 ) ;
140138
139+ let moviesCollection : Collection ;
140+ let dropSearchIndexSpy : MockInstance ;
141+
142+ beforeEach ( async ( { signal } ) => {
143+ const mongoClient = mdbIntegration . mongoClient ( ) ;
144+ moviesCollection = mongoClient . db ( "mflix" ) . collection ( "movies" ) ;
145+ await moviesCollection . insertMany ( [
146+ {
147+ name : "Movie1" ,
148+ plot : "This is a horrible movie about a database called BongoDB and how it tried to copy the OG MangoDB." ,
149+ } ,
150+ ] ) ;
151+ await waitUntilSearchManagementServiceIsReady ( moviesCollection , signal ) ;
152+ await moviesCollection . createSearchIndex ( {
153+ name : "searchIdx" ,
154+ definition : { mappings : { dynamic : true } } ,
155+ } ) ;
156+ await waitUntilSearchIndexIsListed ( moviesCollection , "searchIdx" , signal ) ;
157+
158+ await integration . mcpClient ( ) . callTool ( {
159+ name : "connect" ,
160+ arguments : {
161+ connectionString : mdbIntegration . connectionString ( ) ,
162+ } ,
163+ } ) ;
164+
165+ // Note: Unlike drop-index tool test, we don't test the final state of
166+ // indexes because of possible longer wait periods for changes to
167+ // reflect, at-times taking >30 seconds.
168+ dropSearchIndexSpy = vi . spyOn ( integration . mcpServer ( ) . session . serviceProvider , "dropSearchIndex" ) ;
169+ } ) ;
170+
171+ afterEach ( async ( ) => {
172+ // dropping collection also drops the associated search indexes
173+ await moviesCollection . drop ( ) ;
174+ } ) ;
175+
141176 it ( "should ask for confirmation before proceeding with tool call" , async ( ) => {
142177 mockElicitInput . confirmYes ( ) ;
143178 await integration . mcpClient ( ) . callTool ( {
144179 name : "drop-search-index" ,
145- arguments : { database : "any" , collection : "foo" , indexName : "default" } ,
180+ arguments : { database : "mflix" , collection : "movies" , indexName : "searchIdx" } ,
181+ } ) ;
182+ expect ( mockElicitInput . mock ) . toHaveBeenCalledTimes ( 1 ) ;
183+ expect ( mockElicitInput . mock ) . toHaveBeenCalledWith ( {
184+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
185+ message : expect . stringContaining (
186+ "You are about to drop the `searchIdx` index from the `mflix.movies` namespace"
187+ ) ,
188+ requestedSchema : Elicitation . CONFIRMATION_SCHEMA ,
189+ } ) ;
190+
191+ expect ( dropSearchIndexSpy ) . toHaveBeenCalledExactlyOnceWith ( "mflix" , "movies" , "searchIdx" ) ;
192+ } ) ;
193+
194+ it ( "should not drop the index if the confirmation was not provided" , async ( ) => {
195+ mockElicitInput . confirmNo ( ) ;
196+ await integration . mcpClient ( ) . callTool ( {
197+ name : "drop-search-index" ,
198+ arguments : { database : "mflix" , collection : "movies" , indexName : "searchIdx" } ,
146199 } ) ;
147200 expect ( mockElicitInput . mock ) . toHaveBeenCalledTimes ( 1 ) ;
148201 expect ( mockElicitInput . mock ) . toHaveBeenCalledWith ( {
149202 // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
150- message : expect . stringContaining ( "You are about to drop the `default` index from the `any.foo` namespace" ) ,
203+ message : expect . stringContaining (
204+ "You are about to drop the `searchIdx` index from the `mflix.movies` namespace"
205+ ) ,
151206 requestedSchema : Elicitation . CONFIRMATION_SCHEMA ,
152207 } ) ;
208+ expect ( dropSearchIndexSpy ) . not . toHaveBeenCalled ( ) ;
153209 } ) ;
154210} ) ;
0 commit comments