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/24 03:22:01 UTC

[skywalking-nodejs] branch master updated: Add ioredis plugin (#53)

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 163a70d  Add ioredis plugin  (#53)
163a70d is described below

commit 163a70d4b8b863d063b7d96cacb5641730eb48b7
Author: keke <yo...@gmail.com>
AuthorDate: Mon May 24 11:21:55 2021 +0800

    Add ioredis plugin  (#53)
---
 README.md                                          |   2 +
 package-lock.json                                  |  95 +++++++++++++++
 package.json                                       |   2 +
 src/plugins/IORedisPlugin.ts                       |  62 ++++++++++
 src/trace/Component.ts                             |   3 +-
 .../plugins/ioredis/client.ts                      |  33 +++---
 tests/plugins/ioredis/docker-compose.yml           |  84 +++++++++++++
 tests/plugins/ioredis/expected.data.yaml           | 131 +++++++++++++++++++++
 .../plugins/ioredis/server.ts                      |  45 ++++---
 tests/plugins/ioredis/test.ts                      |  57 +++++++++
 10 files changed, 483 insertions(+), 31 deletions(-)

diff --git a/README.md b/README.md
index 7457ab6..a3395f3 100644
--- a/README.md
+++ b/README.md
@@ -79,11 +79,13 @@ Library | Plugin Name
 | [`Express`](https://expressjs.com) | `express` |
 | [`Axios`](https://github.com/axios/axios) | `axios` |
 | [`MySQL`](https://github.com/mysqljs/mysql) | `mysql` |
+| [`MySQL`](https://github.com/sidorares/node-mysql2) | `mysql2` |
 | [`PostgreSQL`](https://github.com/brianc/node-postgres) | `pg` |
 | [`pg-cursor`](https://github.com/brianc/node-postgres) | `pg-cursor` |
 | [`MongoDB`](https://github.com/mongodb/node-mongodb-native) | `mongodb` |
 | [`Mongoose`](https://github.com/Automattic/mongoose) | `mongoose` |
 | [`RabbitMQ`](https://github.com/squaremo/amqp.node) | `amqplib` |
+| [`Redis`](https://github.com/luin/ioredis) | `ioredis` |
 
 ### Compatible Libraries
 
diff --git a/package-lock.json b/package-lock.json
index 844dadd..add91e0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -953,6 +953,15 @@
         "@types/node": "*"
       }
     },
+    "@types/ioredis": {
+      "version": "4.26.4",
+      "resolved": "https://registry.npmjs.org/@types/ioredis/-/ioredis-4.26.4.tgz",
+      "integrity": "sha512-QFbjNq7EnOGw6d1gZZt2h26OFXjx7z+eqEnbCHSrDI1OOLEgOHMKdtIajJbuCr9uO+X9kQQRe7Lz6uxqxl5XKg==",
+      "dev": true,
+      "requires": {
+        "@types/node": "*"
+      }
+    },
     "@types/istanbul-lib-coverage": {
       "version": "2.0.3",
       "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz",
@@ -2138,6 +2147,12 @@
         "wrap-ansi": "^2.0.0"
       }
     },
+    "cluster-key-slot": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz",
+      "integrity": "sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==",
+      "dev": true
+    },
     "co": {
       "version": "4.6.0",
       "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
@@ -3846,6 +3861,41 @@
       "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
       "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY="
     },
+    "ioredis": {
+      "version": "4.27.2",
+      "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-4.27.2.tgz",
+      "integrity": "sha512-7OpYymIthonkC2Jne5uGWXswdhlua1S1rWGAERaotn0hGJWTSURvxdHA9G6wNbT/qKCloCja/FHsfKXW8lpTmg==",
+      "dev": true,
+      "requires": {
+        "cluster-key-slot": "^1.1.0",
+        "debug": "^4.3.1",
+        "denque": "^1.1.0",
+        "lodash.defaults": "^4.2.0",
+        "lodash.flatten": "^4.4.0",
+        "p-map": "^2.1.0",
+        "redis-commands": "1.7.0",
+        "redis-errors": "^1.2.0",
+        "redis-parser": "^3.0.0",
+        "standard-as-callback": "^2.1.0"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "4.3.1",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
+          "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
+          "dev": true,
+          "requires": {
+            "ms": "2.1.2"
+          }
+        },
+        "ms": {
+          "version": "2.1.2",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+          "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+          "dev": true
+        }
+      }
+    },
     "ipaddr.js": {
       "version": "1.9.1",
       "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
@@ -5234,6 +5284,18 @@
       "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz",
       "integrity": "sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y="
     },
+    "lodash.defaults": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
+      "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=",
+      "dev": true
+    },
+    "lodash.flatten": {
+      "version": "4.4.0",
+      "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
+      "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=",
+      "dev": true
+    },
     "lodash.template": {
       "version": "4.5.0",
       "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz",
@@ -6202,6 +6264,12 @@
         "p-limit": "^2.2.0"
       }
     },
+    "p-map": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz",
+      "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==",
+      "dev": true
+    },
     "p-try": {
       "version": "2.2.0",
       "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
@@ -6585,6 +6653,27 @@
         "util-deprecate": "~1.0.1"
       }
     },
+    "redis-commands": {
+      "version": "1.7.0",
+      "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz",
+      "integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==",
+      "dev": true
+    },
+    "redis-errors": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz",
+      "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=",
+      "dev": true
+    },
+    "redis-parser": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz",
+      "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=",
+      "dev": true,
+      "requires": {
+        "redis-errors": "^1.0.0"
+      }
+    },
     "regex-not": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
@@ -7392,6 +7481,12 @@
         }
       }
     },
+    "standard-as-callback": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz",
+      "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==",
+      "dev": true
+    },
     "static-extend": {
       "version": "0.1.2",
       "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
diff --git a/package.json b/package.json
index a05e96f..fa343d0 100644
--- a/package.json
+++ b/package.json
@@ -40,6 +40,7 @@
   "devDependencies": {
     "@types/express": "^4.17.9",
     "@types/google-protobuf": "^3.7.2",
+    "@types/ioredis": "^4.26.4",
     "@types/jest": "^26.0.15",
     "@types/node": "^14.0.11",
     "@types/semver": "^7.2.0",
@@ -49,6 +50,7 @@
     "express": "^4.17.1",
     "grpc-tools": "^1.10.0",
     "grpc_tools_node_protoc_ts": "^4.0.0",
+    "ioredis": "^4.27.2",
     "jest": "^26.6.3",
     "mongodb": "^3.6.4",
     "mongoose": "^5.12.2",
diff --git a/src/plugins/IORedisPlugin.ts b/src/plugins/IORedisPlugin.ts
new file mode 100644
index 0000000..adbd930
--- /dev/null
+++ b/src/plugins/IORedisPlugin.ts
@@ -0,0 +1,62 @@
+/*!
+ *
+ * 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 PluginInstaller from '../core/PluginInstaller';
+import SwPlugin, { wrapPromise } from '../core/SwPlugin';
+import { SpanLayer } from '../proto/language-agent/Tracing_pb';
+import Tag from '../Tag';
+import { Component } from '../trace/Component';
+import ContextManager from '../trace/context/ContextManager';
+
+class IORedisPlugin implements SwPlugin {
+	readonly module = 'ioredis';
+	readonly versions = '*';
+
+	install(installer: PluginInstaller): void {
+		const Redis = installer.require('ioredis');
+
+		this.interceptOperation(Redis, 'sendCommand');
+	}
+
+	interceptOperation(Cls: any, operation: string): void {
+		const _original = Cls.prototype[operation];
+
+		if (!_original)
+			return;
+
+		Cls.prototype[operation] = function (...args: any[]) {
+			const command = args[0];
+			const host = `${this.options.host}:${this.options.port}`;
+			const span = ContextManager.current.newExitSpan(`redis/${command?.name}`, Component.REDIS);
+
+			span.start();
+			span.component = Component.REDIS;
+			span.layer = SpanLayer.CACHE;
+			span.peer = host;
+			span.tag(Tag.dbType('Redis'));
+			span.tag(Tag.dbInstance(`${this.condition.select}`));
+
+			const ret = wrapPromise(span, _original.apply(this, args));
+			span.async();
+			return ret;
+		}
+	}
+}
+
+export default new IORedisPlugin();
\ No newline at end of file
diff --git a/src/trace/Component.ts b/src/trace/Component.ts
index f45bf0d..a4bd44f 100644
--- a/src/trace/Component.ts
+++ b/src/trace/Component.ts
@@ -21,6 +21,7 @@ export class Component {
   static readonly UNKNOWN = new Component(0);
   static readonly HTTP = new Component(2);
   static readonly MYSQL = new Component(5);
+  static readonly REDIS = new Component(7);
   static readonly MONGODB = new Component(9);
   static readonly POSTGRESQL = new Component(22);
   static readonly HTTP_SERVER = new Component(49);
@@ -31,5 +32,5 @@ export class Component {
   static readonly AXIOS = new Component(4005);
   static readonly MONGOOSE = new Component(4006);
 
-  constructor(public readonly id: number) {}
+  constructor(public readonly id: number) { }
 }
diff --git a/src/trace/Component.ts b/tests/plugins/ioredis/client.ts
similarity index 54%
copy from src/trace/Component.ts
copy to tests/plugins/ioredis/client.ts
index f45bf0d..2f33af1 100644
--- a/src/trace/Component.ts
+++ b/tests/plugins/ioredis/client.ts
@@ -17,19 +17,22 @@
  *
  */
 
-export class Component {
-  static readonly UNKNOWN = new Component(0);
-  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 RABBITMQ_PRODUCER = new Component(52);
-  static readonly RABBITMQ_CONSUMER = new Component(53);
-  static readonly AZURE_HTTPTRIGGER = new Component(111);
-  static readonly EXPRESS = new Component(4002);
-  static readonly AXIOS = new Component(4005);
-  static readonly MONGOOSE = new Component(4006);
+import * as http from 'http';
+import agent from '../../../src';
 
-  constructor(public readonly id: number) {}
-}
+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/ioredis/docker-compose.yml b/tests/plugins/ioredis/docker-compose.yml
new file mode 100644
index 0000000..f66fb04
--- /dev/null
+++ b/tests/plugins/ioredis/docker-compose.yml
@@ -0,0 +1,84 @@
+#
+# 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
+
+  redis:
+    container_name: redis
+    ports:
+      - 6379:6379
+    healthcheck:
+      test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/6379"]
+      interval: 5s
+      timeout: 60s
+      retries: 120
+    image: "redis:latest"
+    networks:
+      - traveling-light
+
+  server:
+    extends:
+      file: ../common/base-compose.yml
+      service: agent
+    ports:
+      - 5000:5000
+    environment:
+      REDIS_HOST: redis
+    volumes:
+      - .:/app/tests/plugins/ioredis
+    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/ioredis/server.ts"]
+    depends_on:
+      collector:
+        condition: service_healthy
+      redis:
+        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/ioredis/client.ts"]
+    depends_on:
+      server:
+        condition: service_healthy
+
+networks:
+  traveling-light:
diff --git a/tests/plugins/ioredis/expected.data.yaml b/tests/plugins/ioredis/expected.data.yaml
new file mode 100644
index 0000000..d527352
--- /dev/null
+++ b/tests/plugins/ioredis/expected.data.yaml
@@ -0,0 +1,131 @@
+#
+# 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: 2
+    segments:
+      - segmentId: not null
+        spans:
+          - operationName: redis/info
+            operationId: 0
+            parentSpanId: -1
+            spanId: 0
+            spanLayer: Cache
+            startTime: gt 0
+            endTime: gt 0
+            componentId: 7
+            spanType: Exit
+            peer: redis:6379
+            skipAnalysis: false
+            tags:
+              - { key: coldStart, value: "true" }
+              - { key: db.type, value: Redis }
+              - { key: db.instance, value: "0" }
+      - segmentId: not null
+        spans:
+          - operationName: redis/set
+            operationId: 0
+            parentSpanId: 0
+            spanId: 1
+            spanLayer: Cache
+            startTime: gt 0
+            endTime: gt 0
+            componentId: 7
+            spanType: Exit
+            peer: redis:6379
+            skipAnalysis: false
+            tags:
+              - { key: db.type, value: Redis }
+              - { key: db.instance, value: "0" }
+          - operationName: redis/get
+            operationId: 0
+            parentSpanId: 0
+            spanId: 2
+            spanLayer: Cache
+            startTime: gt 0
+            endTime: gt 0
+            componentId: 7
+            spanType: Exit
+            peer: redis:6379
+            skipAnalysis: false
+            tags:
+              - { key: db.type, value: Redis }
+              - { key: db.instance, value: "0" }
+          - operationName: /ioredis
+            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/ioredis" }
+              - { 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: /ioredis
+            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: coldStart, value: "true" }
+              - { key: http.url, value: "http://localhost:5001/ioredis" }
+              - { key: http.method, value: GET }
+              - { key: http.status.code, value: "200" }
+              - { key: http.status.msg, value: OK }
+          - operationName: /ioredis
+            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/ioredis" }
+              - { key: http.method, value: GET }
+              - { key: http.status.code, value: "200" }
+              - { key: http.status.msg, value: OK }
diff --git a/src/trace/Component.ts b/tests/plugins/ioredis/server.ts
similarity index 53%
copy from src/trace/Component.ts
copy to tests/plugins/ioredis/server.ts
index f45bf0d..cf56507 100644
--- a/src/trace/Component.ts
+++ b/tests/plugins/ioredis/server.ts
@@ -17,19 +17,34 @@
  *
  */
 
-export class Component {
-  static readonly UNKNOWN = new Component(0);
-  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 RABBITMQ_PRODUCER = new Component(52);
-  static readonly RABBITMQ_CONSUMER = new Component(53);
-  static readonly AZURE_HTTPTRIGGER = new Component(111);
-  static readonly EXPRESS = new Component(4002);
-  static readonly AXIOS = new Component(4005);
-  static readonly MONGOOSE = new Component(4006);
+import * as http from 'http';
+import Redis from 'ioredis';
+import agent from '../../../src';
+import assert from 'assert';
 
-  constructor(public readonly id: number) {}
-}
+agent.start({
+  serviceName: 'server',
+  maxBufferSize: 1000,
+});
+
+const client = new Redis({
+  host: process.env.REDIS_HOST || 'redis',
+});
+
+const server = http.createServer((req, res) => {
+  (async () => {
+    const cacheKey = 'now';
+    const now = '' + Date.now();
+
+    await client.set(cacheKey, now);
+    const _now = await client.get(cacheKey);
+    assert.strictEqual(now, _now);
+
+    res.end(_now);
+  })().catch((err: Error) => {
+    res.statusCode = 500;
+    res.end(err.message);
+  });
+})
+
+server.listen(5000, () => console.info('Listening on port 5000...'));
diff --git a/tests/plugins/ioredis/test.ts b/tests/plugins/ioredis/test.ts
new file mode 100644
index 0000000..8a8b423
--- /dev/null
+++ b/tests/plugins/ioredis/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('redis', Wait.forHealthCheck())
+      .up();
+  });
+
+  afterAll(async () => {
+    await compose.down();
+  });
+
+  it(__filename, async () => {
+    await waitForExpect(async () => expect((await axios.get('http://localhost:5001/ioredis')).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;
+    }
+  });
+});