diff --git a/test/instrumentation/context/nested-async-trace.test.js b/test/instrumentation/context/nested-async-trace.test.js index 60f37d2a..b90ae9b4 100644 --- a/test/instrumentation/context/nested-async-trace.test.js +++ b/test/instrumentation/context/nested-async-trace.test.js @@ -249,155 +249,172 @@ test(`nested mysql async query with express`, async (t) => { }) }) -test.skip(`nested mysql2 async query with express`, async (t) => { - agent.bindHttpWithCallSite() - const source = path.resolve(fixtures, 'mysql.sql') - const container = await new MySqlContainer() - .withCommand(['--default-authentication-plugin=mysql_native_password']) - .withEnvironment({ - 'MYSQL_DATABASE': 'test', - 'TZ': 'Asia/Seoul', - }) - .withCopyFilesToContainer([{ - source: source, - target: '/docker-entrypoint-initdb.d/mysql.sql' - }]) - .start() - - const app = new express() - app.get('/test1', async (req, res) => { - const connection = mysql2.createConnection({ - host: container.getHost(), - port: container.getPort(), - database: 'test', - user: container.getUsername(), - password: container.getUserPassword(), - timezone: '+09:00' - }) +test(`nested mysql2 async query with express`, async (t) => { + const collectorServer = new grpc.Server() + collectorServer.addService(services.AgentService, { + pingSession: unaryService + }) + collectorServer.addService(services.StatService, { + sendAgentStat: unaryService + }) + collectorServer.addService(services.SpanService, { + sendSpan: spanMessageStreamService + }) + + collectorServer.bindAsync('localhost:0', grpc.ServerCredentials.createInsecure(), async (err, port) => { + agent.bindHttpWithCallSite() + const source = path.resolve(fixtures, 'mysql.sql') + const container = await new MySqlContainer() + .withCommand(['--default-authentication-plugin=mysql_native_password']) + .withEnvironment({ + 'MYSQL_DATABASE': 'test', + 'TZ': 'Asia/Seoul', + }) + .withCopyFilesToContainer([{ + source: source, + target: '/docker-entrypoint-initdb.d/mysql.sql' + }]) + .start() + + const app = new express() + app.get('/test1', async (req, res) => { + const connection = mysql2.createConnection({ + host: container.getHost(), + port: container.getPort(), + database: 'test', + user: container.getUsername(), + password: container.getUserPassword(), + timezone: '+09:00' + }) + + connection.query(`SELECT * FROM member`, async function (error, results) { + if (error) throw error + t.equal(results[0].id, 'a', 'SELECT member id') + t.equal(results[0].name, 'name1', 'SELECT member name') + t.equal(results[0].joined.getDate(), new Date('2023-01-18T00:00:00+09:00').getDate(), 'SELECT member joined') + + connection.query(`SELECT * FROM member WHERE id = ?`, results[0].id, async function (error, results) { + + }) + }) + + const [rows] = await connection.promise().query(`SELECT * FROM member WHERE id = ?`, 'a') + t.equal(rows[0].id, 'a', 'id in SELECT query hooking') + t.equal(rows[0].name, 'name1', 'name in SELECT query hooking') + t.equal(rows[0].joined.toISOString().slice(0, 10), '2023-01-17', 'joined in SELECT query hooking') + + setTimeout(() => { + res.send('ok get') + }, 1000) + + agent.callbackTraceClose(async (trace) => { + let actualBuilder = new MethodDescriptorBuilder(expected('get', 'app.get')) + .setClassName(expected('app', 'Function')) + .setLineNumber(280) + .setFileName('nested-async-trace.test.js') + let actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + let actualSpanEvent = trace.spanBuilder.spanEventList.find(spanEvent => spanEvent.sequence === 0) + t.equal(actualMethodDescriptor.apiId, actualSpanEvent.apiId, 'apiId is equal') + t.equal(actualMethodDescriptor.apiDescriptor, expected('app.get', 'Function.app.get'), 'apiDescriptor is equal') + t.equal(actualMethodDescriptor.className, expected('app', 'Function'), 'className is equal') + t.equal(actualMethodDescriptor.methodName, 'get', 'methodName is equal') + t.equal(actualSpanEvent.sequence, 0, 'sequence is 0') + t.equal(actualSpanEvent.depth, 1, 'depth is 0') + t.equal(actualSpanEvent.serviceType, expressServiceType.getCode(), 'serviceType is express') + + actualBuilder = new MethodDescriptorBuilder('createConnection') + .setLineNumber(281) + .setFileName('nested-async-trace.test.js') + actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + actualSpanEvent = trace.spanBuilder.spanEventList.find(spanEvent => spanEvent.sequence === 1) + t.equal(actualMethodDescriptor.apiId, actualSpanEvent.apiId, 'apiId is equal') + t.equal(actualSpanEvent.endPoint, 'localhost', 'endPoint is equal') + t.equal(actualSpanEvent.destinationId, 'test', 'destinationId is equal') + t.equal(actualSpanEvent.sequence, 1, 'sequence is 1') + t.equal(actualSpanEvent.depth, 2, 'depth is 2') + t.equal(actualSpanEvent.serviceType, mysqlServiceType.getCode(), 'serviceType is mysql') - connection.query(`SELECT * FROM member`, async function (error, results) { - if (error) throw error - t.equal(results[0].id, 'a', 'SELECT member id') - t.equal(results[0].name, 'name1', 'SELECT member name') - t.equal(results[0].joined.getDate(), new Date('2023-01-18T00:00:00+09:00').getDate(), 'SELECT member joined') + actualBuilder = new MethodDescriptorBuilder('query') + .setClassName('Connection') + .setLineNumber(290) + .setFileName('nested-async-trace.test.js') + actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + actualSpanEvent = trace.spanBuilder.spanEventList.find(spanEvent => spanEvent.sequence === 2) + t.equal(actualMethodDescriptor.apiId, actualSpanEvent.apiId, 'apiId is equal') + t.equal(actualSpanEvent.sequence, 2, 'sequence is 2') + t.equal(actualSpanEvent.depth, 2, 'depth is 2') + t.equal(actualSpanEvent.serviceType, mysqlExecuteQueryServiceType.getCode(), 'serviceType is mysql') + let actualNextAsyncId = actualSpanEvent.asyncId + + let actualSpanChunk = trace.repository.dataSender.findSpanChunk(actualNextAsyncId) + t.equal(actualSpanChunk.spanId, actualSpanEvent.spanId, 'spanId is equal') + t.equal(actualSpanChunk.getTraceRoot().getTransactionId(), trace.spanBuilder.getTraceRoot().getTransactionId(), 'transactionIdObject is equal') + t.equal(actualSpanChunk.localAsyncId.asyncId, actualSpanEvent.asyncId.asyncId, 'localAsyncId is equal') + t.equal(actualSpanChunk.localAsyncId.sequence, 1, 'localAsyncId.sequence is equal') + t.equal(actualSpanChunk.spanEventList[0].apiId, defaultPredefinedMethodDescriptorRegistry.asyncInvocationDescriptor.apiId, 'apiId is equal') + t.equal(actualSpanChunk.spanEventList[0].depth, 1, 'depth is equal') + t.equal(actualSpanChunk.spanEventList[0].sequence, 0, 'sequence is equal') + t.equal(actualSpanChunk.spanEventList[0].serviceType, ServiceType.async.getCode(), 'serviceType is mysql') - connection.query(`SELECT * FROM member WHERE id = ?`, results[0].id, async function (error, results) { + actualBuilder = new MethodDescriptorBuilder('query') + .setClassName('Connection') + .setLineNumber(296) + .setFileName('nested-async-trace.test.js') + actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + actualSpanEvent = actualSpanChunk.spanEventList[1] + t.equal(actualMethodDescriptor.apiId, actualSpanEvent.apiId, 'apiId is equal') + t.equal(actualSpanEvent.depth, 2, 'depth is 3') + t.equal(actualSpanEvent.sequence, 1, 'sequence is 2') + t.equal(actualSpanEvent.serviceType, mysqlExecuteQueryServiceType.getCode(), 'serviceType is mysql') + actualNextAsyncId = actualSpanEvent.asyncId + actualSpanChunk = trace.repository.dataSender.findSpanChunk(actualNextAsyncId) + t.equal(actualSpanChunk.spanId, actualSpanEvent.spanId, 'spanId is equal') + t.equal(actualSpanChunk.getTraceRoot().getTransactionId(), trace.spanBuilder.getTraceRoot().getTransactionId(), 'transactionIdObject is equal') + t.equal(actualSpanChunk.localAsyncId.asyncId, actualNextAsyncId.asyncId, 'localAsyncId is equal') + t.equal(actualSpanChunk.localAsyncId.sequence, 1, 'localAsyncId.sequence is equal') + t.equal(actualSpanChunk.spanEventList[0].apiId, defaultPredefinedMethodDescriptorRegistry.asyncInvocationDescriptor.apiId, 'apiId is equal') + t.equal(actualSpanChunk.spanEventList[0].depth, 1, 'depth is equal') + t.equal(actualSpanChunk.spanEventList[0].sequence, 0, 'sequence is equal') + t.equal(actualSpanChunk.spanEventList[0].serviceType, ServiceType.async.getCode(), 'serviceType is mysql') + + actualBuilder = new MethodDescriptorBuilder('query') + .setClassName('Connection') + .setLineNumber(103) + .setFileName('promise.js') + actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + actualSpanEvent = trace.spanBuilder.spanEventList.find(spanEvent => spanEvent.sequence === 3) + t.equal(actualMethodDescriptor.apiId, actualSpanEvent.apiId, 'apiId is equal') + t.equal(actualSpanEvent.sequence, 3, 'sequence is 3') + t.equal(actualSpanEvent.depth, 2, 'depth is 2') + t.equal(actualSpanEvent.serviceType, mysqlExecuteQueryServiceType.getCode(), 'serviceType is mysql') + actualNextAsyncId = actualSpanEvent.asyncId + + actualSpanChunk = trace.repository.dataSender.findSpanChunk(actualNextAsyncId) + t.equal(actualSpanChunk.spanId, actualSpanEvent.spanId, 'spanId is equal') + t.equal(actualSpanChunk.getTraceRoot().getTransactionId(), trace.spanBuilder.getTraceRoot().getTransactionId(), 'transactionIdObject is equal') + t.equal(actualSpanChunk.localAsyncId.asyncId, actualNextAsyncId.asyncId, 'localAsyncId is equal') + t.equal(actualSpanChunk.localAsyncId.sequence, 1, 'localAsyncId.sequence is equal') + t.equal(actualSpanChunk.spanEventList[0].apiId, defaultPredefinedMethodDescriptorRegistry.asyncInvocationDescriptor.apiId, 'apiId is equal') + t.equal(actualSpanChunk.spanEventList[0].depth, 1, 'depth is equal') + t.equal(actualSpanChunk.spanEventList[0].sequence, 0, 'sequence is equal') + t.equal(actualSpanChunk.spanEventList[0].serviceType, ServiceType.async.getCode(), 'serviceType is mysql') + + connection.end() + await container.stop() }) }) - const [rows] = await connection.promise().query(`SELECT * FROM member WHERE id = ?`, 'a') - t.equal(rows[0].id, 'a', 'id in SELECT query hooking') - t.equal(rows[0].name, 'name1', 'name in SELECT query hooking') - t.equal(rows[0].joined.toISOString().slice(0, 10), '2023-01-17', 'joined in SELECT query hooking') - - setTimeout(() => { - res.send('ok get') - }, 1000) - - agent.callbackTraceClose(async (trace) => { - let actualBuilder = new MethodDescriptorBuilder(expected('get', 'app.get')) - .setClassName(expected('app', 'Function')) - .setLineNumber(218) - .setFileName('nested-async-trace.test.js') - let actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) - let actualSpanEvent = trace.spanBuilder.spanEventList.find(spanEvent => spanEvent.sequence === 0) - t.equal(actualMethodDescriptor.apiId, actualSpanEvent.apiId, 'apiId is equal') - t.equal(actualMethodDescriptor.apiDescriptor, expected('app.get', 'Function.app.get'), 'apiDescriptor is equal') - t.equal(actualMethodDescriptor.className, expected('app', 'Function'), 'className is equal') - t.equal(actualMethodDescriptor.methodName, 'get', 'methodName is equal') - t.equal(actualSpanEvent.sequence, 0, 'sequence is 0') - t.equal(actualSpanEvent.depth, 1, 'depth is 0') - t.equal(actualSpanEvent.serviceType, expressServiceType.getCode(), 'serviceType is express') - - actualBuilder = new MethodDescriptorBuilder('createConnection') - .setLineNumber(219) - .setFileName('nested-async-trace.test.js') - actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) - actualSpanEvent = trace.spanBuilder.spanEventList.find(spanEvent => spanEvent.sequence === 1) - t.equal(actualMethodDescriptor.apiId, actualSpanEvent.apiId, 'apiId is equal') - t.equal(actualSpanEvent.endPoint, 'localhost', 'endPoint is equal') - t.equal(actualSpanEvent.destinationId, 'test', 'destinationId is equal') - t.equal(actualSpanEvent.sequence, 1, 'sequence is 1') - t.equal(actualSpanEvent.depth, 2, 'depth is 2') - t.equal(actualSpanEvent.serviceType, mysqlServiceType.getCode(), 'serviceType is mysql') - - actualBuilder = new MethodDescriptorBuilder('query') - .setClassName('Connection') - .setLineNumber(228) - .setFileName('nested-async-trace.test.js') - actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) - actualSpanEvent = trace.spanBuilder.spanEventList.find(spanEvent => spanEvent.sequence === 2) - t.equal(actualMethodDescriptor.apiId, actualSpanEvent.apiId, 'apiId is equal') - t.equal(actualSpanEvent.sequence, 2, 'sequence is 2') - t.equal(actualSpanEvent.depth, 2, 'depth is 2') - t.equal(actualSpanEvent.serviceType, mysqlExecuteQueryServiceType.getCode(), 'serviceType is mysql') - let actualNextAsyncId = actualSpanEvent.asyncId - - let actualSpanChunk = trace.repository.dataSender.findSpanChunk(actualNextAsyncId) - t.equal(actualSpanChunk.spanId, actualSpanEvent.spanId, 'spanId is equal') - t.equal(actualSpanChunk.getTraceRoot().getTransactionId(), trace.spanBuilder.getTraceRoot().getTransactionId(), 'transactionIdObject is equal') - t.equal(actualSpanChunk.localAsyncId.asyncId, actualSpanEvent.asyncId.asyncId, 'localAsyncId is equal') - t.equal(actualSpanChunk.localAsyncId.sequence, 1, 'localAsyncId.sequence is equal') - t.equal(actualSpanChunk.spanEventList[0].apiId, defaultPredefinedMethodDescriptorRegistry.asyncInvocationDescriptor.apiId, 'apiId is equal') - t.equal(actualSpanChunk.spanEventList[0].depth, 1, 'depth is equal') - t.equal(actualSpanChunk.spanEventList[0].sequence, 0, 'sequence is equal') - t.equal(actualSpanChunk.spanEventList[0].serviceType, ServiceType.async.getCode(), 'serviceType is mysql') - - actualBuilder = new MethodDescriptorBuilder('query') - .setClassName('Connection') - .setLineNumber(234) - .setFileName('nested-async-trace.test.js') - actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) - actualSpanEvent = actualSpanChunk.spanEventList[1] - t.equal(actualMethodDescriptor.apiId, actualSpanEvent.apiId, 'apiId is equal') - t.equal(actualSpanEvent.depth, 2, 'depth is 3') - t.equal(actualSpanEvent.sequence, 1, 'sequence is 2') - t.equal(actualSpanEvent.serviceType, mysqlExecuteQueryServiceType.getCode(), 'serviceType is mysql') - actualNextAsyncId = actualSpanEvent.asyncId - - actualSpanChunk = trace.repository.dataSender.findSpanChunk(actualNextAsyncId) - t.equal(actualSpanChunk.spanId, actualSpanEvent.spanId, 'spanId is equal') - t.equal(actualSpanChunk.getTraceRoot().getTransactionId(), trace.spanBuilder.getTraceRoot().getTransactionId(), 'transactionIdObject is equal') - t.equal(actualSpanChunk.localAsyncId.asyncId, actualNextAsyncId.asyncId, 'localAsyncId is equal') - t.equal(actualSpanChunk.localAsyncId.sequence, 1, 'localAsyncId.sequence is equal') - t.equal(actualSpanChunk.spanEventList[0].apiId, defaultPredefinedMethodDescriptorRegistry.asyncInvocationDescriptor.apiId, 'apiId is equal') - t.equal(actualSpanChunk.spanEventList[0].depth, 1, 'depth is equal') - t.equal(actualSpanChunk.spanEventList[0].sequence, 0, 'sequence is equal') - t.equal(actualSpanChunk.spanEventList[0].serviceType, ServiceType.async.getCode(), 'serviceType is mysql') - - actualBuilder = new MethodDescriptorBuilder('query') - .setClassName('Connection') - .setLineNumber(103) - .setFileName('promise.js') - actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) - actualSpanEvent = trace.spanBuilder.spanEventList.find(spanEvent => spanEvent.sequence === 3) - t.equal(actualMethodDescriptor.apiId, actualSpanEvent.apiId, 'apiId is equal') - t.equal(actualSpanEvent.sequence, 3, 'sequence is 3') - t.equal(actualSpanEvent.depth, 2, 'depth is 2') - t.equal(actualSpanEvent.serviceType, mysqlExecuteQueryServiceType.getCode(), 'serviceType is mysql') - actualNextAsyncId = actualSpanEvent.asyncId - - actualSpanChunk = trace.repository.dataSender.findSpanChunk(actualNextAsyncId) - t.equal(actualSpanChunk.spanId, actualSpanEvent.spanId, 'spanId is equal') - t.equal(actualSpanChunk.getTraceRoot().getTransactionId(), trace.spanBuilder.getTraceRoot().getTransactionId(), 'transactionIdObject is equal') - t.equal(actualSpanChunk.localAsyncId.asyncId, actualNextAsyncId.asyncId, 'localAsyncId is equal') - t.equal(actualSpanChunk.localAsyncId.sequence, 1, 'localAsyncId.sequence is equal') - t.equal(actualSpanChunk.spanEventList[0].apiId, defaultPredefinedMethodDescriptorRegistry.asyncInvocationDescriptor.apiId, 'apiId is equal') - t.equal(actualSpanChunk.spanEventList[0].depth, 1, 'depth is equal') - t.equal(actualSpanChunk.spanEventList[0].sequence, 0, 'sequence is equal') - t.equal(actualSpanChunk.spanEventList[0].serviceType, ServiceType.async.getCode(), 'serviceType is mysql') - - connection.end() - await container.stop() + const server = app.listen(5006, async () => { + const result = await axios.get('http://localhost:5006/test1') + t.equal(result.status, 200, 'status is 200') + + t.end() + server.close() }) }) - const server = app.listen(5006, async () => { - const result = await axios.get('http://localhost:5006/test1') - t.equal(result.status, 200, 'status is 200') - - t.end() - server.close() + t.teardown(() => { + collectorServer.forceShutdown() }) }) \ No newline at end of file diff --git a/test/support/data-sender-mock.js b/test/support/data-sender-mock.js index 6d37035e..7ec0b1c7 100644 --- a/test/support/data-sender-mock.js +++ b/test/support/data-sender-mock.js @@ -25,6 +25,10 @@ class MockDataSender extends DataSender { } send(data) { + if (this.closed) { + return + } + if (data instanceof AgentInfo) { this.mockAgentInfo = data super.send(data) @@ -66,6 +70,11 @@ class MockDataSender extends DataSender { this.mockSpanChunks = [] this.mockSpans = [] } + + close() { + super.close() + this.closed = true + } } const dataSender = (conf, agentInfo) => {