From 6b7cf21038951c317d99cc6f479f9a9dd86889f4 Mon Sep 17 00:00:00 2001 From: Lucas Ferreira Date: Fri, 8 Mar 2024 22:34:10 +0800 Subject: [PATCH 1/8] Fixing Mongoose instrumentation on Model methods with callback + tests --- .../instrumentation-mongoose/src/mongoose.ts | 3 +- .../instrumentation-mongoose/src/utils.ts | 32 +++-- .../test/mongoose.test.ts | 125 +++++++++++++++++- .../test/mongodb-v3.test.ts | 2 +- 4 files changed, 148 insertions(+), 14 deletions(-) diff --git a/plugins/node/instrumentation-mongoose/src/mongoose.ts b/plugins/node/instrumentation-mongoose/src/mongoose.ts index bfd73574b4..0fcabfb249 100644 --- a/plugins/node/instrumentation-mongoose/src/mongoose.ts +++ b/plugins/node/instrumentation-mongoose/src/mongoose.ts @@ -343,7 +343,8 @@ export class MongooseInstrumentation extends InstrumentationBase { originalThis, span, self._config.responseHook, - moduleVersion + moduleVersion, + args ) ); } else { diff --git a/plugins/node/instrumentation-mongoose/src/utils.ts b/plugins/node/instrumentation-mongoose/src/utils.ts index 2c44c87dae..882d699b4d 100644 --- a/plugins/node/instrumentation-mongoose/src/utils.ts +++ b/plugins/node/instrumentation-mongoose/src/utils.ts @@ -93,15 +93,27 @@ export function handleCallbackResponse( originalThis: any, span: Span, responseHook?: MongooseResponseCustomAttributesFunction, - moduleVersion: string | undefined = undefined + moduleVersion: string | undefined = undefined, + ...args: IArguments[] ) { - return exec.apply(originalThis, [ - (err: Error, response: any) => { - err - ? setErrorStatus(span, err) - : applyResponseHook(span, response, responseHook, moduleVersion); - span.end(); - return callback!(err, response); - }, - ]); + const newArgs = []; + for (const [, argValue] of Object.entries(args[0])) { + newArgs.push(argValue); + } + + let index = 0; + if (newArgs.length === 2) { + index = 1; + } + + newArgs[index] = (err: Error, response: any): any => { + err + ? setErrorStatus(span, err) + : applyResponseHook(span, response, responseHook, moduleVersion); + + span.end(); + return callback!(err, response); + }; + + return exec.apply(originalThis, newArgs); } diff --git a/plugins/node/instrumentation-mongoose/test/mongoose.test.ts b/plugins/node/instrumentation-mongoose/test/mongoose.test.ts index 37ce6c70ec..d55fb71ba8 100644 --- a/plugins/node/instrumentation-mongoose/test/mongoose.test.ts +++ b/plugins/node/instrumentation-mongoose/test/mongoose.test.ts @@ -63,8 +63,17 @@ describe('mongoose instrumentation', () => { beforeEach(async () => { instrumentation.disable(); instrumentation.setConfig({ - dbStatementSerializer: (_operation: string, payload) => - JSON.stringify(payload), + dbStatementSerializer: (_operation: string, payload) => { + try { + return JSON.stringify(payload); + } catch (e) { + if (e instanceof TypeError) { + return '{}'; + } + + throw e; + } + }, }); instrumentation.enable(); await loadUsers(); @@ -94,6 +103,118 @@ describe('mongoose instrumentation', () => { expect(statement.document).toEqual(expect.objectContaining(document)); }); + describe('when save call does not have callback', async () => { + it('instrumenting save operation with promise and committed session', async () => { + const session = await User.startSession(); + await session.startTransaction(); + const document = { + firstName: 'Test first name', + lastName: 'Test last name', + email: 'test@example.com', + }; + const user: IUser = new User(document); + + await user.save({ session }); + await session.commitTransaction(); + session.endSession(); + + const spans = getTestSpans(); + expect(spans.length).toBe(1); + assertSpan(spans[0] as ReadableSpan); + expect(spans[0].attributes[SemanticAttributes.DB_OPERATION]).toBe('save'); + const statement = getStatement(spans[0] as ReadableSpan); + expect(statement.document).toEqual(expect.objectContaining(document)); + + const createdUser = await User.findById(user._id).lean(); + expect(createdUser?._id.toString()).toEqual(user._id.toString()); + }); + + it('instrumenting save operation with promise and session with aborted transaction ', async () => { + const session = await User.startSession(); + await session.startTransaction(); + const document = { + firstName: 'Test first name', + lastName: 'Test last name', + email: 'test@example.com', + }; + const user: IUser = new User(document); + + await user.save({ session }); + await session.abortTransaction(); + session.endSession(); + + const spans = getTestSpans(); + expect(spans.length).toBe(1); + assertSpan(spans[0] as ReadableSpan); + expect(spans[0].attributes[SemanticAttributes.DB_OPERATION]).toBe('save'); + const statement = getStatement(spans[0] as ReadableSpan); + expect(statement.document).toEqual(expect.objectContaining(document)); + + const createdUser = await User.findById(user._id).lean(); + expect(createdUser).toEqual(null); + }); + }); + + describe('when save call has callback', async () => { + it('instrumenting save operation with promise and committed session', async done => { + const session = await User.startSession(); + await session.startTransaction(); + const document = { + firstName: 'Test first name', + lastName: 'Test last name', + email: 'test@example.com', + }; + const user: IUser = new User(document); + + user.save({ session }, async () => { + await session.commitTransaction(); + session.endSession(); + + const spans = getTestSpans(); + expect(spans.length).toBe(1); + assertSpan(spans[0] as ReadableSpan); + expect(spans[0].attributes[SemanticAttributes.DB_OPERATION]).toBe( + 'save' + ); + const statement = getStatement(spans[0] as ReadableSpan); + expect(statement.document).toEqual(expect.objectContaining(document)); + + const createdUser = await User.findById(user._id).lean(); + expect(createdUser?._id.toString()).toEqual(user._id.toString()); + done(); + }); + }); + + it('instrumenting save operation with promise and session with aborted transaction ', async done => { + const session = await User.startSession(); + await session.startTransaction(); + const document = { + firstName: 'Test first name', + lastName: 'Test last name', + email: 'test@example.com', + }; + const user: IUser = new User(document); + + user.save({ session }, async () => { + await session.abortTransaction(); + session.endSession(); + + const spans = getTestSpans(); + expect(spans.length).toBe(1); + assertSpan(spans[0] as ReadableSpan); + expect(spans[0].attributes[SemanticAttributes.DB_OPERATION]).toBe( + 'save' + ); + const statement = getStatement(spans[0] as ReadableSpan); + expect(statement.document).toEqual(expect.objectContaining(document)); + + const createdUser = await User.findById(user._id).lean(); + expect(createdUser).toEqual(null); + done(); + }); + }); + }); + it('instrumenting save operation with callback', done => { const document = { firstName: 'Test first name', diff --git a/plugins/node/opentelemetry-instrumentation-mongodb/test/mongodb-v3.test.ts b/plugins/node/opentelemetry-instrumentation-mongodb/test/mongodb-v3.test.ts index 938c00875a..98b0831ab8 100644 --- a/plugins/node/opentelemetry-instrumentation-mongodb/test/mongodb-v3.test.ts +++ b/plugins/node/opentelemetry-instrumentation-mongodb/test/mongodb-v3.test.ts @@ -401,7 +401,7 @@ describe('MongoDBInstrumentation-Tracing-v3', () => { }); }); - it('should attach response hook data to the resulting span for insert function', done => { + it.skip('should attach response hook data to the resulting span for insert function', done => { const insertData = [{ a: 1 }, { a: 2 }, { a: 3 }]; const span = trace.getTracer('default').startSpan('insertRootSpan'); context.with(trace.setSpan(context.active(), span), () => { From 2283303670f96fd85dc922d1cfae3c570acac165 Mon Sep 17 00:00:00 2001 From: Lucas Ferreira Date: Sun, 10 Mar 2024 14:27:37 +0800 Subject: [PATCH 2/8] Fixing and adding more tests --- .../test/mongoose.test.ts | 116 ++++++++++-------- 1 file changed, 67 insertions(+), 49 deletions(-) diff --git a/plugins/node/instrumentation-mongoose/test/mongoose.test.ts b/plugins/node/instrumentation-mongoose/test/mongoose.test.ts index d55fb71ba8..be4341ec10 100644 --- a/plugins/node/instrumentation-mongoose/test/mongoose.test.ts +++ b/plugins/node/instrumentation-mongoose/test/mongoose.test.ts @@ -64,15 +64,9 @@ describe('mongoose instrumentation', () => { instrumentation.disable(); instrumentation.setConfig({ dbStatementSerializer: (_operation: string, payload) => { - try { - return JSON.stringify(payload); - } catch (e) { - if (e instanceof TypeError) { - return '{}'; - } - - throw e; - } + return JSON.stringify(payload, (key, value) => { + return key === 'session' ? '[Session]' : value; + }); }, }); instrumentation.enable(); @@ -156,9 +150,65 @@ describe('mongoose instrumentation', () => { }); describe('when save call has callback', async () => { - it('instrumenting save operation with promise and committed session', async done => { - const session = await User.startSession(); - await session.startTransaction(); + it('instrumenting save operation with promise, session and committed transaction', done => { + User.startSession().then(async session => { + await session.startTransaction(); + const document = { + firstName: 'Test first name', + lastName: 'Test last name', + email: 'test@example.com', + }; + const user: IUser = new User(document); + await user.save({ session }, async () => { + const spans = getTestSpans(); + expect(spans.length).toBe(1); + assertSpan(spans[0] as ReadableSpan); + expect(spans[0].attributes[SemanticAttributes.DB_OPERATION]).toBe( + 'save' + ); + const statement = getStatement(spans[0] as ReadableSpan); + expect(statement.document).toEqual(expect.objectContaining(document)); + + const createdUser = await User.findById(user._id).lean(); + expect(createdUser?._id.toString()).toEqual(user._id.toString()); + done(); + }); + await session.commitTransaction(); + session.endSession(); + }); + }); + + it('instrumenting save operation with promise, session and aborted transaction', done => { + User.startSession().then(async session => { + await session.startTransaction(); + const document = { + firstName: 'Test first name', + lastName: 'Test last name', + email: 'test@example.com', + }; + const user: IUser = new User(document); + + await user.save({ session }, async () => { + await session.abortTransaction(); + session.endSession(); + + const spans = getTestSpans(); + expect(spans.length).toBe(1); + assertSpan(spans[0] as ReadableSpan); + expect(spans[0].attributes[SemanticAttributes.DB_OPERATION]).toBe( + 'save' + ); + const statement = getStatement(spans[0] as ReadableSpan); + expect(statement.document).toEqual(expect.objectContaining(document)); + + const createdUser = await User.findById(user._id).lean(); + expect(createdUser).toEqual(null); + done(); + }); + }); + }); + + it('instrumenting save operation with generic options and callback', done => { const document = { firstName: 'Test first name', lastName: 'Test last name', @@ -166,11 +216,9 @@ describe('mongoose instrumentation', () => { }; const user: IUser = new User(document); - user.save({ session }, async () => { - await session.commitTransaction(); - session.endSession(); - + user.save({}, () => { const spans = getTestSpans(); + expect(spans.length).toBe(1); assertSpan(spans[0] as ReadableSpan); expect(spans[0].attributes[SemanticAttributes.DB_OPERATION]).toBe( @@ -178,16 +226,11 @@ describe('mongoose instrumentation', () => { ); const statement = getStatement(spans[0] as ReadableSpan); expect(statement.document).toEqual(expect.objectContaining(document)); - - const createdUser = await User.findById(user._id).lean(); - expect(createdUser?._id.toString()).toEqual(user._id.toString()); done(); }); }); - it('instrumenting save operation with promise and session with aborted transaction ', async done => { - const session = await User.startSession(); - await session.startTransaction(); + it('instrumenting save operation with only callback', done => { const document = { firstName: 'Test first name', lastName: 'Test last name', @@ -195,11 +238,9 @@ describe('mongoose instrumentation', () => { }; const user: IUser = new User(document); - user.save({ session }, async () => { - await session.abortTransaction(); - session.endSession(); - + user.save(() => { const spans = getTestSpans(); + expect(spans.length).toBe(1); assertSpan(spans[0] as ReadableSpan); expect(spans[0].attributes[SemanticAttributes.DB_OPERATION]).toBe( @@ -207,34 +248,11 @@ describe('mongoose instrumentation', () => { ); const statement = getStatement(spans[0] as ReadableSpan); expect(statement.document).toEqual(expect.objectContaining(document)); - - const createdUser = await User.findById(user._id).lean(); - expect(createdUser).toEqual(null); done(); }); }); }); - it('instrumenting save operation with callback', done => { - const document = { - firstName: 'Test first name', - lastName: 'Test last name', - email: 'test@example.com', - }; - const user: IUser = new User(document); - - user.save(() => { - const spans = getTestSpans(); - - expect(spans.length).toBe(1); - assertSpan(spans[0] as ReadableSpan); - expect(spans[0].attributes[SemanticAttributes.DB_OPERATION]).toBe('save'); - const statement = getStatement(spans[0] as ReadableSpan); - expect(statement.document).toEqual(expect.objectContaining(document)); - done(); - }); - }); - it('instrumenting find operation', async () => { await User.find({ id: '_test' }); From fac12517fc1eba7c2542db42cc920d3e84682af8 Mon Sep 17 00:00:00 2001 From: Lucas Ferreira Date: Sun, 10 Mar 2024 14:33:28 +0800 Subject: [PATCH 3/8] Removing skip on existing failing test --- .../test/mongodb-v3.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/node/opentelemetry-instrumentation-mongodb/test/mongodb-v3.test.ts b/plugins/node/opentelemetry-instrumentation-mongodb/test/mongodb-v3.test.ts index 98b0831ab8..938c00875a 100644 --- a/plugins/node/opentelemetry-instrumentation-mongodb/test/mongodb-v3.test.ts +++ b/plugins/node/opentelemetry-instrumentation-mongodb/test/mongodb-v3.test.ts @@ -401,7 +401,7 @@ describe('MongoDBInstrumentation-Tracing-v3', () => { }); }); - it.skip('should attach response hook data to the resulting span for insert function', done => { + it('should attach response hook data to the resulting span for insert function', done => { const insertData = [{ a: 1 }, { a: 2 }, { a: 3 }]; const span = trace.getTracer('default').startSpan('insertRootSpan'); context.with(trace.setSpan(context.active(), span), () => { From f205174af758967402f3973091bd67872ec2b9d5 Mon Sep 17 00:00:00 2001 From: Lucas Ferreira Date: Sun, 10 Mar 2024 14:35:58 +0800 Subject: [PATCH 4/8] Renaming callback index variable --- plugins/node/instrumentation-mongoose/src/utils.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/node/instrumentation-mongoose/src/utils.ts b/plugins/node/instrumentation-mongoose/src/utils.ts index 882d699b4d..19147722e2 100644 --- a/plugins/node/instrumentation-mongoose/src/utils.ts +++ b/plugins/node/instrumentation-mongoose/src/utils.ts @@ -101,12 +101,12 @@ export function handleCallbackResponse( newArgs.push(argValue); } - let index = 0; + let callbackArgumentIndex = 0; if (newArgs.length === 2) { - index = 1; + callbackArgumentIndex = 1; } - newArgs[index] = (err: Error, response: any): any => { + newArgs[callbackArgumentIndex] = (err: Error, response: any): any => { err ? setErrorStatus(span, err) : applyResponseHook(span, response, responseHook, moduleVersion); From 784e99e54da2e7722708d3238783c6a478452500 Mon Sep 17 00:00:00 2001 From: Lucas Ferreira Date: Wed, 22 May 2024 18:09:31 +0800 Subject: [PATCH 5/8] use exported strings for attributes --- .../instrumentation-mongoose/test/mongoose.test.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/node/instrumentation-mongoose/test/mongoose.test.ts b/plugins/node/instrumentation-mongoose/test/mongoose.test.ts index 2c149803b9..1c84af85bf 100644 --- a/plugins/node/instrumentation-mongoose/test/mongoose.test.ts +++ b/plugins/node/instrumentation-mongoose/test/mongoose.test.ts @@ -143,7 +143,7 @@ describe('mongoose instrumentation', () => { const spans = getTestSpans(); expect(spans.length).toBe(1); assertSpan(spans[0] as ReadableSpan); - expect(spans[0].attributes[SemanticAttributes.DB_OPERATION]).toBe('save'); + expect(spans[0].attributes[SEMATTRS_DB_OPERATION]).toBe('save'); const statement = getStatement(spans[0] as ReadableSpan); expect(statement.document).toEqual(expect.objectContaining(document)); @@ -166,7 +166,7 @@ describe('mongoose instrumentation', () => { const spans = getTestSpans(); expect(spans.length).toBe(1); assertSpan(spans[0] as ReadableSpan); - expect(spans[0].attributes[SemanticAttributes.DB_OPERATION]).toBe( + expect(spans[0].attributes[SEMATTRS_DB_OPERATION]).toBe( 'save' ); const statement = getStatement(spans[0] as ReadableSpan); @@ -198,7 +198,7 @@ describe('mongoose instrumentation', () => { const spans = getTestSpans(); expect(spans.length).toBe(1); assertSpan(spans[0] as ReadableSpan); - expect(spans[0].attributes[SemanticAttributes.DB_OPERATION]).toBe( + expect(spans[0].attributes[SEMATTRS_DB_OPERATION]).toBe( 'save' ); const statement = getStatement(spans[0] as ReadableSpan); @@ -224,7 +224,7 @@ describe('mongoose instrumentation', () => { expect(spans.length).toBe(1); assertSpan(spans[0] as ReadableSpan); - expect(spans[0].attributes[SemanticAttributes.DB_OPERATION]).toBe( + expect(spans[0].attributes[SEMATTRS_DB_OPERATION]).toBe( 'save' ); const statement = getStatement(spans[0] as ReadableSpan); @@ -246,7 +246,7 @@ describe('mongoose instrumentation', () => { expect(spans.length).toBe(1); assertSpan(spans[0] as ReadableSpan); - expect(spans[0].attributes[SemanticAttributes.DB_OPERATION]).toBe( + expect(spans[0].attributes[SEMATTRS_DB_OPERATION]).toBe( 'save' ); const statement = getStatement(spans[0] as ReadableSpan); From 908b8af034db5031e660b71c21f9879e7caa1562 Mon Sep 17 00:00:00 2001 From: Lucas Ferreira Date: Wed, 22 May 2024 19:37:51 +0800 Subject: [PATCH 6/8] Defining args as IArguments and updating its values directly --- .../node/instrumentation-mongoose/src/mongoose.ts | 4 ++-- .../node/instrumentation-mongoose/src/utils.ts | 15 +++++---------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/plugins/node/instrumentation-mongoose/src/mongoose.ts b/plugins/node/instrumentation-mongoose/src/mongoose.ts index 5f998ffe5d..a14488c3e2 100644 --- a/plugins/node/instrumentation-mongoose/src/mongoose.ts +++ b/plugins/node/instrumentation-mongoose/src/mongoose.ts @@ -330,9 +330,9 @@ export class MongooseInstrumentation extends InstrumentationBase { exec, originalThis, span, + args, self._config.responseHook, - moduleVersion, - args + moduleVersion ) ); } else { diff --git a/plugins/node/instrumentation-mongoose/src/utils.ts b/plugins/node/instrumentation-mongoose/src/utils.ts index 774a309582..7f78bcc882 100644 --- a/plugins/node/instrumentation-mongoose/src/utils.ts +++ b/plugins/node/instrumentation-mongoose/src/utils.ts @@ -98,21 +98,16 @@ export function handleCallbackResponse( exec: Function, originalThis: any, span: Span, + args: IArguments, responseHook?: MongooseResponseCustomAttributesFunction, - moduleVersion: string | undefined = undefined, - ...args: IArguments[] + moduleVersion: string | undefined = undefined ) { - const newArgs = []; - for (const [, argValue] of Object.entries(args[0])) { - newArgs.push(argValue); - } - let callbackArgumentIndex = 0; - if (newArgs.length === 2) { + if (args.length === 2) { callbackArgumentIndex = 1; } - newArgs[callbackArgumentIndex] = (err: Error, response: any): any => { + args[callbackArgumentIndex] = (err: Error, response: any): any => { err ? setErrorStatus(span, err) : applyResponseHook(span, response, responseHook, moduleVersion); @@ -121,5 +116,5 @@ export function handleCallbackResponse( return callback!(err, response); }; - return exec.apply(originalThis, newArgs); + return exec.apply(originalThis, args); } From cbce832777ecb89b7b923a00708224d2212b5eca Mon Sep 17 00:00:00 2001 From: Lucas Ferreira Date: Thu, 23 May 2024 16:20:22 +0800 Subject: [PATCH 7/8] Fix lint issues --- .../test/mongoose.test.ts | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/plugins/node/instrumentation-mongoose/test/mongoose.test.ts b/plugins/node/instrumentation-mongoose/test/mongoose.test.ts index 1c84af85bf..57fb69e783 100644 --- a/plugins/node/instrumentation-mongoose/test/mongoose.test.ts +++ b/plugins/node/instrumentation-mongoose/test/mongoose.test.ts @@ -166,9 +166,7 @@ describe('mongoose instrumentation', () => { const spans = getTestSpans(); expect(spans.length).toBe(1); assertSpan(spans[0] as ReadableSpan); - expect(spans[0].attributes[SEMATTRS_DB_OPERATION]).toBe( - 'save' - ); + expect(spans[0].attributes[SEMATTRS_DB_OPERATION]).toBe('save'); const statement = getStatement(spans[0] as ReadableSpan); expect(statement.document).toEqual(expect.objectContaining(document)); @@ -198,9 +196,7 @@ describe('mongoose instrumentation', () => { const spans = getTestSpans(); expect(spans.length).toBe(1); assertSpan(spans[0] as ReadableSpan); - expect(spans[0].attributes[SEMATTRS_DB_OPERATION]).toBe( - 'save' - ); + expect(spans[0].attributes[SEMATTRS_DB_OPERATION]).toBe('save'); const statement = getStatement(spans[0] as ReadableSpan); expect(statement.document).toEqual(expect.objectContaining(document)); @@ -224,9 +220,7 @@ describe('mongoose instrumentation', () => { expect(spans.length).toBe(1); assertSpan(spans[0] as ReadableSpan); - expect(spans[0].attributes[SEMATTRS_DB_OPERATION]).toBe( - 'save' - ); + expect(spans[0].attributes[SEMATTRS_DB_OPERATION]).toBe('save'); const statement = getStatement(spans[0] as ReadableSpan); expect(statement.document).toEqual(expect.objectContaining(document)); done(); @@ -246,9 +240,7 @@ describe('mongoose instrumentation', () => { expect(spans.length).toBe(1); assertSpan(spans[0] as ReadableSpan); - expect(spans[0].attributes[SEMATTRS_DB_OPERATION]).toBe( - 'save' - ); + expect(spans[0].attributes[SEMATTRS_DB_OPERATION]).toBe('save'); const statement = getStatement(spans[0] as ReadableSpan); expect(statement.document).toEqual(expect.objectContaining(document)); done(); From b414324984d8856ef2e7fe63f3f84c5af02552ed Mon Sep 17 00:00:00 2001 From: Lucas Ferreira Date: Fri, 24 May 2024 12:27:01 +0800 Subject: [PATCH 8/8] Replacing tests with session option to use wtimeout to avoid the need of transactions in the instrumentation test --- .../test/mongoose.test.ts | 95 ++++--------------- 1 file changed, 17 insertions(+), 78 deletions(-) diff --git a/plugins/node/instrumentation-mongoose/test/mongoose.test.ts b/plugins/node/instrumentation-mongoose/test/mongoose.test.ts index 57fb69e783..3badef8d6d 100644 --- a/plugins/node/instrumentation-mongoose/test/mongoose.test.ts +++ b/plugins/node/instrumentation-mongoose/test/mongoose.test.ts @@ -101,19 +101,14 @@ describe('mongoose instrumentation', () => { }); describe('when save call does not have callback', async () => { - it('instrumenting save operation with promise and committed session', async () => { - const session = await User.startSession(); - await session.startTransaction(); + it('instrumenting save operation with option property set', async () => { const document = { firstName: 'Test first name', lastName: 'Test last name', email: 'test@example.com', }; const user: IUser = new User(document); - - await user.save({ session }); - await session.commitTransaction(); - session.endSession(); + await user.save({ wtimeout: 42 }); const spans = getTestSpans(); expect(spans.length).toBe(1); @@ -121,89 +116,33 @@ describe('mongoose instrumentation', () => { expect(spans[0].attributes[SEMATTRS_DB_OPERATION]).toBe('save'); const statement = getStatement(spans[0] as ReadableSpan); expect(statement.document).toEqual(expect.objectContaining(document)); + expect(statement.options.wtimeout).toEqual(42); const createdUser = await User.findById(user._id).lean(); expect(createdUser?._id.toString()).toEqual(user._id.toString()); }); + }); - it('instrumenting save operation with promise and session with aborted transaction ', async () => { - const session = await User.startSession(); - await session.startTransaction(); + describe('when save call has callback', async () => { + it('instrumenting save operation with promise and option property set', done => { const document = { firstName: 'Test first name', lastName: 'Test last name', email: 'test@example.com', }; const user: IUser = new User(document); + user.save({ wtimeout: 42 }, async () => { + const spans = getTestSpans(); + expect(spans.length).toBe(1); + assertSpan(spans[0] as ReadableSpan); + expect(spans[0].attributes[SEMATTRS_DB_OPERATION]).toBe('save'); + const statement = getStatement(spans[0] as ReadableSpan); + expect(statement.document).toEqual(expect.objectContaining(document)); + expect(statement.options.wtimeout).toEqual(42); - await user.save({ session }); - await session.abortTransaction(); - session.endSession(); - - const spans = getTestSpans(); - expect(spans.length).toBe(1); - assertSpan(spans[0] as ReadableSpan); - expect(spans[0].attributes[SEMATTRS_DB_OPERATION]).toBe('save'); - const statement = getStatement(spans[0] as ReadableSpan); - expect(statement.document).toEqual(expect.objectContaining(document)); - - const createdUser = await User.findById(user._id).lean(); - expect(createdUser).toEqual(null); - }); - }); - - describe('when save call has callback', async () => { - it('instrumenting save operation with promise, session and committed transaction', done => { - User.startSession().then(async session => { - await session.startTransaction(); - const document = { - firstName: 'Test first name', - lastName: 'Test last name', - email: 'test@example.com', - }; - const user: IUser = new User(document); - await user.save({ session }, async () => { - const spans = getTestSpans(); - expect(spans.length).toBe(1); - assertSpan(spans[0] as ReadableSpan); - expect(spans[0].attributes[SEMATTRS_DB_OPERATION]).toBe('save'); - const statement = getStatement(spans[0] as ReadableSpan); - expect(statement.document).toEqual(expect.objectContaining(document)); - - const createdUser = await User.findById(user._id).lean(); - expect(createdUser?._id.toString()).toEqual(user._id.toString()); - done(); - }); - await session.commitTransaction(); - session.endSession(); - }); - }); - - it('instrumenting save operation with promise, session and aborted transaction', done => { - User.startSession().then(async session => { - await session.startTransaction(); - const document = { - firstName: 'Test first name', - lastName: 'Test last name', - email: 'test@example.com', - }; - const user: IUser = new User(document); - - await user.save({ session }, async () => { - await session.abortTransaction(); - session.endSession(); - - const spans = getTestSpans(); - expect(spans.length).toBe(1); - assertSpan(spans[0] as ReadableSpan); - expect(spans[0].attributes[SEMATTRS_DB_OPERATION]).toBe('save'); - const statement = getStatement(spans[0] as ReadableSpan); - expect(statement.document).toEqual(expect.objectContaining(document)); - - const createdUser = await User.findById(user._id).lean(); - expect(createdUser).toEqual(null); - done(); - }); + const createdUser = await User.findById(user._id).lean(); + expect(createdUser?._id.toString()).toEqual(user._id.toString()); + done(); }); });