Skip to content
Merged
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
Expand Up @@ -10,6 +10,7 @@ interface Env {
interface WorkflowParams {
failCount: number;
captureManual?: boolean;
captureManualTwice?: boolean;
}
class StepContextTestWorkflowBase extends WorkflowEntrypoint<Env, WorkflowParams> {
async run(event: WorkflowEvent<WorkflowParams>, step: WorkflowStep): Promise<void> {
Expand All @@ -28,6 +29,13 @@ class StepContextTestWorkflowBase extends WorkflowEntrypoint<Env, WorkflowParams
Sentry.captureException(new Error(`Manual capture on attempt ${ctx.attempt}`));
}

// Both errors originate from the same line so they share a stack trace, which is what the
// Dedupe integration keys on
if (event.payload.captureManualTwice) {
Sentry.captureException(new Error('Manual capture'));
Sentry.captureException(new Error('Manual capture'));
}

if (remainingFailures > 0) {
remainingFailures--;
throw new Error('Intentional failure for retry test');
Expand Down Expand Up @@ -59,10 +67,11 @@ export default Sentry.withSentry(
if (url.pathname === '/trigger-workflow') {
const failCount = parseInt(url.searchParams.get('failCount') || '0', 10);
const captureManual = url.searchParams.get('captureManual') === 'true';
const captureManualTwice = url.searchParams.get('captureManualTwice') === 'true';

try {
const instance = await env.STEP_CONTEXT_WORKFLOW.create({
params: { failCount, captureManual },
params: { failCount, captureManual, captureManualTwice },
});

return new Response(JSON.stringify({ id: instance.id }), { headers: { 'Content-Type': 'application/json' } });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,36 @@ it('No error event when step eventually succeeds within retry limit', async ({ s
await runner.completed();
});

// Workflows opt out of the Dedupe integration via `enableDedupe: false`, so identical errors
// captured within one run are all delivered instead of being collapsed into a single event.
it('Identical exceptions captured within one run are all sent (Dedupe is disabled)', async ({ signal }) => {
const runner = createRunner(__dirname)
.expectN(2, (envelope: Envelope): void => {
const [, items] = envelope;
const [itemHeader, itemBody] = items[0] as [{ type: string }, Record<string, unknown>];

expect(itemHeader.type).toBe('event');

const exception = itemBody.exception as { values?: Array<{ value?: string }> };
expect(exception?.values?.[0]?.value).toBe('Manual capture');
})
.expect(flushMarkerMatcher)
.unordered()
.start(signal);

const trigger = await runner.makeRequest<TriggerResponse>(
'get',
'/trigger-workflow?failCount=0&captureManualTwice=true',
);
expect(trigger?.id).toBeDefined();

const status = await waitForWorkflowStatus(runner.makeRequest.bind(runner), trigger!.id);
expect(status?.status?.status).toBe('complete');

await runner.makeRequest('get', '/flush-marker');
await runner.completed();
});

it('Manually captured exceptions are always sent on every attempt', async ({ signal }) => {
const runner = createRunner(__dirname)
.expectN(3, (envelope: Envelope): void => {
Expand Down
22 changes: 22 additions & 0 deletions packages/cloudflare/test/sdk.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,28 @@ describe('init', () => {
);
});

test('installs Dedupe integration by default', () => {
init({ dsn: 'https://public@dsn.ingest.sentry.io/1337' });
const client = getClient();

expect(client?.getOptions()).toEqual(
expect.objectContaining({
integrations: expect.arrayContaining([expect.objectContaining({ name: 'Dedupe' })]),
}),
);
});

test('does not install Dedupe integration when enableDedupe is false', () => {
init({ dsn: 'https://public@dsn.ingest.sentry.io/1337', enableDedupe: false });
const client = getClient();

expect(client?.getOptions()).toEqual(
expect.objectContaining({
integrations: expect.not.arrayContaining([expect.objectContaining({ name: 'Dedupe' })]),
}),
);
});

type MarkedIntegration = Integration & { _custom?: boolean };

test("doesn't add spanStreamingIntegration if user added it manually", () => {
Expand Down
Loading