You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by ke...@apache.org on 2021/05/20 22:53:04 UTC

[skywalking-nodejs] branch master updated: Add mysql2 plugin (#54)

This is an automated email from the ASF dual-hosted git repository.

kezhenxu94 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/skywalking-nodejs.git


The following commit(s) were added to refs/heads/master by this push:
     new 4e8ea7d  Add mysql2 plugin  (#54)
4e8ea7d is described below

commit 4e8ea7decf582465da967053b6d50e3267bf4208
Author: keke <yo...@gmail.com>
AuthorDate: Fri May 21 06:52:54 2021 +0800

    Add mysql2 plugin  (#54)
---
 package-lock.json                       |  93 +++++++++++++++++++++++
 package.json                            |   3 +-
 src/plugins/MySQL2Plugin.ts             | 130 ++++++++++++++++++++++++++++++++
 tests/plugins/mysql2/client.ts          |  38 ++++++++++
 tests/plugins/mysql2/docker-compose.yml |  89 ++++++++++++++++++++++
 tests/plugins/mysql2/expected.data.yaml | 115 ++++++++++++++++++++++++++++
 tests/plugins/mysql2/init/init.sql      |  22 ++++++
 tests/plugins/mysql2/server.ts          |  47 ++++++++++++
 tests/plugins/mysql2/test.ts            |  57 ++++++++++++++
 9 files changed, 593 insertions(+), 1 deletion(-)

diff --git a/package-lock.json b/package-lock.json
index 62a9e49..844dadd 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3224,6 +3224,15 @@
         "wide-align": "^1.1.0"
       }
     },
+    "generate-function": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz",
+      "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==",
+      "dev": true,
+      "requires": {
+        "is-property": "^1.0.2"
+      }
+    },
     "gensync": {
       "version": "1.0.0-beta.2",
       "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
@@ -4032,6 +4041,12 @@
       "integrity": "sha1-DFLlS8yjkbssSUsh6GJtczbG45c=",
       "dev": true
     },
+    "is-property": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
+      "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=",
+      "dev": true
+    },
     "is-self-closing": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/is-self-closing/-/is-self-closing-1.0.1.tgz",
@@ -5675,6 +5690,72 @@
         "sqlstring": "2.3.1"
       }
     },
+    "mysql2": {
+      "version": "2.2.5",
+      "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-2.2.5.tgz",
+      "integrity": "sha512-XRqPNxcZTpmFdXbJqb+/CtYVLCx14x1RTeNMD4954L331APu75IC74GDqnZMEt1kwaXy6TySo55rF2F3YJS78g==",
+      "dev": true,
+      "requires": {
+        "denque": "^1.4.1",
+        "generate-function": "^2.3.1",
+        "iconv-lite": "^0.6.2",
+        "long": "^4.0.0",
+        "lru-cache": "^6.0.0",
+        "named-placeholders": "^1.1.2",
+        "seq-queue": "^0.0.5",
+        "sqlstring": "^2.3.2"
+      },
+      "dependencies": {
+        "iconv-lite": {
+          "version": "0.6.2",
+          "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz",
+          "integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==",
+          "dev": true,
+          "requires": {
+            "safer-buffer": ">= 2.1.2 < 3.0.0"
+          }
+        },
+        "long": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
+          "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==",
+          "dev": true
+        },
+        "sqlstring": {
+          "version": "2.3.2",
+          "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.2.tgz",
+          "integrity": "sha512-vF4ZbYdKS8OnoJAWBmMxCQDkiEBkGQYU7UZPtL8flbDRSNkhaXvRJ279ZtI6M+zDaQovVU4tuRgzK5fVhvFAhg==",
+          "dev": true
+        }
+      }
+    },
+    "named-placeholders": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.2.tgz",
+      "integrity": "sha512-wiFWqxoLL3PGVReSZpjLVxyJ1bRqe+KKJVbr4hGs1KWfTZTQyezHFBbuKj9hsizHyGV2ne7EMjHdxEGAybD5SA==",
+      "dev": true,
+      "requires": {
+        "lru-cache": "^4.1.3"
+      },
+      "dependencies": {
+        "lru-cache": {
+          "version": "4.1.5",
+          "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
+          "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
+          "dev": true,
+          "requires": {
+            "pseudomap": "^1.0.2",
+            "yallist": "^2.1.2"
+          }
+        },
+        "yallist": {
+          "version": "2.1.2",
+          "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
+          "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
+          "dev": true
+        }
+      }
+    },
     "nan": {
       "version": "2.14.2",
       "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz",
@@ -6384,6 +6465,12 @@
         "ipaddr.js": "1.9.1"
       }
     },
+    "pseudomap": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
+      "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=",
+      "dev": true
+    },
     "psl": {
       "version": "1.8.0",
       "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
@@ -6901,6 +6988,12 @@
         }
       }
     },
+    "seq-queue": {
+      "version": "0.0.5",
+      "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz",
+      "integrity": "sha1-1WgS4cAXpuTnw+Ojeh2m143TyT4=",
+      "dev": true
+    },
     "serve-static": {
       "version": "1.14.1",
       "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
diff --git a/package.json b/package.json
index 7fccce8..a05e96f 100644
--- a/package.json
+++ b/package.json
@@ -47,12 +47,13 @@
     "amqplib": "^0.7.0",
     "axios": "^0.21.0",
     "express": "^4.17.1",
-    "grpc_tools_node_protoc_ts": "^4.0.0",
     "grpc-tools": "^1.10.0",
+    "grpc_tools_node_protoc_ts": "^4.0.0",
     "jest": "^26.6.3",
     "mongodb": "^3.6.4",
     "mongoose": "^5.12.2",
     "mysql": "^2.18.1",
+    "mysql2": "^2.2.5",
     "pg": "^8.5.1",
     "prettier": "^2.0.5",
     "testcontainers": "^6.2.0",
diff --git a/src/plugins/MySQL2Plugin.ts b/src/plugins/MySQL2Plugin.ts
new file mode 100644
index 0000000..4488c96
--- /dev/null
+++ b/src/plugins/MySQL2Plugin.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, { wrapEmit, wrapCallback } 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 PluginInstaller from '../core/PluginInstaller';
+import agentConfig from '../config/AgentConfig';
+
+class MySQL2Plugin implements SwPlugin {
+  readonly module = 'mysql2';
+  readonly versions = '*';
+
+  install(installer: PluginInstaller): void {
+    const Connection = installer.require('mysql2/lib/connection');
+    const _query = Connection.prototype.query;
+
+    Connection.prototype.query = function (sql: any, values: any, cb: any) {
+      let query: any;
+
+      const host = `${this.config.host}:${this.config.port}`;
+      const span = ContextManager.current.newExitSpan('mysql/query', Component.MYSQL);
+
+      span.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;
+
+        if (typeof sql === 'function') {
+          sql = wrapCallback(span, sql, 0);
+
+        } else if (typeof sql === 'object') {
+          _sql = sql.sql;
+
+          if (typeof values === 'function') {
+            values = wrapCallback(span, values, 0);
+            _values = sql.values;
+
+          } else if (values !== undefined) {
+            _values = values;
+
+            if (typeof cb === 'function') {
+              cb = wrapCallback(span, cb, 0);
+            } else {
+              streaming = true;
+            }
+
+          } else {
+            streaming = true;
+          }
+
+        } else {
+          _sql = sql;
+
+          if (typeof values === 'function') {
+            values = wrapCallback(span, values, 0);
+
+          } else if (values !== undefined) {
+            _values = values;
+
+            if (typeof cb === 'function') {
+              cb = wrapCallback(span, cb, 0);
+            } else {
+              streaming = true;
+            }
+
+          } else {
+            streaming = true;
+          }
+        }
+
+        span.tag(Tag.dbStatement(`${_sql}`));
+
+        if (agentConfig.sqlTraceParameters && _values) {
+          let vals = _values.map((v: any) => v === undefined ? 'undefined' : JSON.stringify(v)).join(', ');
+
+          if (vals.length > agentConfig.sqlParametersMaxLength)
+            vals = vals.slice(0, agentConfig.sqlParametersMaxLength) + ' ...';
+
+          span.tag(Tag.dbSqlParameters(`[${vals}]`));
+        }
+
+        query = _query.call(this, sql, values, cb);
+
+        if (streaming)
+          wrapEmit(span, query, true, 'end');
+
+      } catch (e) {
+        span.error(e);
+        span.stop();
+
+        throw e;
+      }
+
+      span.async();
+
+      return query;
+    };
+  }
+}
+
+// noinspection JSUnusedGlobalSymbols
+export default new MySQL2Plugin();
diff --git a/tests/plugins/mysql2/client.ts b/tests/plugins/mysql2/client.ts
new file mode 100644
index 0000000..2f33af1
--- /dev/null
+++ b/tests/plugins/mysql2/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/mysql2/docker-compose.yml b/tests/plugins/mysql2/docker-compose.yml
new file mode 100644
index 0000000..c53a9e6
--- /dev/null
+++ b/tests/plugins/mysql2/docker-compose.yml
@@ -0,0 +1,89 @@
+#
+# 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
+
+  mysql:
+    container_name: 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:5.7.33"
+    networks:
+      - traveling-light
+
+  server:
+    extends:
+      file: ../common/base-compose.yml
+      service: agent
+    ports:
+      - 5000:5000
+    environment:
+      MYSQL_HOST: mysql
+    volumes:
+      - .:/app/tests/plugins/mysql2
+    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/mysql2/expected.data.yaml b/tests/plugins/mysql2/expected.data.yaml
new file mode 100644
index 0000000..221f4e9
--- /dev/null
+++ b/tests/plugins/mysql2/expected.data.yaml
@@ -0,0 +1,115 @@
+#
+# 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/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"
+          - 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"
+              - key: http.status.msg
+                value: OK
+            refs:
+              - parentEndpoint: ""
+                networkAddress: server:5000
+                refType: CrossProcess
+                parentSpanId: 1
+                parentTraceSegmentId: not null
+                parentServiceInstance: not null
+                parentService: client
+                traceId: not null
+  - 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"
+              - key: http.status.msg
+                value: OK
+          - 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: http://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/mysql2/init/init.sql b/tests/plugins/mysql2/init/init.sql
new file mode 100644
index 0000000..844112b
--- /dev/null
+++ b/tests/plugins/mysql2/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.
+
+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/mysql2/server.ts b/tests/plugins/mysql2/server.ts
new file mode 100644
index 0000000..2381ca8
--- /dev/null
+++ b/tests/plugins/mysql2/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 'mysql2';
+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/mysql2/test.ts b/tests/plugins/mysql2/test.ts
new file mode 100644
index 0000000..e2b14b5
--- /dev/null
+++ b/tests/plugins/mysql2/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('mysql', 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;
+    }
+  });
+});