From d8be481d1378b2dd732b60c155d0a053025fb693 Mon Sep 17 00:00:00 2001 From: Tomasz Pytel Date: Tue, 23 Feb 2021 11:37:57 -0300 Subject: [PATCH 1/9] Add PgPlugin - PosgreSQL --- README.md | 3 +- src/config/AgentConfig.ts | 4 +- src/plugins/MySQLPlugin.ts | 21 +++--- src/plugins/PgPlugin.ts | 130 +++++++++++++++++++++++++++++++++++++ src/trace/Component.ts | 1 + 5 files changed, 146 insertions(+), 13 deletions(-) create mode 100644 src/plugins/PgPlugin.ts diff --git a/README.md b/README.md index 94c2d0c..f710d45 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ Environment Variable | Description | Default | `SW_AGENT_LOGGING_LEVEL` | The logging level, could be one of `CRITICAL`, `FATAL`, `ERROR`, `WARN`(`WARNING`), `INFO`, `DEBUG` | `INFO` | | `SW_IGNORE_SUFFIX` | The suffices of endpoints that will be ignored (not traced), comma separated | `.jpg,.jpeg,.js,.css,.png,.bmp,.gif,.ico,.mp3,.mp4,.html,.svg` | | `SW_TRACE_IGNORE_PATH` | The paths of endpoints that will be ignored (not traced), comma separated | `` | -| `SW_MYSQL_SQL_PARAMETERS_MAX_LENGTH` | The maximum string length of MySQL parameters to log | `512` | +| `SW_SQL_PARAMETERS_MAX_LENGTH` | The maximum string length of SQL parameters to log | `512` | | `SW_AGENT_MAX_BUFFER_SIZE` | The maximum buffer size before sending the segment data to backend | `'1000'` | ## Supported Libraries @@ -70,6 +70,7 @@ Library | Plugin Name | [`express`](https://expressjs.com) | `express` | | [`axios`](https://github.com/axios/axios) | `axios` | | [`mysql`](https://github.com/mysqljs/mysql) | `mysql` | +| [`pg`](https://github.com/brianc/node-postgres) | `pg` | ### Compatible Libraries diff --git a/src/config/AgentConfig.ts b/src/config/AgentConfig.ts index de68ccb..09eeb41 100644 --- a/src/config/AgentConfig.ts +++ b/src/config/AgentConfig.ts @@ -27,7 +27,7 @@ export type AgentConfig = { maxBufferSize?: number; ignoreSuffix?: string; traceIgnorePath?: string; - mysql_sql_parameters_max_length?: number; + sql_parameters_max_length?: number; // the following is internal state computed from config values reIgnoreOperation?: RegExp; }; @@ -60,6 +60,6 @@ export default { Number.parseInt(process.env.SW_AGENT_MAX_BUFFER_SIZE as string, 10) : 1000, ignoreSuffix: process.env.SW_IGNORE_SUFFIX ?? '.jpg,.jpeg,.js,.css,.png,.bmp,.gif,.ico,.mp3,.mp4,.html,.svg', traceIgnorePath: process.env.SW_TRACE_IGNORE_PATH || '', - mysql_sql_parameters_max_length: Math.trunc(Math.max(0, Number(process.env.SW_MYSQL_SQL_PARAMETERS_MAX_LENGTH))) || 512, + sql_parameters_max_length: Math.trunc(Math.max(0, Number(process.env.SW_MYSQL_SQL_PARAMETERS_MAX_LENGTH))) || 512, reIgnoreOperation: RegExp(''), // temporary placeholder so Typescript doesn't throw a fit }; diff --git a/src/plugins/MySQLPlugin.ts b/src/plugins/MySQLPlugin.ts index 6a3e6dd..a1888f2 100644 --- a/src/plugins/MySQLPlugin.ts +++ b/src/plugins/MySQLPlugin.ts @@ -56,6 +56,13 @@ class MySQLPlugin implements SwPlugin { const span = ContextManager.current.newExitSpan('mysql/query', host).start(); try { + span.component = Component.MYSQL; + span.layer = SpanLayer.DATABASE; + span.peer = host; + + span.tag(Tag.dbType('Mysql')); + span.tag(Tag.dbInstance(`${this.config.database}`)); + let _sql: any; let _values: any; let streaming: any; @@ -103,19 +110,13 @@ class MySQLPlugin implements SwPlugin { } } - span.component = Component.MYSQL; - span.layer = SpanLayer.DATABASE; - span.peer = host; - - span.tag(Tag.dbType('mysql')); - span.tag(Tag.dbInstance(this.config.database || '')); - span.tag(Tag.dbStatement(_sql || '')); + span.tag(Tag.dbStatement(`${_sql}`)); if (_values) { - let vals = _values.map((v: any) => `${v}`).join(', '); + let vals = _values.map((v: any) => v === undefined ? 'undefined' : JSON.stringify(v)).join(', '); - if (vals.length > config.mysql_sql_parameters_max_length) - vals = vals.splice(0, config.mysql_sql_parameters_max_length); + if (vals.length > config.sql_parameters_max_length) + vals = vals.splice(0, config.sql_parameters_max_length); span.tag(Tag.dbSqlParameters(`[${vals}]`)); } diff --git a/src/plugins/PgPlugin.ts b/src/plugins/PgPlugin.ts new file mode 100644 index 0000000..5e426b2 --- /dev/null +++ b/src/plugins/PgPlugin.ts @@ -0,0 +1,130 @@ +/*! + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import SwPlugin from '../core/SwPlugin'; +import ContextManager from '../trace/context/ContextManager'; +import { Component } from '../trace/Component'; +import Tag from '../Tag'; +import { SpanLayer } from '../proto/language-agent/Tracing_pb'; +import { createLogger } from '../logging'; +import PluginInstaller from '../core/PluginInstaller'; +import agentConfig from '../config/AgentConfig'; + +const logger = createLogger(__filename); + +class MySQLPlugin implements SwPlugin { + readonly module = 'pg'; + readonly versions = '*'; + + install(installer: PluginInstaller): void { + if (logger.isDebugEnabled()) { + logger.debug('installing pg plugin'); + } + + const Client = installer.require('pg/lib/client'); + const _query = Client.prototype.query; + + Client.prototype.query = function(config: any, values: any, callback: any) { + const wrapCallback = (_cb: any) => { + return function(this: any, err: any, res: any) { + if (err) + span.error(err); + + span.stop(); + + return _cb.call(this, err, res); + } + }; + + const host = `${this.host}:${this.port}`; + const span = ContextManager.current.newExitSpan('pg/query', host).start(); + + try { + span.component = Component.POSTGRESQL; + span.layer = SpanLayer.DATABASE; + span.peer = host; + + span.tag(Tag.dbType('PostgreSQL')); + span.tag(Tag.dbInstance(`${this.connectionParameters.database}`)); + + let _sql: any; + let _values: any; + + if (typeof config === 'string') + _sql = config; + + else if (config !== null && config !== undefined) { + _sql = config.text; + _values = config.values; + + if (typeof config.callback === 'function') + config.callback = wrapCallback(config.callback); + } + + if (typeof values === 'function') + values = wrapCallback(values); + else + _values = values; + + if (typeof callback === 'function') + callback = wrapCallback(callback); + + span.tag(Tag.dbStatement(`${_sql}`)); + + if (_values) { + let vals = _values.map((v: any) => v === undefined ? 'undefined' : JSON.stringify(v)).join(', '); + + if (vals.length > agentConfig.sql_parameters_max_length) + vals = vals.splice(0, agentConfig.sql_parameters_max_length); + + span.tag(Tag.dbSqlParameters(`[${vals}]`)); + } + + let query = _query.call(this, config, values, callback); + + if (query && typeof query.then === 'function' && typeof query.catch === 'function') // generic Promise check + query = query.then( + (res: any) => { + span.stop(); + + return res; + }, + + (err: any) => { + span.error(err); + span.stop(); + + return Promise.reject(err); + } + ); + + return query; + + } catch (e) { + span.error(e); + span.stop(); + + throw e; + } + }; + } +} + +// noinspection JSUnusedGlobalSymbols +export default new MySQLPlugin(); diff --git a/src/trace/Component.ts b/src/trace/Component.ts index 6469900..6c3a69d 100644 --- a/src/trace/Component.ts +++ b/src/trace/Component.ts @@ -22,6 +22,7 @@ export class Component { static readonly HTTP = new Component(2); static readonly MYSQL = new Component(5); static readonly MONGODB = new Component(9); + static readonly POSTGRESQL = new Component(22); static readonly HTTP_SERVER = new Component(49); static readonly EXPRESS = new Component(4002); static readonly AXIOS = new Component(4005); From b2dec487565bfb01ffa3106603d2fc2c2c05e312 Mon Sep 17 00:00:00 2001 From: Tomasz Pytel Date: Tue, 23 Feb 2021 11:55:54 -0300 Subject: [PATCH 2/9] forgot the async() and resync() --- src/plugins/MySQLPlugin.ts | 23 ++++++++++++++++++----- src/plugins/PgPlugin.ts | 14 +++++++++++--- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/plugins/MySQLPlugin.ts b/src/plugins/MySQLPlugin.ts index a1888f2..6510089 100644 --- a/src/plugins/MySQLPlugin.ts +++ b/src/plugins/MySQLPlugin.ts @@ -43,6 +43,8 @@ class MySQLPlugin implements SwPlugin { Connection.prototype.query = function(sql: any, values: any, cb: any) { const wrapCallback = (_cb: any) => { return function(this: any, error: any, results: any, fields: any) { + span.resync(); + if (error) span.error(error); @@ -52,6 +54,8 @@ class MySQLPlugin implements SwPlugin { } }; + let query: any; + const host = `${this.config.host}:${this.config.port}`; const span = ContextManager.current.newExitSpan('mysql/query', host).start(); @@ -121,21 +125,30 @@ class MySQLPlugin implements SwPlugin { span.tag(Tag.dbSqlParameters(`[${vals}]`)); } - const query = _query.call(this, sql, values, cb); + query = _query.call(this, sql, values, cb); if (streaming) { - query.on('error', (e: any) => span.error(e)); - query.on('end', () => span.stop()); + query.on('error', (e: any) => { + span.resync(); + span.error(e); + }); + + query.on('end', () => { + span.resync(); // may have already been done in 'error' but safe to do multiple times + span.stop() + }); } - return query; - } catch (e) { span.error(e); span.stop(); throw e; } + + span.async(); + + return query; }; } } diff --git a/src/plugins/PgPlugin.ts b/src/plugins/PgPlugin.ts index 5e426b2..e17cf60 100644 --- a/src/plugins/PgPlugin.ts +++ b/src/plugins/PgPlugin.ts @@ -43,6 +43,8 @@ class MySQLPlugin implements SwPlugin { Client.prototype.query = function(config: any, values: any, callback: any) { const wrapCallback = (_cb: any) => { return function(this: any, err: any, res: any) { + span.resync(); + if (err) span.error(err); @@ -52,6 +54,8 @@ class MySQLPlugin implements SwPlugin { } }; + let query: any; + const host = `${this.host}:${this.port}`; const span = ContextManager.current.newExitSpan('pg/query', host).start(); @@ -96,17 +100,19 @@ class MySQLPlugin implements SwPlugin { span.tag(Tag.dbSqlParameters(`[${vals}]`)); } - let query = _query.call(this, config, values, callback); + query = _query.call(this, config, values, callback); if (query && typeof query.then === 'function' && typeof query.catch === 'function') // generic Promise check query = query.then( (res: any) => { + span.resync(); span.stop(); return res; }, (err: any) => { + span.resync(); span.error(err); span.stop(); @@ -114,14 +120,16 @@ class MySQLPlugin implements SwPlugin { } ); - return query; - } catch (e) { span.error(e); span.stop(); throw e; } + + span.async(); + + return query; }; } } From 12ae5ca0c7931548857ef91e59dd2423eacd5b8d Mon Sep 17 00:00:00 2001 From: Tomasz Pytel Date: Tue, 23 Feb 2021 15:11:11 -0300 Subject: [PATCH 3/9] trying test copied from mysql2 branch --- package.json | 4 +- tests/plugins/mysql/client.ts | 38 +++++++++ tests/plugins/mysql/docker-compose.yml | 88 ++++++++++++++++++++ tests/plugins/mysql/expected.data.yaml | 111 +++++++++++++++++++++++++ tests/plugins/mysql/init/init.sql | 7 ++ tests/plugins/mysql/server.ts | 47 +++++++++++ tests/plugins/mysql/test.ts | 56 +++++++++++++ 7 files changed, 350 insertions(+), 1 deletion(-) create mode 100644 tests/plugins/mysql/client.ts create mode 100644 tests/plugins/mysql/docker-compose.yml create mode 100644 tests/plugins/mysql/expected.data.yaml create mode 100644 tests/plugins/mysql/init/init.sql create mode 100644 tests/plugins/mysql/server.ts create mode 100644 tests/plugins/mysql/test.ts diff --git a/package.json b/package.json index f6c9d31..b037c70 100644 --- a/package.json +++ b/package.json @@ -46,9 +46,11 @@ "@types/uuid": "^8.0.0", "axios": "^0.21.0", "express": "^4.17.1", - "grpc-tools": "^1.10.0", "grpc_tools_node_protoc_ts": "^4.0.0", + "grpc-tools": "^1.10.0", "jest": "^26.6.3", + "mysql": "^2.18.1", + "pg": "^8.5.1", "prettier": "^2.0.5", "testcontainers": "^6.2.0", "ts-jest": "^26.4.4", diff --git a/tests/plugins/mysql/client.ts b/tests/plugins/mysql/client.ts new file mode 100644 index 0000000..2f33af1 --- /dev/null +++ b/tests/plugins/mysql/client.ts @@ -0,0 +1,38 @@ +/*! + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import * as http from 'http'; +import agent from '../../../src'; + +agent.start({ + serviceName: 'client', + maxBufferSize: 1000, +}) + +const server = http.createServer((req, res) => { + http + .request(`http://${process.env.SERVER || 'localhost:5000'}${req.url}`, (r) => { + let data = ''; + r.on('data', (chunk) => (data += chunk)); + r.on('end', () => res.end(data)); + }) + .end(); +}); + +server.listen(5001, () => console.info('Listening on port 5001...')); diff --git a/tests/plugins/mysql/docker-compose.yml b/tests/plugins/mysql/docker-compose.yml new file mode 100644 index 0000000..95ed073 --- /dev/null +++ b/tests/plugins/mysql/docker-compose.yml @@ -0,0 +1,88 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +version: "3.8" + +services: + collector: + extends: + file: ../common/base-compose.yml + service: collector + networks: + - traveling-light + + mysql: + environment: + MYSQL_ROOT_PASSWORD: "root" + MYSQL_DATABASE: "test" + ports: + - 3306:3306 + volumes: + - ./init:/docker-entrypoint-initdb.d + healthcheck: + test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/3306"] + interval: 5s + timeout: 60s + retries: 120 + image: "docker.io/mysql:latest" + networks: + - traveling-light + + server: + extends: + file: ../common/base-compose.yml + service: agent + ports: + - 5000:5000 + environment: + MYSQL_HOST: mysql + volumes: + - .:/app/tests/plugins/mysql + healthcheck: + test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/5000"] + interval: 5s + timeout: 60s + retries: 120 + entrypoint: + ["bash", "-c", "npx ts-node /app/tests/plugins/mysql/server.ts"] + depends_on: + collector: + condition: service_healthy + mysql: + condition: service_healthy + + client: + extends: + file: ../common/base-compose.yml + service: agent + ports: + - 5001:5001 + environment: + SERVER: server:5000 + healthcheck: + test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/5001"] + interval: 5s + timeout: 60s + retries: 120 + entrypoint: + ["bash", "-c", "npx ts-node /app/tests/plugins/mysql/client.ts"] + depends_on: + server: + condition: service_healthy + +networks: + traveling-light: diff --git a/tests/plugins/mysql/expected.data.yaml b/tests/plugins/mysql/expected.data.yaml new file mode 100644 index 0000000..85d082b --- /dev/null +++ b/tests/plugins/mysql/expected.data.yaml @@ -0,0 +1,111 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +segmentItems: + - serviceName: server + segmentSize: 1 + segments: + - segmentId: not null + spans: + - operationName: /mysql + operationId: 0 + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: gt 0 + endTime: gt 0 + componentId: 49 + spanType: Entry + peer: not null + skipAnalysis: false + tags: + - key: http.url + value: http://server:5000/mysql + - key: http.method + value: GET + - key: http.status.code + value: "200" + refs: + - parentEndpoint: "" + networkAddress: server:5000 + refType: CrossProcess + parentSpanId: 1 + parentTraceSegmentId: not null + parentServiceInstance: not null + parentService: client + traceId: not null + - operationName: Mysql/query + operationId: 0 + parentSpanId: 0 + spanId: 1 + spanLayer: Database + startTime: gt 0 + endTime: gt 0 + componentId: 5 + spanType: Exit + peer: mysql:3306 + skipAnalysis: false + tags: + - key: db.type + value: mysql + - key: db.instance + value: test + - key: db.statement + value: SELECT * FROM `user` WHERE `name` = "u1" + - serviceName: client + segmentSize: 1 + segments: + - segmentId: not null + spans: + - operationName: /mysql + operationId: 0 + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: gt 0 + endTime: gt 0 + componentId: 49 + spanType: Entry + peer: not null + skipAnalysis: false + tags: + - key: http.url + value: http://localhost:5001/mysql + - key: http.method + value: GET + - key: http.status.code + value: "200" + - operationName: /mysql + operationId: 0 + parentSpanId: 0 + spanId: 1 + spanLayer: Http + startTime: gt 0 + endTime: gt 0 + componentId: 2 + spanType: Exit + peer: server:5000 + skipAnalysis: false + tags: + - key: http.url + value: server:5000/mysql + - key: http.method + value: GET + - key: http.status.code + value: "200" + - key: http.status.msg + value: OK diff --git a/tests/plugins/mysql/init/init.sql b/tests/plugins/mysql/init/init.sql new file mode 100644 index 0000000..6d9931d --- /dev/null +++ b/tests/plugins/mysql/init/init.sql @@ -0,0 +1,7 @@ +use test; + +CREATE TABLE IF NOT EXISTS `user`( + `id` INT UNSIGNED AUTO_INCREMENT, + `name` VARCHAR(100) NOT NULL, + PRIMARY KEY( `id` ) +)ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/tests/plugins/mysql/server.ts b/tests/plugins/mysql/server.ts new file mode 100644 index 0000000..2d9e20d --- /dev/null +++ b/tests/plugins/mysql/server.ts @@ -0,0 +1,47 @@ +/*! + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import * as http from 'http'; +import mysql from 'mysql'; +import agent from '../../../src'; + +agent.start({ + serviceName: 'server', + maxBufferSize: 1000, +}) + +const server = http.createServer((req, res) => { + const connection = mysql.createConnection({ + host: process.env.MYSQL_HOST || 'mysql', + user: 'root', + password: 'root', + database: 'test' + }); + connection.query( + 'SELECT * FROM `user` WHERE `name` = "u1"', + function (err: any, results: any, fields: any) { + res.end(JSON.stringify({ + results, + fields + })) + } + ); +}) + +server.listen(5000, () => console.info('Listening on port 5000...')); diff --git a/tests/plugins/mysql/test.ts b/tests/plugins/mysql/test.ts new file mode 100644 index 0000000..87087d3 --- /dev/null +++ b/tests/plugins/mysql/test.ts @@ -0,0 +1,56 @@ +/*! + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import * as path from 'path'; +import { DockerComposeEnvironment, StartedDockerComposeEnvironment, Wait } from 'testcontainers'; +import axios from 'axios'; +import waitForExpect from 'wait-for-expect'; +import { promises as fs } from 'fs'; + +const rootDir = path.resolve(__dirname); + +describe('plugin tests', () => { + let compose: StartedDockerComposeEnvironment; + + beforeAll(async () => { + compose = await new DockerComposeEnvironment(rootDir, 'docker-compose.yml') + .withWaitStrategy('client', Wait.forHealthCheck()) + .up(); + }); + + afterAll(async () => { + await compose.down(); + }); + + it(__filename, async () => { + await waitForExpect(async () => expect((await axios.get('http://localhost:5001/mysql')).status).toBe(200)); + + const expectedData = await fs.readFile(path.join(rootDir, 'expected.data.yaml'), 'utf8'); + + try { + await waitForExpect(async () => + expect((await axios.post('http://localhost:12800/dataValidate', expectedData)).status).toBe(200), + ); + } catch (e) { + const actualData = (await axios.get('http://localhost:12800/receiveData')).data; + console.info({ actualData }); + throw e; + } + }); +}); From 08b332f3e2385a397870e5eb412c6985026587b5 Mon Sep 17 00:00:00 2001 From: Tomasz Pytel Date: Thu, 25 Feb 2021 08:07:32 -0300 Subject: [PATCH 4/9] test older version of mysql --- tests/plugins/mysql/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/plugins/mysql/docker-compose.yml b/tests/plugins/mysql/docker-compose.yml index 95ed073..26ae713 100644 --- a/tests/plugins/mysql/docker-compose.yml +++ b/tests/plugins/mysql/docker-compose.yml @@ -38,7 +38,7 @@ services: interval: 5s timeout: 60s retries: 120 - image: "docker.io/mysql:latest" + image: "docker.io/mysql:5.7.33" networks: - traveling-light From 5a71ebff6c87aecd3b617373e414947f738ca358 Mon Sep 17 00:00:00 2001 From: Tomasz Pytel Date: Thu, 25 Feb 2021 12:28:53 -0300 Subject: [PATCH 5/9] ... --- src/plugins/MySQLPlugin.ts | 2 +- tests/plugins/mysql/expected.data.yaml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/MySQLPlugin.ts b/src/plugins/MySQLPlugin.ts index 6510089..8e87ab6 100644 --- a/src/plugins/MySQLPlugin.ts +++ b/src/plugins/MySQLPlugin.ts @@ -122,7 +122,7 @@ class MySQLPlugin implements SwPlugin { if (vals.length > config.sql_parameters_max_length) vals = vals.splice(0, config.sql_parameters_max_length); - span.tag(Tag.dbSqlParameters(`[${vals}]`)); + span.tag(Tag.dbSqlParameters(`[${vals}]`)); } query = _query.call(this, sql, values, cb); diff --git a/tests/plugins/mysql/expected.data.yaml b/tests/plugins/mysql/expected.data.yaml index 85d082b..bc8bc1e 100644 --- a/tests/plugins/mysql/expected.data.yaml +++ b/tests/plugins/mysql/expected.data.yaml @@ -48,7 +48,7 @@ segmentItems: parentServiceInstance: not null parentService: client traceId: not null - - operationName: Mysql/query + - operationName: mysql/query operationId: 0 parentSpanId: 0 spanId: 1 @@ -61,7 +61,7 @@ segmentItems: skipAnalysis: false tags: - key: db.type - value: mysql + value: Mysql - key: db.instance value: test - key: db.statement From e82099032a29d558e29998dd62016d14071b4f9e Mon Sep 17 00:00:00 2001 From: kezhenxu94 Date: Fri, 26 Feb 2021 17:51:51 +0800 Subject: [PATCH 6/9] fix test --- tests/plugins/mysql/docker-compose.yml | 1 + tests/plugins/mysql/test.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/plugins/mysql/docker-compose.yml b/tests/plugins/mysql/docker-compose.yml index 26ae713..839b603 100644 --- a/tests/plugins/mysql/docker-compose.yml +++ b/tests/plugins/mysql/docker-compose.yml @@ -26,6 +26,7 @@ services: - traveling-light mysql: + container_name: mysql environment: MYSQL_ROOT_PASSWORD: "root" MYSQL_DATABASE: "test" diff --git a/tests/plugins/mysql/test.ts b/tests/plugins/mysql/test.ts index 87087d3..e2b14b5 100644 --- a/tests/plugins/mysql/test.ts +++ b/tests/plugins/mysql/test.ts @@ -31,6 +31,7 @@ describe('plugin tests', () => { beforeAll(async () => { compose = await new DockerComposeEnvironment(rootDir, 'docker-compose.yml') .withWaitStrategy('client', Wait.forHealthCheck()) + .withWaitStrategy('mysql', Wait.forHealthCheck()) .up(); }); From 05669891dbd93a2f5203e540726a24890335fdf3 Mon Sep 17 00:00:00 2001 From: Zhenxu Ke Date: Fri, 26 Feb 2021 18:18:05 +0800 Subject: [PATCH 7/9] Apply suggestions from code review --- src/config/AgentConfig.ts | 2 +- tests/plugins/mysql/init/init.sql | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/config/AgentConfig.ts b/src/config/AgentConfig.ts index 09eeb41..a91fbc9 100644 --- a/src/config/AgentConfig.ts +++ b/src/config/AgentConfig.ts @@ -60,6 +60,6 @@ export default { Number.parseInt(process.env.SW_AGENT_MAX_BUFFER_SIZE as string, 10) : 1000, ignoreSuffix: process.env.SW_IGNORE_SUFFIX ?? '.jpg,.jpeg,.js,.css,.png,.bmp,.gif,.ico,.mp3,.mp4,.html,.svg', traceIgnorePath: process.env.SW_TRACE_IGNORE_PATH || '', - sql_parameters_max_length: Math.trunc(Math.max(0, Number(process.env.SW_MYSQL_SQL_PARAMETERS_MAX_LENGTH))) || 512, + sql_parameters_max_length: Math.trunc(Math.max(0, Number(process.env.SW_SQL_SQL_PARAMETERS_MAX_LENGTH))) || 512, reIgnoreOperation: RegExp(''), // temporary placeholder so Typescript doesn't throw a fit }; diff --git a/tests/plugins/mysql/init/init.sql b/tests/plugins/mysql/init/init.sql index 6d9931d..844112b 100644 --- a/tests/plugins/mysql/init/init.sql +++ b/tests/plugins/mysql/init/init.sql @@ -1,3 +1,18 @@ +-- Licensed to the Apache Software Foundation (ASF) under one or more +-- contributor license agreements. See the NOTICE file distributed with +-- this work for additional information regarding copyright ownership. +-- The ASF licenses this file to You under the Apache License, Version 2.0 +-- (the "License"); you may not use this file except in compliance with +-- the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. + use test; CREATE TABLE IF NOT EXISTS `user`( From a882dce6dd1eb82b646ff530ba546b05e4697125 Mon Sep 17 00:00:00 2001 From: Tomasz Pytel Date: Fri, 26 Feb 2021 10:20:41 -0300 Subject: [PATCH 8/9] postgres test --- tests/plugins/axios/docker-compose.yml | 2 +- tests/plugins/express/docker-compose.yml | 2 +- tests/plugins/mysql/docker-compose.yml | 2 +- tests/plugins/pg/client.ts | 40 ++++++++ tests/plugins/pg/docker-compose.yml | 90 ++++++++++++++++++ tests/plugins/pg/expected.data.yaml | 111 +++++++++++++++++++++++ tests/plugins/pg/init/init.sql | 22 +++++ tests/plugins/pg/server.ts | 50 ++++++++++ tests/plugins/pg/test.ts | 57 ++++++++++++ 9 files changed, 373 insertions(+), 3 deletions(-) create mode 100644 tests/plugins/pg/client.ts create mode 100644 tests/plugins/pg/docker-compose.yml create mode 100644 tests/plugins/pg/expected.data.yaml create mode 100644 tests/plugins/pg/init/init.sql create mode 100644 tests/plugins/pg/server.ts create mode 100644 tests/plugins/pg/test.ts diff --git a/tests/plugins/axios/docker-compose.yml b/tests/plugins/axios/docker-compose.yml index b88cbee..0216550 100644 --- a/tests/plugins/axios/docker-compose.yml +++ b/tests/plugins/axios/docker-compose.yml @@ -15,7 +15,7 @@ # limitations under the License. # -version: '3.8' +version: '2.1' services: collector: diff --git a/tests/plugins/express/docker-compose.yml b/tests/plugins/express/docker-compose.yml index 224e4c6..69c9e35 100644 --- a/tests/plugins/express/docker-compose.yml +++ b/tests/plugins/express/docker-compose.yml @@ -15,7 +15,7 @@ # limitations under the License. # -version: '3.8' +version: '2.1' services: collector: diff --git a/tests/plugins/mysql/docker-compose.yml b/tests/plugins/mysql/docker-compose.yml index 839b603..e0bb15e 100644 --- a/tests/plugins/mysql/docker-compose.yml +++ b/tests/plugins/mysql/docker-compose.yml @@ -15,7 +15,7 @@ # limitations under the License. # -version: "3.8" +version: "2.1" services: collector: diff --git a/tests/plugins/pg/client.ts b/tests/plugins/pg/client.ts new file mode 100644 index 0000000..25ff2b3 --- /dev/null +++ b/tests/plugins/pg/client.ts @@ -0,0 +1,40 @@ +/*! + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import * as http from 'http'; +import agent from '../../../src'; + +process.env.SW_AGENT_LOGGING_LEVEL = 'ERROR'; + +agent.start({ + serviceName: 'client', + maxBufferSize: 1000, +}) + +const server = http.createServer((req, res) => { + http + .request(`http://${process.env.SERVER || 'localhost:5000'}${req.url}`, (r) => { + let data = ''; + r.on('data', (chunk) => (data += chunk)); + r.on('end', () => res.end(data)); + }) + .end(); +}); + +server.listen(5001, () => console.info('Listening on port 5001...')); diff --git a/tests/plugins/pg/docker-compose.yml b/tests/plugins/pg/docker-compose.yml new file mode 100644 index 0000000..be76bbd --- /dev/null +++ b/tests/plugins/pg/docker-compose.yml @@ -0,0 +1,90 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +version: "2.1" + +services: + collector: + extends: + file: ../common/base-compose.yml + service: collector + networks: + - traveling-light + + postgres: + container_name: postgres + environment: + POSTGRES_USER: "root" + POSTGRES_PASSWORD: "root" + POSTGRES_DB: "test" + ports: + - 5432:5432 + volumes: + - ./init:/docker-entrypoint-initdb.d + healthcheck: + test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/5432"] + interval: 5s + timeout: 60s + retries: 120 + image: "docker.io/postgres:13.2" + networks: + - traveling-light + + server: + extends: + file: ../common/base-compose.yml + service: agent + ports: + - 5000:5000 + environment: + POSTGRES_HOST: postgres + volumes: + - .:/app/tests/plugins/pg + healthcheck: + test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/5000"] + interval: 5s + timeout: 60s + retries: 120 + entrypoint: + ["bash", "-c", "npx ts-node /app/tests/plugins/pg/server.ts"] + depends_on: + collector: + condition: service_healthy + postgres: + condition: service_healthy + + client: + extends: + file: ../common/base-compose.yml + service: agent + ports: + - 5001:5001 + environment: + SERVER: server:5000 + healthcheck: + test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/5001"] + interval: 5s + timeout: 60s + retries: 120 + entrypoint: + ["bash", "-c", "npx ts-node /app/tests/plugins/pg/client.ts"] + depends_on: + server: + condition: service_healthy + +networks: + traveling-light: diff --git a/tests/plugins/pg/expected.data.yaml b/tests/plugins/pg/expected.data.yaml new file mode 100644 index 0000000..fabf6ec --- /dev/null +++ b/tests/plugins/pg/expected.data.yaml @@ -0,0 +1,111 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +segmentItems: + - serviceName: server + segmentSize: 1 + segments: + - segmentId: not null + spans: + - operationName: /postgres + operationId: 0 + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: gt 0 + endTime: gt 0 + componentId: 49 + spanType: Entry + peer: not null + skipAnalysis: false + tags: + - key: http.url + value: http://server:5000/postgres + - key: http.method + value: GET + - key: http.status.code + value: "200" + refs: + - parentEndpoint: "" + networkAddress: server:5000 + refType: CrossProcess + parentSpanId: 1 + parentTraceSegmentId: not null + parentServiceInstance: not null + parentService: client + traceId: not null + - operationName: pg/query + operationId: 0 + parentSpanId: 0 + spanId: 1 + spanLayer: Database + startTime: gt 0 + endTime: gt 0 + componentId: 5 + spanType: Exit + peer: postgres:3306 + skipAnalysis: false + tags: + - key: db.type + value: Mysql + - key: db.instance + value: test + - key: db.statement + value: SELECT * FROM `user` WHERE `name` = "u1" + - serviceName: client + segmentSize: 1 + segments: + - segmentId: not null + spans: + - operationName: /postgres + operationId: 0 + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: gt 0 + endTime: gt 0 + componentId: 49 + spanType: Entry + peer: not null + skipAnalysis: false + tags: + - key: http.url + value: http://localhost:5001/postgres + - key: http.method + value: GET + - key: http.status.code + value: "200" + - operationName: /postgres + operationId: 0 + parentSpanId: 0 + spanId: 1 + spanLayer: Http + startTime: gt 0 + endTime: gt 0 + componentId: 2 + spanType: Exit + peer: server:5000 + skipAnalysis: false + tags: + - key: http.url + value: server:5000/postgres + - key: http.method + value: GET + - key: http.status.code + value: "200" + - key: http.status.msg + value: OK diff --git a/tests/plugins/pg/init/init.sql b/tests/plugins/pg/init/init.sql new file mode 100644 index 0000000..84ed399 --- /dev/null +++ b/tests/plugins/pg/init/init.sql @@ -0,0 +1,22 @@ +-- Licensed to the Apache Software Foundation (ASF) under one or more +-- contributor license agreements. See the NOTICE file distributed with +-- this work for additional information regarding copyright ownership. +-- The ASF licenses this file to You under the Apache License, Version 2.0 +-- (the "License"); you may not use this file except in compliance with +-- the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. + +CREATE SEQUENCE user_seq; + +CREATE TABLE IF NOT EXISTS "user"( + id INT CHECK (id > 0) DEFAULT NEXTVAL ('user_seq'), + name VARCHAR(100) NOT NULL, + PRIMARY KEY( id ) +); diff --git a/tests/plugins/pg/server.ts b/tests/plugins/pg/server.ts new file mode 100644 index 0000000..5538ffc --- /dev/null +++ b/tests/plugins/pg/server.ts @@ -0,0 +1,50 @@ +/*! + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import * as http from 'http'; +import {Client} from 'pg'; +import agent from '../../../src'; + +process.env.SW_AGENT_LOGGING_LEVEL = 'ERROR'; + +agent.start({ + serviceName: 'server', + maxBufferSize: 1000, +}) + +const server = http.createServer((req, res) => { + const client = new Client({ + host: process.env.POSTGRES_HOST || 'postgres', + user: 'root', + password: 'root', + database: 'test' + }); + client.connect(); + client.query(`SELECT * FROM "user" where name = 'u1'`).then( + (resDB: any) => { + res.end(JSON.stringify(resDB.rows)); + client.end(); + }, + (err: any) => { + client.end(); + }, + ); +}) + +server.listen(5000, () => console.info('Listening on port 5000...')); diff --git a/tests/plugins/pg/test.ts b/tests/plugins/pg/test.ts new file mode 100644 index 0000000..c0f1bf3 --- /dev/null +++ b/tests/plugins/pg/test.ts @@ -0,0 +1,57 @@ +/*! + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import * as path from 'path'; +import { DockerComposeEnvironment, StartedDockerComposeEnvironment, Wait } from 'testcontainers'; +import axios from 'axios'; +import waitForExpect from 'wait-for-expect'; +import { promises as fs } from 'fs'; + +const rootDir = path.resolve(__dirname); + +describe('plugin tests', () => { + let compose: StartedDockerComposeEnvironment; + + beforeAll(async () => { + compose = await new DockerComposeEnvironment(rootDir, 'docker-compose.yml') + .withWaitStrategy('client', Wait.forHealthCheck()) + .withWaitStrategy('postgre', Wait.forHealthCheck()) + .up(); + }); + + afterAll(async () => { + await compose.down(); + }); + + it(__filename, async () => { + await waitForExpect(async () => expect((await axios.get('http://localhost:5001/postgre')).status).toBe(200)); + + const expectedData = await fs.readFile(path.join(rootDir, 'expected.data.yaml'), 'utf8'); + + try { + await waitForExpect(async () => + expect((await axios.post('http://localhost:12800/dataValidate', expectedData)).status).toBe(200), + ); + } catch (e) { + const actualData = (await axios.get('http://localhost:12800/receiveData')).data; + console.info({ actualData }); + throw e; + } + }); +}); From c644048def567953bb5a18cbf75c8bf4c20dae5a Mon Sep 17 00:00:00 2001 From: Tomasz Pytel Date: Fri, 26 Feb 2021 15:56:00 -0300 Subject: [PATCH 9/9] I have no expectation at all that this will actually work --- src/config/AgentConfig.ts | 2 +- tests/plugins/pg/expected.data.yaml | 43 ++++++++++------------------- tests/plugins/pg/test.ts | 4 +-- 3 files changed, 18 insertions(+), 31 deletions(-) diff --git a/src/config/AgentConfig.ts b/src/config/AgentConfig.ts index a91fbc9..a28be12 100644 --- a/src/config/AgentConfig.ts +++ b/src/config/AgentConfig.ts @@ -41,7 +41,7 @@ export function finalizeConfig(config: AgentConfig): void { (s2) => s2.split('*').map( (s3) => s3.split('?').map(escapeRegExp).join('[^/]') // replaces "?" ).join('[^/]*') // replaces "*" - ).join('(?:(?:[^/]+\.)*[^/]+)?') // replaces "**" + ).join('(?:(?:[^/]+/)*[^/]+)?') // replaces "**" ).join('|') + ')$'; // replaces "," config.reIgnoreOperation = RegExp(`${ignoreSuffix}|${ignorePath}`); diff --git a/tests/plugins/pg/expected.data.yaml b/tests/plugins/pg/expected.data.yaml index fabf6ec..f89d1c8 100644 --- a/tests/plugins/pg/expected.data.yaml +++ b/tests/plugins/pg/expected.data.yaml @@ -33,12 +33,9 @@ segmentItems: peer: not null skipAnalysis: false tags: - - key: http.url - value: http://server:5000/postgres - - key: http.method - value: GET - - key: http.status.code - value: "200" + - { key: http.url, value: 'http://server:5000/postgres' } + - { key: http.method, value: GET } + - { key: http.status.code, value: '200' } refs: - parentEndpoint: "" networkAddress: server:5000 @@ -55,17 +52,14 @@ segmentItems: spanLayer: Database startTime: gt 0 endTime: gt 0 - componentId: 5 + componentId: 22 spanType: Exit - peer: postgres:3306 + peer: postgres:5432 skipAnalysis: false tags: - - key: db.type - value: Mysql - - key: db.instance - value: test - - key: db.statement - value: SELECT * FROM `user` WHERE `name` = "u1" + - { key: db.type, value: PostgreSQL } + - { key: db.instance, value: test } + - { key: db.statement, value: SELECT * FROM "user" where name = 'u1' } - serviceName: client segmentSize: 1 segments: @@ -83,12 +77,9 @@ segmentItems: peer: not null skipAnalysis: false tags: - - key: http.url - value: http://localhost:5001/postgres - - key: http.method - value: GET - - key: http.status.code - value: "200" + - { key: http.url, value: 'http://localhost:5001/postgres' } + - { key: http.method, value: GET } + - { key: http.status.code, value: '200' } - operationName: /postgres operationId: 0 parentSpanId: 0 @@ -101,11 +92,7 @@ segmentItems: peer: server:5000 skipAnalysis: false tags: - - key: http.url - value: server:5000/postgres - - key: http.method - value: GET - - key: http.status.code - value: "200" - - key: http.status.msg - value: OK + - { key: http.url, value: 'server:5000/postgres' } + - { key: http.method, value: GET } + - { key: http.status.code, value: '200' } + - { key: http.status.msg, value: OK } diff --git a/tests/plugins/pg/test.ts b/tests/plugins/pg/test.ts index c0f1bf3..f541e8c 100644 --- a/tests/plugins/pg/test.ts +++ b/tests/plugins/pg/test.ts @@ -31,7 +31,7 @@ describe('plugin tests', () => { beforeAll(async () => { compose = await new DockerComposeEnvironment(rootDir, 'docker-compose.yml') .withWaitStrategy('client', Wait.forHealthCheck()) - .withWaitStrategy('postgre', Wait.forHealthCheck()) + .withWaitStrategy('postgres', Wait.forHealthCheck()) .up(); }); @@ -40,7 +40,7 @@ describe('plugin tests', () => { }); it(__filename, async () => { - await waitForExpect(async () => expect((await axios.get('http://localhost:5001/postgre')).status).toBe(200)); + await waitForExpect(async () => expect((await axios.get('http://localhost:5001/postgres')).status).toBe(200)); const expectedData = await fs.readFile(path.join(rootDir, 'expected.data.yaml'), 'utf8');