Skip to content

Commit 2aef158

Browse files
david-lunatrentmpichlermarc
authored
chore: Add docker compose and test services scripts (#2886)
Co-authored-by: Trent Mick <[email protected]> Co-authored-by: Marc Pichler <[email protected]>
1 parent 50af7ec commit 2aef158

File tree

11 files changed

+235
-63
lines changed

11 files changed

+235
-63
lines changed

.github/workflows/test-all-versions.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,6 @@ jobs:
163163
- uses: actions/setup-node@v4
164164
with:
165165
node-version: ${{ matrix.node }}
166-
- name: Set MySQL variables
167-
run: mysql --user=root --password=${MYSQL_ROOT_PASSWORD} --host=${MYSQL_HOST} --port=${MYSQL_PORT} -e "SET GLOBAL log_output='TABLE'; SET GLOBAL general_log = 1;" mysql
168166
- name: Install
169167
run: npm ci
170168
- name: Download Build Artifacts

.github/workflows/unit-test.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,6 @@ jobs:
176176
- uses: actions/setup-node@v4
177177
with:
178178
node-version: ${{ matrix.node }}
179-
- name: Set MySQL variables
180-
run: mysql --user=root --password=${MYSQL_ROOT_PASSWORD} --host=${MYSQL_HOST} --port=${MYSQL_PORT} -e "SET GLOBAL log_output='TABLE'; SET GLOBAL general_log = 1;" mysql
181179
- name: Install
182180
run: npm ci
183181
- name: Download Build Artifacts

CONTRIBUTING.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,34 @@ The conventional commit type (in PR title) is very important to automatically bu
135135

136136
There is no need to update the CHANGELOG in a PR because it will be updated as part of the release process (see [RELEASING.md](RELEASING.md) for more details).
137137

138+
### Testing
139+
140+
Most unit tests case be run via:
141+
142+
```sh
143+
npm test
144+
```
145+
146+
However, some instrumentations require test-services to be running (e.g. the `instrumentation-mongodb` package requires a MongoDB server). Use the `test-services`-related npm scripts to start all required services in Docker and then run the tests with the appropriate configuration to use those services:
147+
148+
```sh
149+
npm run test-services:start # starts services in Docker
150+
npm run test:with-services-config # runs 'npm test' with envvars from test/test-services.env
151+
npm run test-services:stop # stops services in Docker
152+
```
153+
154+
If you only want to test a single package (e.g. the `instrumentation-mongodb`) you can `cd` into it and run the tests after you started the services.
155+
156+
```sh
157+
npm run test-services:start # starts services in Docker
158+
cd plugins/node/opentelemetry-instrumentation-mongodb # get into the instrumenation folder
159+
RUN_MONGODB_TESTS=1 npm test # run the test with the proper config (check each package)
160+
cd ../../.. # go back to root folder
161+
npm run test-services:stop # stops services in Docker
162+
```
163+
164+
NOTE: scripts for each package will be added to avoid extra consumption of resources and improve the development experience.
165+
138166
### Benchmarks
139167

140168
When two or more approaches must be compared, please write a benchmark in the benchmark/index.js module so that we can keep track of the most efficient algorithm.

package-lock.json

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
"test:browser": "nx run-many -t test:browser",
2020
"test:ci:changed": "nx affected -t test --base=origin/main --head=HEAD",
2121
"test-all-versions": "nx run-many -t test-all-versions",
22+
"test-services:start": "docker compose -f ./test/docker-compose.yaml up -d --wait",
23+
"test-services:stop": "docker compose -f ./test/docker-compose.yaml down",
24+
"test:with-services-env": "cross-env NODE_OPTIONS='-r dotenv/config' DOTENV_CONFIG_PATH=./test/test-services.env npm test",
2225
"changelog": "lerna-changelog",
2326
"lint": "nx run-many -t lint && npm run lint:deps && npm run lint:readme && npm run lint:markdown && npm run lint:semconv-deps",
2427
"lint:fix": "nx run-many -t lint:fix && npm run lint:markdown:fix",
@@ -43,6 +46,7 @@
4346
"devDependencies": {
4447
"@typescript-eslint/eslint-plugin": "5.62.0",
4548
"@typescript-eslint/parser": "5.62.0",
49+
"cross-env": "^7.0.3",
4650
"eslint": "8.7.0",
4751
"eslint-config-airbnb-base": "15.0.0",
4852
"eslint-config-prettier": "8.8.0",

plugins/node/opentelemetry-instrumentation-mysql/test/index.metrics.test.ts

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import {
2424
} from '@opentelemetry/sdk-metrics';
2525
import * as assert from 'assert';
2626
import { MySQLInstrumentation } from '../src';
27-
import * as testUtils from '@opentelemetry/contrib-test-utils';
2827
import { registerInstrumentationTesting } from '@opentelemetry/contrib-test-utils';
2928

3029
const instrumentation = registerInstrumentationTesting(
@@ -62,9 +61,9 @@ import * as mysqlTypes from 'mysql';
6261
describe('[email protected]', () => {
6362
let otelTestingMeterProvider;
6463
let inMemoryMetricsExporter: InMemoryMetricExporter;
65-
const testMysql = process.env.RUN_MYSQL_TESTS; // For CI: assumes local mysql db is already available
66-
const testMysqlLocally = process.env.RUN_MYSQL_TESTS_LOCAL; // For local: spins up local mysql db via docker
67-
const shouldTest = testMysql || testMysqlLocally; // Skips these tests if false (default)
64+
// assumes local mysql db is already available in CI or
65+
// using `npm run test-services:start` script at the root folder
66+
const shouldTest = process.env.RUN_MYSQL_TESTS;
6867

6968
function initMeterProvider() {
7069
inMemoryMetricsExporter = new InMemoryMetricExporter(
@@ -90,22 +89,7 @@ describe('[email protected]', () => {
9089
console.log('Skipping test-mysql for metrics.');
9190
this.skip();
9291
}
93-
94-
if (testMysqlLocally) {
95-
testUtils.startDocker('mysql');
96-
// wait 15 seconds for docker container to start
97-
this.timeout(20000);
98-
setTimeout(done, 15000);
99-
} else {
100-
done();
101-
}
102-
});
103-
104-
after(function () {
105-
if (testMysqlLocally) {
106-
this.timeout(5000);
107-
testUtils.cleanUpDocker('mysql');
108-
}
92+
done();
10993
});
11094

11195
describe('#Pool - metrics', () => {

plugins/node/opentelemetry-instrumentation-mysql/test/index.test.ts

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import {
2525
SEMATTRS_NET_PEER_NAME,
2626
SEMATTRS_NET_PEER_PORT,
2727
} from '@opentelemetry/semantic-conventions';
28-
import * as testUtils from '@opentelemetry/contrib-test-utils';
2928
import {
3029
BasicTracerProvider,
3130
InMemorySpanExporter,
@@ -54,9 +53,9 @@ describe('[email protected]', () => {
5453
let connection: mysqlTypes.Connection;
5554
let pool: mysqlTypes.Pool;
5655
let poolCluster: mysqlTypes.PoolCluster;
57-
const testMysql = process.env.RUN_MYSQL_TESTS; // For CI: assumes local mysql db is already available
58-
const testMysqlLocally = process.env.RUN_MYSQL_TESTS_LOCAL; // For local: spins up local mysql db via docker
59-
const shouldTest = testMysql || testMysqlLocally; // Skips these tests if false (default)
56+
// assumes local mysql db is already available in CI or
57+
// using `npm run test-services:start` script at the root folder
58+
const shouldTest = process.env.RUN_MYSQL_TESTS;
6059
const memoryExporter = new InMemorySpanExporter();
6160
const provider = new BasicTracerProvider({
6261
spanProcessors: [new SimpleSpanProcessor(memoryExporter)],
@@ -68,25 +67,11 @@ describe('[email protected]', () => {
6867
// https://github.com/mochajs/mocha/issues/2683#issuecomment-375629901
6968
this.test!.parent!.pending = true;
7069
this.skip();
71-
}
72-
73-
if (testMysqlLocally) {
74-
testUtils.startDocker('mysql');
75-
// wait 15 seconds for docker container to start
76-
this.timeout(20000);
77-
setTimeout(done, 15000);
7870
} else {
7971
done();
8072
}
8173
});
8274

83-
after(function () {
84-
if (testMysqlLocally) {
85-
this.timeout(5000);
86-
testUtils.cleanUpDocker('mysql');
87-
}
88-
});
89-
9075
beforeEach(() => {
9176
instrumentation.disable();
9277
contextManager = new AsyncLocalStorageContextManager().enable();

plugins/node/opentelemetry-instrumentation-mysql2/test/mysql.test.ts

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -64,28 +64,38 @@ interface Result extends RowDataPacket {
6464
solution: number;
6565
}
6666

67-
describe('mysql2', () => {
68-
const testMysql = process.env.RUN_MYSQL_TESTS; // For CI: assumes local mysql db is already available
69-
const testMysqlLocally = process.env.RUN_MYSQL_TESTS_LOCAL; // For local: spins up local mysql db via docker
70-
const shouldTest = testMysql || testMysqlLocally; // Skips these tests if false (default)
71-
72-
before(function (done) {
73-
if (testMysqlLocally) {
74-
testUtils.startDocker('mysql');
75-
// wait 15 seconds for docker container to start
76-
this.timeout(20000);
77-
setTimeout(done, 15000);
78-
} else {
79-
done();
80-
}
67+
// Helper function to setup the database
68+
const execPromise = (conn: Connection, command: string) => {
69+
return new Promise<void>((res, rej) => {
70+
conn.execute(command, err => {
71+
if (err) rej(err);
72+
else res();
73+
});
8174
});
75+
};
8276

83-
after(function (done) {
84-
if (testMysqlLocally) {
85-
this.timeout(5000);
86-
testUtils.cleanUpDocker('mysql');
77+
describe('mysql2', () => {
78+
// assumes local mysql db is already available in CI or
79+
// using `npm run test-services:start` script at the root folder
80+
const shouldTest = process.env.RUN_MYSQL_TESTS;
81+
82+
before(async function () {
83+
const connection = createConnection({
84+
port,
85+
user: 'root',
86+
host,
87+
password: rootPassword,
88+
database,
89+
});
90+
try {
91+
await execPromise(connection, "SET GLOBAL log_output='TABLE'");
92+
await execPromise(connection, 'SET GLOBAL general_log = 1');
93+
} catch (execErr) {
94+
console.error('MySQL seup error: ', execErr);
95+
this.skip();
96+
} finally {
97+
connection.end();
8798
}
88-
done();
8999
});
90100

91101
describe('callback API', () => {
@@ -358,7 +368,7 @@ describe('mysql2', () => {
358368
it('should not add comment by default', done => {
359369
const span = provider.getTracer('default').startSpan('test span');
360370
context.with(trace.setSpan(context.active(), span), () => {
361-
connection.query('SELECT 1+1 as solution', () => {
371+
connection.query('SELECT 1+1 as solution', (e, r) => {
362372
const spans = memoryExporter.getFinishedSpans();
363373
assert.strictEqual(spans.length, 1);
364374
getLastQueries(1).then(([query]) => {

plugins/node/opentelemetry-instrumentation-pg/test/pg.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ const memoryExporter = new InMemorySpanExporter();
6464
const CONFIG = {
6565
user: process.env.POSTGRES_USER || 'postgres',
6666
password: process.env.POSTGRES_PASSWORD || 'postgres',
67-
database: process.env.POSTGRES_DB || 'postgres',
67+
database: process.env.POSTGRES_DB || 'otel_pg_database',
6868
host: process.env.POSTGRES_HOST || 'localhost',
6969
port: process.env.POSTGRES_PORT
7070
? parseInt(process.env.POSTGRES_PORT, 10)

test/docker-compose.yaml

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
# A `docker compose` config file to run tests services for testing
2+
# `@opentelemetry/instrumentation-*` locally.
3+
#
4+
# Note: This isn't used in CI. CI uses GitHub Actions' `services: ...` for
5+
# defining test services.
6+
#
7+
# Usage:
8+
# npm run test-services:start [services...]
9+
# npm run test-services:stop [services...]
10+
11+
name: opentelemetry-js-contrib-test-services
12+
13+
services:
14+
cassandra:
15+
image: cassandra:3
16+
environment:
17+
MAX_HEAP_SIZE: "1G"
18+
HEAP_NEWSIZE: 400m
19+
ports:
20+
- "9042:9042"
21+
healthcheck:
22+
test: ["CMD-SHELL", "[ $$(nodetool statusgossip) = running ]"]
23+
interval: 1s
24+
timeout: 10s
25+
retries: 30
26+
27+
memcached:
28+
image: memcached:1.6.37-alpine
29+
ports:
30+
- 11211:11211
31+
32+
mongodb:
33+
image: mongo:7
34+
ports:
35+
- "27017:27017"
36+
healthcheck:
37+
test: ["CMD", "mongosh", "--eval", "db.runCommand('ping').ok", "--quiet"]
38+
interval: 1s
39+
timeout: 10s
40+
retries: 30
41+
42+
mssql:
43+
# Tags listed at https://hub.docker.com/r/microsoft/mssql-server
44+
# Docs: https://learn.microsoft.com/en-us/sql/linux/quickstart-install-connect-docker
45+
image: mcr.microsoft.com/mssql/server:2022-CU14-ubuntu-22.04
46+
platform: linux/amd64
47+
environment:
48+
- ACCEPT_EULA=Y
49+
- MSSQL_SA_PASSWORD=mssql_passw0rd
50+
ports:
51+
- "1433:1433"
52+
healthcheck:
53+
test: ["CMD", "/opt/mssql-tools18/bin/sqlcmd", "-C", "-S", "mssql", "-U", "sa", "-P", "mssql_passw0rd", "-Q", "select 1"]
54+
interval: 1s
55+
timeout: 10s
56+
retries: 30
57+
58+
mysql:
59+
image: mysql:5.7
60+
# No ARM64 image layer. See https://stackoverflow.com/a/65592942
61+
platform: linux/x86_64
62+
environment:
63+
MYSQL_USER: "otel"
64+
MYSQL_PASSWORD: "secret"
65+
MYSQL_DATABASE: "otel_mysql_database"
66+
MYSQL_ROOT_PASSWORD: "rootpw"
67+
ports:
68+
- "33306:3306"
69+
healthcheck:
70+
test: ["CMD", "mysqladmin" ,"ping"]
71+
interval: 1s
72+
timeout: 10s
73+
retries: 30
74+
75+
oracledb:
76+
image: gvenzl/oracle-free:slim
77+
environment:
78+
APP_USER: "otel"
79+
APP_USER_PASSWORD: "secret"
80+
ORACLE_PASSWORD: "oracle"
81+
ports:
82+
- 1521:1521
83+
healthcheck:
84+
test: ["CMD", "sqlplus" ,"system/oracle@//localhost/FREEPDB1"]
85+
interval: 10s
86+
timeout: 5s
87+
retries: 30
88+
89+
postgres:
90+
# https://github.com/docker-library/docs/blob/master/postgres/README.md#how-to-extend-this-image
91+
image: postgres:16-alpine
92+
ports:
93+
- "54320:5432"
94+
environment:
95+
POSTGRES_HOST_AUTH_METHOD: "trust"
96+
POSTGRES_USER: "postgres"
97+
POSTGRES_DB: "otel_pg_database"
98+
POSTGRES_PASSWORD: "postgres"
99+
healthcheck:
100+
test: ["CMD", "pg_isready"]
101+
interval: 1s
102+
timeout: 10s
103+
retries: 30
104+
105+
rabbitmq:
106+
image: rabbitmq:3
107+
environment:
108+
RABBITMQ_DEFAULT_USER: "username"
109+
RABBITMQ_DEFAULT_PASS: "password"
110+
ports:
111+
- "22221:5672"
112+
113+
redis:
114+
image: redis:7
115+
ports:
116+
- "6379:6379"
117+
healthcheck:
118+
test: ["CMD", "redis-cli", "ping"]
119+
interval: 1s
120+
timeout: 10s
121+
retries: 30

0 commit comments

Comments
 (0)