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,23 @@
import * as Sentry from '@sentry/browser';
import { createClient } from '@supabase/supabase-js';

window.Sentry = Sentry;

const supabaseClient = createClient('https://test.supabase.co', 'test-key');

Sentry.init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
integrations: [Sentry.browserTracingIntegration(), Sentry.supabaseIntegration({ supabaseClient })],
tracesSampleRate: 1.0,
});

// Simulate generic RPC call
async function callGenericRpc() {
try {
await supabaseClient.rpc('my_custom_function', { param1: 'value1' });
} catch (error) {
Sentry.captureException(error);
}
}

callGenericRpc();
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import type { Page } from '@playwright/test';
import { expect } from '@playwright/test';
import type { Event } from '@sentry/core';
import { sentryTest } from '../../../../utils/fixtures';
import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers';

async function mockSupabaseRoute(page: Page) {
await page.route('**/rpc/my_custom_function', route => {
return route.fulfill({
status: 200,
body: JSON.stringify({ result: 'success' }),
headers: {
'Content-Type': 'application/json',
},
});
});
}

const bundle = process.env.PW_BUNDLE || '';
// We only want to run this in non-CDN bundle mode
if (bundle.startsWith('bundle')) {
sentryTest.skip();
}

sentryTest(
'should capture exactly one db span for generic RPC calls (no double instrumentation)',
async ({ getLocalTestUrl, page }) => {
if (shouldSkipTracingTest()) {
return;
}

await mockSupabaseRoute(page);

const url = await getLocalTestUrl({ testDir: __dirname });

const event = await getFirstSentryEnvelopeRequest<Event>(page, url);
const dbSpans = event.spans?.filter(({ op }) => op === 'db');

// Should have exactly one db span (not doubled by PostgREST instrumentation)
expect(dbSpans).toHaveLength(1);

expect(dbSpans![0]).toMatchObject({
description: 'rpc(my_custom_function)',
parent_span_id: event.contexts?.trace?.span_id,
span_id: expect.any(String),
start_timestamp: expect.any(Number),
timestamp: expect.any(Number),
trace_id: event.contexts?.trace?.trace_id,
data: expect.objectContaining({
'sentry.op': 'db',
'sentry.origin': 'auto.db.supabase',
'db.system': 'postgresql',
'db.operation': 'rpc',
'db.params': { param1: 'value1' },
}),
});
},
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import * as Sentry from '@sentry/browser';
import { createClient } from '@supabase/supabase-js';

window.Sentry = Sentry;

const supabaseClient = createClient('https://test.supabase.co', 'test-key', {
db: {
schema: 'pgmq_public',
},
});

Sentry.init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
integrations: [Sentry.browserTracingIntegration(), Sentry.supabaseIntegration({ supabaseClient })],
tracesSampleRate: 1.0,
});

// Simulate queue operations
async function performQueueOperations() {
try {
await supabaseClient.rpc('send', {
queue_name: 'todos',
message: { title: 'Test Todo' },
});

await supabaseClient.rpc('pop', {
queue_name: 'todos',
});
} catch (error) {
Sentry.captureException(error);
}
}

performQueueOperations();
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import type { Page } from '@playwright/test';
import { expect } from '@playwright/test';
import type { Event } from '@sentry/core';
import { sentryTest } from '../../../../utils/fixtures';
import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers';

async function mockSupabaseRoute(page: Page) {
await page.route('**/rpc/send', route => {
return route.fulfill({
status: 200,
body: JSON.stringify([0]),
headers: {
'Content-Type': 'application/json',
},
});
});

await page.route('**/rpc/pop', route => {
return route.fulfill({
status: 200,
body: JSON.stringify([
{
msg_id: 0,
},
]),
headers: {
'Content-Type': 'application/json',
},
});
});
}

const bundle = process.env.PW_BUNDLE || '';
// We only want to run this in non-CDN bundle mode
if (bundle.startsWith('bundle')) {
sentryTest.skip();
}

sentryTest('should capture Supabase queue spans from client.rpc', async ({ getLocalTestUrl, page }) => {
if (shouldSkipTracingTest()) {
return;
}

await mockSupabaseRoute(page);

const url = await getLocalTestUrl({ testDir: __dirname });

const event = await getFirstSentryEnvelopeRequest<Event>(page, url);
const queueSpans = event.spans?.filter(({ op }) => op?.startsWith('queue.'));

expect(queueSpans).toHaveLength(2);

expect(queueSpans![0]).toMatchObject({
description: 'publish todos',
parent_span_id: event.contexts?.trace?.span_id,
span_id: expect.any(String),
start_timestamp: expect.any(Number),
timestamp: expect.any(Number),
trace_id: event.contexts?.trace?.trace_id,
data: expect.objectContaining({
'sentry.op': 'queue.publish',
'sentry.origin': 'auto.db.supabase.queue.producer',
'messaging.destination.name': 'todos',
'messaging.message.id': '0',
}),
});

expect(queueSpans![1]).toMatchObject({
description: 'process todos',
parent_span_id: event.contexts?.trace?.span_id,
span_id: expect.any(String),
start_timestamp: expect.any(Number),
timestamp: expect.any(Number),
trace_id: event.contexts?.trace?.trace_id,
data: expect.objectContaining({
'sentry.op': 'queue.process',
'sentry.origin': 'auto.db.supabase.queue.consumer',
'messaging.destination.name': 'todos',
'messaging.message.id': '0',
}),
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import * as Sentry from '@sentry/browser';
import { createClient } from '@supabase/supabase-js';

window.Sentry = Sentry;

const supabaseClient = createClient('https://test.supabase.co', 'test-key', {
db: {
schema: 'pgmq_public',
},
});

Sentry.init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
integrations: [Sentry.browserTracingIntegration(), Sentry.supabaseIntegration({ supabaseClient })],
tracesSampleRate: 1.0,
});

// Simulate queue operations
async function performQueueOperations() {
try {
await supabaseClient.rpc('pgmq.send', {
queue_name: 'todos',
message: { title: 'Test Todo' },
});

await supabaseClient.rpc('pgmq.pop', {
queue_name: 'todos',
});
} catch (error) {
Sentry.captureException(error);
}
}

performQueueOperations();
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import type { Page } from '@playwright/test';
import { expect } from '@playwright/test';
import type { Event } from '@sentry/core';
import { sentryTest } from '../../../../utils/fixtures';
import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers';

async function mockSupabaseRoute(page: Page) {
await page.route('**/rpc/pgmq.send', route => {
return route.fulfill({
status: 200,
body: JSON.stringify([0]),
headers: {
'Content-Type': 'application/json',
},
});
});

await page.route('**/rpc/pgmq.pop', route => {
return route.fulfill({
status: 200,
body: JSON.stringify([
{
msg_id: 0,
},
]),
headers: {
'Content-Type': 'application/json',
},
});
});
}

const bundle = process.env.PW_BUNDLE || '';
// We only want to run this in non-CDN bundle mode
if (bundle.startsWith('bundle')) {
sentryTest.skip();
}

sentryTest('should capture Supabase queue spans from schema-qualified RPC names', async ({ getLocalTestUrl, page }) => {
if (shouldSkipTracingTest()) {
return;
}

await mockSupabaseRoute(page);

const url = await getLocalTestUrl({ testDir: __dirname });

const event = await getFirstSentryEnvelopeRequest<Event>(page, url);
const queueSpans = event.spans?.filter(({ op }) => op?.startsWith('queue.'));

expect(queueSpans).toHaveLength(2);

expect(queueSpans![0]).toMatchObject({
description: 'publish todos',
parent_span_id: event.contexts?.trace?.span_id,
span_id: expect.any(String),
start_timestamp: expect.any(Number),
timestamp: expect.any(Number),
trace_id: event.contexts?.trace?.trace_id,
data: expect.objectContaining({
'sentry.op': 'queue.publish',
'sentry.origin': 'auto.db.supabase.queue.producer',
'messaging.destination.name': 'todos',
'messaging.message.id': '0',
}),
});

expect(queueSpans![1]).toMatchObject({
description: 'process todos',
parent_span_id: event.contexts?.trace?.span_id,
span_id: expect.any(String),
start_timestamp: expect.any(Number),
timestamp: expect.any(Number),
trace_id: event.contexts?.trace?.trace_id,
data: expect.objectContaining({
'sentry.op': 'queue.process',
'sentry.origin': 'auto.db.supabase.queue.consumer',
'messaging.destination.name': 'todos',
'messaging.message.id': '0',
}),
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import * as Sentry from '@sentry/browser';
import { createClient } from '@supabase/supabase-js';

window.Sentry = Sentry;

const supabaseClient = createClient('https://test.supabase.co', 'test-key', {
db: {
schema: 'pgmq_public',
},
});

Sentry.init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
integrations: [Sentry.browserTracingIntegration(), Sentry.supabaseIntegration({ supabaseClient })],
tracesSampleRate: 1.0,
});

// Simulate queue operations
async function performQueueOperations() {
try {
await supabaseClient.schema('pgmq_public').rpc('send', {
queue_name: 'todos',
message: { title: 'Test Todo' },
});

await supabaseClient.schema('pgmq_public').rpc('pop', {
queue_name: 'todos',
});
} catch (error) {
Sentry.captureException(error);
}
}

performQueueOperations();
Loading
Loading