Skip to content

Commit

Permalink
test(e2e): Add event proxy option to allow for event dumps (#13998)
Browse files Browse the repository at this point in the history
  • Loading branch information
lforst authored Oct 21, 2024
1 parent ab28544 commit 2648ef6
Show file tree
Hide file tree
Showing 7 changed files with 139 additions and 1 deletion.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ module.exports = {
'examples/**',
'test/manual/**',
'types/**',
'scripts/*.js',
],
reportUnusedDisableDirectives: true,
overrides: [
Expand Down
9 changes: 9 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1035,6 +1035,15 @@ jobs:
overwrite: true
retention-days: 7

- name: Upload E2E Test Event Dumps
uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-event-dumps-job_e2e_playwright_tests-${{ matrix.test-application }}
path: dev-packages/e2e-tests/test-applications/${{ matrix.test-application }}/event-dumps
overwrite: true
retention-days: 7

- name: Upload test results to Codecov
if: cancelled() == false
continue-on-error: true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,4 @@ next-env.d.ts
.vscode

test-results
event-dumps
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
/// <reference types="next/image-types/global" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import * as fs from 'fs';
import * as path from 'path';
import { startEventProxyServer } from '@sentry-internal/test-utils';

const packageJson = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'package.json')));

startEventProxyServer({
port: 3031,
proxyServerName: 'nextjs-15',
envelopeDumpPath: path.join(
process.cwd(),
`event-dumps/next-${packageJson.dependencies.next}-${process.env.TEST_ENV}.dump`,
),
});
10 changes: 10 additions & 0 deletions dev-packages/test-utils/src/event-proxy-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ interface EventProxyServerOptions {
port: number;
/** The name for the proxy server used for referencing it with listener functions */
proxyServerName: string;
/** A path to optionally output all Envelopes to. Can be used to compare event payloads before and after changes. */
envelopeDumpPath?: string;
}

interface SentryRequestCallbackData {
Expand Down Expand Up @@ -167,6 +169,10 @@ export async function startProxyServer(
* option to this server (like this `tunnel: http://localhost:${port option}/`).
*/
export async function startEventProxyServer(options: EventProxyServerOptions): Promise<void> {
if (options.envelopeDumpPath) {
await fs.promises.mkdir(path.dirname(path.resolve(options.envelopeDumpPath)), { recursive: true });
}

await startProxyServer(options, async (eventCallbackListeners, proxyRequest, proxyRequestBody, eventBuffer) => {
const data: SentryRequestCallbackData = {
envelope: parseEnvelope(proxyRequestBody),
Expand All @@ -183,6 +189,10 @@ export async function startEventProxyServer(options: EventProxyServerOptions): P
listener(dataString);
});

if (options.envelopeDumpPath) {
fs.appendFileSync(path.resolve(options.envelopeDumpPath), `${JSON.stringify(data.envelope)}\n`, 'utf-8');
}

return [
200,
'{}',
Expand Down
109 changes: 109 additions & 0 deletions scripts/normalize-e2e-test-dump-transaction-events.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/* eslint-disable no-console */

const fs = require('fs');
const path = require('path');

if (process.argv.length < 4) {
throw new Error('Please provide an input and output file path as an argument.');
}

const resolvedInputPath = path.resolve(process.argv[2]);
const resolvedOutputPath = path.resolve(process.argv[3]);

const fileContents = fs.readFileSync(resolvedInputPath, 'utf8');

const transactionNodes = [];

fileContents.split('\n').forEach(serializedEnvelope => {
let envelope;
try {
envelope = JSON.parse(serializedEnvelope);
} catch (e) {
return;
// noop
}

const envelopeItems = envelope[1];

envelopeItems.forEach(([envelopeItemHeader, transaction]) => {
if (envelopeItemHeader.type === 'transaction') {
const rootNode = {
runtime: transaction.contexts.runtime?.name,
op: transaction.contexts.trace.op,
name: transaction.transaction,
children: [],
};

const spanMap = new Map();
spanMap.set(transaction.contexts.trace.span_id, rootNode);

transaction.spans.forEach(span => {
const node = {
op: span.data['sentry.op'],
name: span.description,
parent_span_id: span.parent_span_id,
children: [],
};
spanMap.set(span.span_id, node);
});

transaction.spans.forEach(span => {
const node = spanMap.get(span.span_id);
if (node && node.parent_span_id) {
const parentNode = spanMap.get(node.parent_span_id);
parentNode.children.push(node);
}
});

transactionNodes.push(rootNode);
}
});
});

const output = transactionNodes
.sort((a, b) => {
const aSerialized = serializeNode(a);
const bSerialized = serializeNode(b);
if (aSerialized < bSerialized) {
return -1;
} else if (aSerialized > bSerialized) {
return 1;
} else {
return 0;
}
})
.map(node => buildDeterministicStringFromNode(node))
.join('\n\n-----------------------\n\n');

fs.writeFileSync(resolvedOutputPath, output, 'utf-8');

// ------- utility fns ----------

function buildDeterministicStringFromNode(node, depth = 0) {
const mainParts = [];
if (node.runtime) {
mainParts.push(`(${node.runtime})`);
}
mainParts.push(`${node.op ?? 'default'} -`);
mainParts.push(node.name);
const main = mainParts.join(' ');
const children = node.children
.sort((a, b) => {
const aSerialized = serializeNode(a);
const bSerialized = serializeNode(b);
if (aSerialized < bSerialized) {
return -1;
} else if (aSerialized > bSerialized) {
return 1;
} else {
return 0;
}
})
.map(child => '\n' + buildDeterministicStringFromNode(child, depth + 1))
.join('');
return `${main}${children}`.split('\n').join('\n ');
}

function serializeNode(node) {
return [node.op, node.name, node.runtime].join('---');
}

0 comments on commit 2648ef6

Please sign in to comment.