Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# build output
dist/
# generated types
.astro/

# dependencies
node_modules/

# logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*


# environment variables
.env
.env.production

# macOS-specific files
.DS_Store

# jetbrains setting folder
.idea/

test-results
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
## Development

When starting the dev server, use background mode:

```
astro dev --background
```

Manage the background server with `astro dev stop`, `astro dev status`, and `astro dev logs`.

## Documentation

Full documentation: https://docs.astro.build

Consult these guides before working on related tasks:

- [Adding pages, dynamic routes, or middleware](https://docs.astro.build/en/guides/routing/)
- [Working with Astro components](https://docs.astro.build/en/basics/astro-components/)
- [Using React, Vue, Svelte, or other framework components](https://docs.astro.build/en/guides/framework-components/)
- [Adding or managing content](https://docs.astro.build/en/guides/content-collections/)
- [Adding styles or using Tailwind](https://docs.astro.build/en/guides/styling/)
- [Supporting multiple languages](https://docs.astro.build/en/guides/internationalization/)
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import node from '@astrojs/node';
import sentry from '@sentry/astro';
import { sentryOrchestrionPlugin } from '@sentry/server-utils/orchestrion/vite';
// @ts-check
import { defineConfig } from 'astro/config';

// https://astro.build/config
export default defineConfig({
integrations: [
sentry({
debug: true,
sourceMapsUploadOptions: {
enabled: false,
},
}),
],
output: 'server',
adapter: node({
mode: 'standalone',
}),
vite: {
// Run the orchestrion code transform on the Vite SSR bundle so instrumented
// DB drivers get `diagnostics_channel` publishers injected at build time.
plugins: [sentryOrchestrionPlugin()],

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: I think it makes sense to keep using the internal bundler bits in e2e tests at this stage, but at some point we need to do a git grep -i orchestrion dev-packages/e2e-tests/test-applications and make sure that our public-facing bundler API can handle all the use cases, and make the tests not "cheat" by using the internal machinery. Similar discussions I saw around using orchestrion+vite for cloudflare. (cc: @timfish, @JPeer264)

},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
services:
db:
image: mysql:8.0
restart: always
container_name: e2e-tests-astro-7-orchestrion-mysql
# The `mysql` 2.x driver doesn't speak MySQL 8's default
# `caching_sha2_password` auth, so force the legacy plugin.
command: ['--default-authentication-plugin=mysql_native_password']
ports:
- '3306:3306'
environment:
MYSQL_ROOT_PASSWORD: docker
healthcheck:
test: ['CMD-SHELL', 'mysqladmin ping -h 127.0.0.1 -uroot -pdocker']
interval: 2s
timeout: 3s
retries: 30
start_period: 10s

redis:
image: redis:7
restart: always
container_name: e2e-tests-astro-7-orchestrion-redis
ports:
- '6379:6379'
healthcheck:
test: ['CMD', 'redis-cli', 'ping']
interval: 2s
timeout: 3s
retries: 30
start_period: 5s
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { execSync } from 'child_process';
import { dirname } from 'path';
import { fileURLToPath } from 'url';

const __dirname = dirname(fileURLToPath(import.meta.url));

export default async function globalSetup() {
// Start MySQL + Redis via Docker Compose. `--wait` blocks until the
// healthchecks in docker-compose.yml pass, so the app can connect immediately.
execSync('docker compose up -d --wait', {
cwd: __dirname,
stdio: 'inherit',
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { execSync } from 'child_process';
import { dirname } from 'path';
import { fileURLToPath } from 'url';

const __dirname = dirname(fileURLToPath(import.meta.url));

export default async function globalTeardown() {
execSync('docker compose down --volumes', {
cwd: __dirname,
stdio: 'inherit',
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "astro-7-orchestrion",
"type": "module",
"version": "0.0.1",
"scripts": {
"dev": "astro dev",
"build": "astro build",
"preview": "astro preview",
"astro": "astro",
"start": "node ./dist/server/entry.mjs",
"test:build": "pnpm install && pnpm build",
"test:assert": "TEST_ENV=production playwright test"
},
"//": "Need to use ioredis 5.10.1 because that's the last version before they support tracing channels",
"dependencies": {
"@astrojs/node": "^11.0.2",
"@playwright/test": "~1.56.0",
"@sentry-internal/test-utils": "link:../../../test-utils",
"@sentry/astro": "file:../../packed/sentry-astro-packed.tgz",
"@sentry/node": "file:../../packed/sentry-node-packed.tgz",
"@sentry/server-utils": "file:../../packed/sentry-server-utils-packed.tgz",
"astro": "^7.0.6",
"ioredis": "5.10.1",
"mysql": "^2.18.1"
},
"devDependencies": {
"@apm-js-collab/code-transformer-bundler-plugins": "^0.5.0"
},
"volta": {
"node": "22.22.0",
"extends": "../../package.json"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { getPlaywrightConfig } from '@sentry-internal/test-utils';

const config = getPlaywrightConfig({
startCommand: 'pnpm start',
});

export default {
...config,
globalSetup: './global-setup.mjs',
globalTeardown: './global-teardown.mjs',
};
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import * as Sentry from '@sentry/astro';

Sentry.init({
dsn: import.meta.env.PUBLIC_E2E_TEST_DSN,
environment: 'qa',
tracesSampleRate: 1.0,
tunnel: 'http://localhost:3031/', // proxy server
debug: true,
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import * as Sentry from '@sentry/astro';
import { experimentalUseDiagnosticsChannelInjection } from '@sentry/node';

// Registers the diagnostics-channel subscribers that turn the build-time
// injected channel events (from the orchestrion Vite plugin) into Sentry spans.
// Must run before Sentry.init()
experimentalUseDiagnosticsChannelInjection();

Sentry.init({
dsn: import.meta.env.PUBLIC_E2E_TEST_DSN,
environment: 'qa',
tracesSampleRate: 1.0,
tunnel: 'http://localhost:3031/', // proxy server
debug: true,
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
import Redis from 'ioredis';

export const prerender = false;

const redis = new Redis({
// Don't keep retrying forever if Redis goes away (e.g. on test teardown)
maxRetriesPerRequest: 1,
retryStrategy: () => null,
});

let value: string | null = null;

try {
await redis.set('test-key', 'test-value');
value = await redis.get('test-key');
} finally {
redis.disconnect();
}
---

<html lang="en">
<head>
<meta charset="utf-8" />
<title>db-ioredis</title>
</head>
<body>
<h1>ioredis queries executed</h1>
<p>{value}</p>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
import mysql from 'mysql';

export const prerender = false;

const connection = mysql.createConnection({
user: 'root',
password: 'docker',
});

await new Promise<void>(resolve => {
connection.query('SELECT 1 + 1 AS solution', () => {
connection.query('SELECT NOW()', ['1', '2'], () => {
resolve();
});
});
});
---

<html lang="en">
<head>
<meta charset="utf-8" />
<title>db-mysql</title>
</head>
<body>
<h1>mysql queries executed</h1>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---

---

<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width" />
<meta name="generator" content={Astro.generator} />
<title>Astro</title>
</head>
<body>
<h1>Astro Orchestrion E2E Test App</h1>
<ul>
<li><a href="/db-mysql">db-mysql</a></li>
<li><a href="/db-ioredis">db-ioredis</a></li>
</ul>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { startEventProxyServer } from '@sentry-internal/test-utils';

startEventProxyServer({
port: 3031,
proxyServerName: 'astro-7-orchestrion',
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { expect, test } from '@playwright/test';
import { waitForTransaction } from '@sentry-internal/test-utils';

test('Instruments ioredis automatically', async ({ baseURL }) => {
const transactionEventPromise = waitForTransaction('astro-7-orchestrion', transactionEvent => {
return transactionEvent.contexts?.trace?.op === 'http.server' && transactionEvent.transaction === 'GET /db-ioredis';
});

await fetch(`${baseURL}/db-ioredis`);

const transactionEvent = await transactionEventPromise;

expect(transactionEvent.contexts?.trace?.op).toEqual('http.server');
expect(transactionEvent.transaction).toEqual('GET /db-ioredis');

const spans = transactionEvent.spans || [];

expect(spans).toContainEqual(
expect.objectContaining({
op: 'db',
origin: 'auto.db.orchestrion.redis',
description: 'set test-key [1 other arguments]',
status: 'ok',
data: expect.objectContaining({
'db.system': 'redis',
'db.statement': 'set test-key [1 other arguments]',
}),
}),
);
expect(spans).toContainEqual(
expect.objectContaining({
op: 'db',
origin: 'auto.db.orchestrion.redis',
description: 'get test-key',
status: 'ok',
data: expect.objectContaining({
'db.system': 'redis',
'db.statement': 'get test-key',
}),
}),
);
});

test('Instruments mysql automatically', async ({ baseURL }) => {
const transactionEventPromise = waitForTransaction('astro-7-orchestrion', transactionEvent => {
return transactionEvent.contexts?.trace?.op === 'http.server' && transactionEvent.transaction === 'GET /db-mysql';
});

await fetch(`${baseURL}/db-mysql`);

const transactionEvent = await transactionEventPromise;

const spans = transactionEvent.spans || [];

expect(spans).toContainEqual(
expect.objectContaining({
op: 'db',
origin: 'auto.db.orchestrion.mysql',
description: 'SELECT 1 + 1 AS solution',
status: 'ok',
data: expect.objectContaining({
'db.system': 'mysql',
'db.statement': 'SELECT 1 + 1 AS solution',
'db.user': 'root',
'db.connection_string': expect.any(String),
'net.peer.name': expect.any(String),
'net.peer.port': 3306,
}),
}),
);
expect(spans).toContainEqual(
expect.objectContaining({
op: 'db',
origin: 'auto.db.orchestrion.mysql',
description: 'SELECT NOW()',
status: 'ok',
data: expect.objectContaining({
'db.system': 'mysql',
'db.statement': 'SELECT NOW()',
'db.user': 'root',
'db.connection_string': expect.any(String),
'net.peer.name': expect.any(String),
'net.peer.port': 3306,
}),
}),
);
});
Loading
Loading