77 translateFiltersIntoMethods ,
88} from '../../../src/integrations/supabase' ;
99import type { PostgRESTQueryBuilder , SupabaseClientInstance } from '../../../src/integrations/supabase' ;
10+ import { resolveDataCollectionOptions } from '../../../src/utils/data-collection/resolveDataCollectionOptions' ;
1011
1112const tracingMocks = vi . hoisted ( ( ) => ( {
1213 startSpan : vi . fn ( ( _opts : unknown , cb : ( span : unknown ) => unknown ) => {
@@ -38,23 +39,23 @@ type CreateMockSupabaseClientOptions = {
3839 method ?: string ;
3940 url ?: URL | string ;
4041 body ?: unknown ;
41- /** When set, configures the mocked Sentry client `sendDefaultPii `. Omit to leave `getClient` to the test file `beforeEach`. */
42- sendDefaultPii ?: boolean ;
42+ /** When set, configures the mocked Sentry client's `dataCollection.userInfo `. Omit to leave `getClient` to the test file `beforeEach`. */
43+ dataCollectionUserInfo ?: boolean ;
4344} ;
4445
4546const DEFAULT_MOCK_SUPABASE_REST_URL = 'https://example.supabase.co/rest/v1/todos' ;
4647
47- /** Shared PATCH + query string + body shape for `sendDefaultPii` tests. */
48+ /** Shared PATCH + query string + body shape for operation data tests. */
4849const MOCK_SUPABASE_PII_SCENARIO : Pick < CreateMockSupabaseClientOptions , 'method' | 'url' | 'body' > = {
4950 method : 'PATCH' ,
5051 url : 'https://example.supabase.co/rest/v1/users?email=eq.secret%40example.com&select=id' ,
5152 body : { full_name : 'Jane Doe' , phone : '555-0100' } ,
5253} ;
5354
5455function createMockSupabaseClient ( resolveWith : unknown , options ?: CreateMockSupabaseClientOptions ) : unknown {
55- if ( options ?. sendDefaultPii !== undefined ) {
56+ if ( options ?. dataCollectionUserInfo !== undefined ) {
5657 currentScopesMocks . getClient . mockReturnValue ( {
57- getOptions : ( ) => ( { sendDefaultPii : options . sendDefaultPii } ) ,
58+ getDataCollectionOptions : ( ) => ( { userInfo : options . dataCollectionUserInfo } ) ,
5859 } as any ) ;
5960 }
6061
@@ -223,7 +224,7 @@ describe('Supabase Integration', () => {
223224 } ) ;
224225 } ) ;
225226
226- describe ( 'sendDefaultPii ' , ( ) => {
227+ describe ( 'operation data collection ' , ( ) => {
227228 let captureExceptionSpy : ReturnType < typeof vi . spyOn > ;
228229 let addBreadcrumbSpy : ReturnType < typeof vi . spyOn > ;
229230
@@ -236,10 +237,10 @@ describe('Supabase Integration', () => {
236237 vi . restoreAllMocks ( ) ;
237238 } ) ;
238239
239- it ( 'omits db.query, db.body, and breadcrumb query/body when sendDefaultPii is false' , async ( ) => {
240+ it ( 'omits db.query, db.body, and breadcrumb query/body when dataCollection.userInfo is false' , async ( ) => {
240241 const client = createMockSupabaseClient (
241242 { status : 200 } ,
242- { ...MOCK_SUPABASE_PII_SCENARIO , sendDefaultPii : false } ,
243+ { ...MOCK_SUPABASE_PII_SCENARIO , dataCollectionUserInfo : false } ,
243244 ) ;
244245 instrumentSupabaseClient ( client ) ;
245246
@@ -258,8 +259,11 @@ describe('Supabase Integration', () => {
258259 expect ( breadcrumb ) . not . toHaveProperty ( 'data' ) ;
259260 } ) ;
260261
261- it ( 'includes db.query, db.body, and breadcrumb query/body when sendDefaultPii is true' , async ( ) => {
262- const client = createMockSupabaseClient ( { status : 200 } , { ...MOCK_SUPABASE_PII_SCENARIO , sendDefaultPii : true } ) ;
262+ it ( 'includes db.query, db.body, and breadcrumb query/body when dataCollection.userInfo is true' , async ( ) => {
263+ const client = createMockSupabaseClient (
264+ { status : 200 } ,
265+ { ...MOCK_SUPABASE_PII_SCENARIO , dataCollectionUserInfo : true } ,
266+ ) ;
263267 instrumentSupabaseClient ( client ) ;
264268
265269 await ( client as any ) . from ( 'users' ) . update ( { } ) . then ( ) ;
@@ -286,10 +290,95 @@ describe('Supabase Integration', () => {
286290 ) ;
287291 } ) ;
288292
289- it ( 'omits supabase error context query/body when sendDefaultPii is false' , async ( ) => {
293+ it ( 'includes data when sendOperationData option is set, regardless of dataCollection.userInfo' , async ( ) => {
294+ const client = createMockSupabaseClient (
295+ { status : 200 } ,
296+ { ...MOCK_SUPABASE_PII_SCENARIO , dataCollectionUserInfo : false } ,
297+ ) ;
298+ instrumentSupabaseClient ( client , { sendOperationData : true } ) ;
299+
300+ await ( client as any ) . from ( 'users' ) . update ( { } ) . then ( ) ;
301+
302+ const spanOptions = tracingMocks . startSpan . mock . calls [ 0 ] ! [ 0 ] as {
303+ name : string ;
304+ attributes : Record < string , unknown > ;
305+ } ;
306+ expect ( spanOptions . name ) . toContain ( 'eq(email, secret@example.com)' ) ;
307+ expect ( spanOptions . attributes [ 'db.query' ] ) . toEqual (
308+ expect . arrayContaining ( [ expect . stringContaining ( 'secret@example.com' ) ] ) ,
309+ ) ;
310+ expect ( spanOptions . attributes [ 'db.body' ] ) . toEqual (
311+ expect . objectContaining ( { full_name : 'Jane Doe' , phone : '555-0100' } ) ,
312+ ) ;
313+ } ) ;
314+
315+ it ( 'sendOperationData: false takes precedence over dataCollection.userInfo: true' , async ( ) => {
316+ const client = createMockSupabaseClient (
317+ { status : 200 } ,
318+ { ...MOCK_SUPABASE_PII_SCENARIO , dataCollectionUserInfo : true } ,
319+ ) ;
320+ instrumentSupabaseClient ( client , { sendOperationData : false } ) ;
321+
322+ await ( client as any ) . from ( 'users' ) . update ( { } ) . then ( ) ;
323+
324+ const spanOptions = tracingMocks . startSpan . mock . calls [ 0 ] ! [ 0 ] as {
325+ name : string ;
326+ attributes : Record < string , unknown > ;
327+ } ;
328+ expect ( spanOptions . name ) . toContain ( '[redacted]' ) ;
329+ expect ( spanOptions . attributes [ 'db.query' ] ) . toBeUndefined ( ) ;
330+ expect ( spanOptions . attributes [ 'db.body' ] ) . toBeUndefined ( ) ;
331+ } ) ;
332+
333+ it ( 'includes data when legacy sendDefaultPii: true is bridged to dataCollection.userInfo' , async ( ) => {
334+ const resolved = resolveDataCollectionOptions ( { sendDefaultPii : true } ) ;
335+ currentScopesMocks . getClient . mockReturnValue ( {
336+ getDataCollectionOptions : ( ) => resolved ,
337+ } as any ) ;
338+
339+ const client = createMockSupabaseClient ( { status : 200 } , { ...MOCK_SUPABASE_PII_SCENARIO } ) ;
340+ instrumentSupabaseClient ( client ) ;
341+
342+ await ( client as any ) . from ( 'users' ) . update ( { } ) . then ( ) ;
343+
344+ const spanOptions = tracingMocks . startSpan . mock . calls [ 0 ] ! [ 0 ] as {
345+ name : string ;
346+ attributes : Record < string , unknown > ;
347+ } ;
348+ expect ( spanOptions . name ) . toContain ( 'eq(email, secret@example.com)' ) ;
349+ expect ( spanOptions . attributes [ 'db.query' ] ) . toEqual (
350+ expect . arrayContaining ( [ expect . stringContaining ( 'secret@example.com' ) ] ) ,
351+ ) ;
352+ expect ( spanOptions . attributes [ 'db.body' ] ) . toEqual (
353+ expect . objectContaining ( { full_name : 'Jane Doe' , phone : '555-0100' } ) ,
354+ ) ;
355+ } ) ;
356+
357+ it ( 'redacts data when legacy sendDefaultPii is not set (bridged defaults)' , async ( ) => {
358+ const resolved = resolveDataCollectionOptions ( { sendDefaultPii : false } ) ;
359+ currentScopesMocks . getClient . mockReturnValue ( {
360+ getDataCollectionOptions : ( ) => resolved ,
361+ } as any ) ;
362+
363+ const client = createMockSupabaseClient ( { status : 200 } , { ...MOCK_SUPABASE_PII_SCENARIO } ) ;
364+ instrumentSupabaseClient ( client ) ;
365+
366+ await ( client as any ) . from ( 'users' ) . update ( { } ) . then ( ) ;
367+
368+ const spanOptions = tracingMocks . startSpan . mock . calls [ 0 ] ! [ 0 ] as {
369+ name : string ;
370+ attributes : Record < string , unknown > ;
371+ } ;
372+ expect ( spanOptions . name ) . toContain ( '[redacted]' ) ;
373+ expect ( spanOptions . name ) . not . toContain ( 'secret' ) ;
374+ expect ( spanOptions . attributes [ 'db.query' ] ) . toBeUndefined ( ) ;
375+ expect ( spanOptions . attributes [ 'db.body' ] ) . toBeUndefined ( ) ;
376+ } ) ;
377+
378+ it ( 'omits supabase error context query/body when data collection is off' , async ( ) => {
290379 const client = createMockSupabaseClient (
291380 { status : 400 , error : { message : 'Bad request' , code : '400' } } ,
292- { ...MOCK_SUPABASE_PII_SCENARIO , sendDefaultPii : false } ,
381+ { ...MOCK_SUPABASE_PII_SCENARIO , dataCollectionUserInfo : false } ,
293382 ) ;
294383 instrumentSupabaseClient ( client ) ;
295384
@@ -328,7 +417,7 @@ describe('Supabase Integration', () => {
328417 method : 'POST' ,
329418 url : 'https://example.supabase.co/rest/v1/todos?columns=' ,
330419 body : [ { title : 'Test Todo' } ] ,
331- sendDefaultPii : true ,
420+ dataCollectionUserInfo : true ,
332421 } ,
333422 ) ;
334423 instrumentSupabaseClient ( client ) ;
0 commit comments