Skip to content

Commit 16dd435

Browse files
chargomeclaude
andcommitted
test(solidstart): Add DB e2e coverage for orchestrion
Extend the solidstart e2e app with mysql/ioredis routes and a Docker-backed test asserting the `auto.db.orchestrion.*` spans, proving the auto-wiring. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent ab1d207 commit 16dd435

8 files changed

Lines changed: 189 additions & 2 deletions

File tree

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
services:
2+
db:
3+
image: mysql:8.0
4+
restart: always
5+
container_name: e2e-tests-solidstart-mysql
6+
# The `mysql` 2.x driver doesn't speak MySQL 8's default
7+
# `caching_sha2_password` auth, so force the legacy plugin.
8+
command: ['--default-authentication-plugin=mysql_native_password']
9+
ports:
10+
- '3306:3306'
11+
environment:
12+
MYSQL_ROOT_PASSWORD: docker
13+
healthcheck:
14+
test: ['CMD-SHELL', 'mysqladmin ping -h 127.0.0.1 -uroot -pdocker']
15+
interval: 2s
16+
timeout: 3s
17+
retries: 30
18+
start_period: 10s
19+
20+
redis:
21+
image: redis:7
22+
restart: always
23+
container_name: e2e-tests-solidstart-redis
24+
ports:
25+
- '6379:6379'
26+
healthcheck:
27+
test: ['CMD', 'redis-cli', 'ping']
28+
interval: 2s
29+
timeout: 3s
30+
retries: 30
31+
start_period: 5s
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { execSync } from 'child_process';
2+
import { dirname } from 'path';
3+
import { fileURLToPath } from 'url';
4+
5+
const __dirname = dirname(fileURLToPath(import.meta.url));
6+
7+
export default async function globalSetup() {
8+
// Start MySQL + Redis via Docker Compose. `--wait` blocks until the
9+
// healthchecks in docker-compose.yml pass, so the app can connect immediately.
10+
execSync('docker compose up -d --wait', {
11+
cwd: __dirname,
12+
stdio: 'inherit',
13+
});
14+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { execSync } from 'child_process';
2+
import { dirname } from 'path';
3+
import { fileURLToPath } from 'url';
4+
5+
const __dirname = dirname(fileURLToPath(import.meta.url));
6+
7+
export default async function globalTeardown() {
8+
execSync('docker compose down --volumes', {
9+
cwd: __dirname,
10+
stdio: 'inherit',
11+
});
12+
}

dev-packages/e2e-tests/test-applications/solidstart/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212
},
1313
"type": "module",
1414
"dependencies": {
15-
"@sentry/solidstart": "file:../../packed/sentry-solidstart-packed.tgz"
15+
"@sentry/solidstart": "file:../../packed/sentry-solidstart-packed.tgz",
16+
"ioredis": "5.10.1",
17+
"mysql": "^2.18.1"
1618
},
1719
"devDependencies": {
1820
"@playwright/test": "~1.56.0",

dev-packages/e2e-tests/test-applications/solidstart/playwright.config.mjs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,8 @@ const config = getPlaywrightConfig({
55
port: 3030,
66
});
77

8-
export default config;
8+
export default {
9+
...config,
10+
globalSetup: './global-setup.mjs',
11+
globalTeardown: './global-teardown.mjs',
12+
};
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { json } from '@solidjs/router';
2+
import Redis from 'ioredis';
3+
4+
export async function GET() {
5+
const redis = new Redis({
6+
// Don't keep retrying forever if Redis goes away (e.g. on test teardown)
7+
maxRetriesPerRequest: 1,
8+
retryStrategy: () => null,
9+
});
10+
11+
try {
12+
await redis.set('test-key', 'test-value');
13+
const value = await redis.get('test-key');
14+
return json({ value });
15+
} finally {
16+
redis.disconnect();
17+
}
18+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { json } from '@solidjs/router';
2+
import mysql from 'mysql';
3+
4+
export async function GET() {
5+
const connection = mysql.createConnection({ user: 'root', password: 'docker' });
6+
try {
7+
await new Promise<void>((resolve, reject) => {
8+
connection.query('SELECT 1 + 1 AS solution', err1 => {
9+
if (err1) return reject(err1);
10+
connection.query('SELECT NOW()', ['1', '2'], err2 => {
11+
if (err2) return reject(err2);
12+
resolve();
13+
});
14+
});
15+
});
16+
return json({ status: 'ok' });
17+
} finally {
18+
connection.end(() => {});
19+
}
20+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import { expect, test } from '@playwright/test';
2+
import { waitForTransaction } from '@sentry-internal/test-utils';
3+
4+
test('Instruments ioredis automatically via build-time orchestrion', async ({ baseURL }) => {
5+
const transactionEventPromise = waitForTransaction('solidstart', transactionEvent => {
6+
return (
7+
transactionEvent.contexts?.trace?.op === 'http.server' && !!transactionEvent.transaction?.includes('db-ioredis')
8+
);
9+
});
10+
11+
await fetch(`${baseURL}/api/db-ioredis`);
12+
13+
const transactionEvent = await transactionEventPromise;
14+
const spans = transactionEvent.spans || [];
15+
16+
expect(spans).toContainEqual(
17+
expect.objectContaining({
18+
op: 'db',
19+
origin: 'auto.db.orchestrion.redis',
20+
description: 'set test-key [1 other arguments]',
21+
status: 'ok',
22+
data: expect.objectContaining({
23+
'db.system': 'redis',
24+
'db.statement': 'set test-key [1 other arguments]',
25+
}),
26+
}),
27+
);
28+
expect(spans).toContainEqual(
29+
expect.objectContaining({
30+
op: 'db',
31+
origin: 'auto.db.orchestrion.redis',
32+
description: 'get test-key',
33+
status: 'ok',
34+
data: expect.objectContaining({
35+
'db.system': 'redis',
36+
'db.statement': 'get test-key',
37+
}),
38+
}),
39+
);
40+
});
41+
42+
test('Instruments mysql automatically via build-time orchestrion', async ({ baseURL }) => {
43+
const transactionEventPromise = waitForTransaction('solidstart', transactionEvent => {
44+
return (
45+
transactionEvent.contexts?.trace?.op === 'http.server' && !!transactionEvent.transaction?.includes('db-mysql')
46+
);
47+
});
48+
49+
await fetch(`${baseURL}/api/db-mysql`);
50+
51+
const transactionEvent = await transactionEventPromise;
52+
const spans = transactionEvent.spans || [];
53+
54+
expect(spans).toContainEqual(
55+
expect.objectContaining({
56+
op: 'db',
57+
origin: 'auto.db.orchestrion.mysql',
58+
description: 'SELECT 1 + 1 AS solution',
59+
status: 'ok',
60+
data: expect.objectContaining({
61+
'db.system': 'mysql',
62+
'db.statement': 'SELECT 1 + 1 AS solution',
63+
'db.user': 'root',
64+
'db.connection_string': expect.any(String),
65+
'net.peer.name': expect.any(String),
66+
'net.peer.port': 3306,
67+
}),
68+
}),
69+
);
70+
expect(spans).toContainEqual(
71+
expect.objectContaining({
72+
op: 'db',
73+
origin: 'auto.db.orchestrion.mysql',
74+
description: 'SELECT NOW()',
75+
status: 'ok',
76+
data: expect.objectContaining({
77+
'db.system': 'mysql',
78+
'db.statement': 'SELECT NOW()',
79+
'db.user': 'root',
80+
'db.connection_string': expect.any(String),
81+
'net.peer.name': expect.any(String),
82+
'net.peer.port': 3306,
83+
}),
84+
}),
85+
);
86+
});

0 commit comments

Comments
 (0)