@@ -477,24 +477,44 @@ test.describe("Tracing", () => {
477477 ) ;
478478 expect ( transactionEvents . length ) . toBeGreaterThan ( 0 ) ;
479479
480- // Find API transaction and LiveView transaction
480+ // Find API transaction
481481 const apiTransaction = transactionEvents . find (
482482 ( event ) =>
483483 event . transaction ?. includes ( "/api/data" ) ||
484484 ( event . contexts ?. trace ?. data as any ) ?. [ "http.route" ] === "/api/data"
485485 ) ;
486- const liveViewTransaction = transactionEvents . find (
486+ expect ( apiTransaction ) . toBeDefined ( ) ;
487+
488+ // Find LiveView HTTP transaction (static render - no DB queries here)
489+ const liveViewHttpTransaction = transactionEvents . find (
487490 ( event ) =>
488- event . transaction ?. includes ( "/users" ) ||
489- ( event . contexts ?. trace ?. data as any ) ?. [ "http.route" ] === "/users"
491+ event . contexts ?. trace ?. op === "http.server" &&
492+ ( ( event . contexts ?. trace ?. data as any ) ?. [ "http.route" ] === "/users" ||
493+ event . transaction ?. includes ( "/users" ) )
490494 ) ;
495+ expect ( liveViewHttpTransaction ) . toBeDefined ( ) ;
491496
492- expect ( apiTransaction ) . toBeDefined ( ) ;
493- expect ( liveViewTransaction ) . toBeDefined ( ) ;
497+ // Find LiveView mount transaction (WebSocket - DB queries run here)
498+ // The mount transaction is created by opentelemetry_phoenix for LiveView WebSocket
499+ // There may be multiple mount transactions - find the one with spans (where DB query ran)
500+ const liveViewMountTransactions = transactionEvents . filter (
501+ ( event ) =>
502+ event . transaction ?. includes ( "UserLive.Index.mount" ) ||
503+ event . transaction ?. includes ( ".mount" )
504+ ) ;
505+
506+ // Find mount transaction that has spans (prefer one with DB spans)
507+ const liveViewMountTransaction = liveViewMountTransactions . find ( tx => {
508+ const spans = ( tx as any ) . spans || [ ] ;
509+ return spans . length > 0 ;
510+ } ) || liveViewMountTransactions [ 0 ] ;
511+
512+ expect ( liveViewMountTransaction ) . toBeDefined ( ) ;
494513
495- // Both should have valid trace IDs
514+ // All should have valid trace IDs
496515 expect ( apiTransaction ?. contexts ?. trace ?. trace_id ) . toMatch ( / ^ [ a - f 0 - 9 ] { 32 } $ / ) ;
497- expect ( liveViewTransaction ?. contexts ?. trace ?. trace_id ) . toMatch ( / ^ [ a - f 0 - 9 ] { 32 } $ / ) ;
516+ expect ( liveViewHttpTransaction ?. contexts ?. trace ?. trace_id ) . toMatch ( / ^ [ a - f 0 - 9 ] { 32 } $ / ) ;
517+ expect ( liveViewMountTransaction ?. contexts ?. trace ?. trace_id ) . toMatch ( / ^ [ a - f 0 - 9 ] { 32 } $ / ) ;
498518
499519 // Verify the API transaction has database spans
500520 const apiSpans = ( apiTransaction as any ) ?. spans || [ ] ;
@@ -508,17 +528,29 @@ test.describe("Tracing", () => {
508528 expect ( dbSpan . description ) . toMatch ( / S E L E C T / i) ;
509529 } ) ;
510530
511- // Check LiveView transaction for spans (optional, may not have them in all cases)
512- const liveViewSpans = ( liveViewTransaction as any ) ?. spans || [ ] ;
513- if ( liveViewSpans . length > 0 ) {
514- const liveViewDbSpans = liveViewSpans . filter ( ( span : any ) => span . op === "db" ) ;
515- if ( liveViewDbSpans . length > 0 ) {
516- liveViewDbSpans . forEach ( ( dbSpan : any ) => {
517- expect ( dbSpan . data [ "db.system" ] ) . toBeDefined ( ) ;
518- expect ( dbSpan . data [ "db.statement" ] ) . toBeDefined ( ) ;
519- } ) ;
520- }
521- }
531+ // LiveView HTTP transaction should NOT have DB spans
532+ // (static render doesn't run Ecto query because connected?(socket) == false)
533+ const httpSpans = ( liveViewHttpTransaction as any ) ?. spans || [ ] ;
534+ const httpDbSpans = httpSpans . filter ( ( span : any ) => span . op === "db" ) ;
535+ expect ( httpDbSpans . length ) . toBe ( 0 ) ;
536+
537+ // LiveView mount transaction MUST have DB spans
538+ // (WebSocket mount runs Ecto query because connected?(socket) == true)
539+ const mountSpans = ( liveViewMountTransaction as any ) ?. spans || [ ] ;
540+ expect ( mountSpans . length ) . toBeGreaterThan ( 0 ) ;
541+
542+ const mountDbSpans = mountSpans . filter ( ( span : any ) => span . op === "db" ) ;
543+ expect ( mountDbSpans . length ) . toBeGreaterThan ( 0 ) ;
544+
545+ // Verify mount DB spans have proper structure
546+ mountDbSpans . forEach ( ( dbSpan : any ) => {
547+ expect ( dbSpan . span_id ) . toMatch ( / ^ [ a - f 0 - 9 ] { 16 } $ / ) ;
548+ expect ( dbSpan . trace_id ) . toMatch ( / ^ [ a - f 0 - 9 ] { 32 } $ / ) ;
549+ expect ( dbSpan . data [ "db.system" ] ) . toBeDefined ( ) ;
550+ expect ( dbSpan . data [ "db.statement" ] ) . toBeDefined ( ) ;
551+ expect ( dbSpan . data [ "db.statement" ] ) . toMatch ( / u s e r s / i) ;
552+ expect ( dbSpan . description ) . toMatch ( / S E L E C T / i) ;
553+ } ) ;
522554 } ) ;
523555 } ) ;
524556} ) ;
0 commit comments