From db1601da27843dbb18610efcf3dfc6130d1c6f51 Mon Sep 17 00:00:00 2001 From: Borislav Borisov Date: Tue, 28 Apr 2026 06:43:45 +0100 Subject: [PATCH 1/4] test: Add macro-based parameterisation foundation for backend tests Scaffolding for collapsing the ~95% structural duplication across tests/sqlite.rs, tests/postgres.rs, and tests/mysql.rs. - Add Dialect struct and SQLITE_DIALECT / POSTGRES_DIALECT / MYSQL_DIALECT constants supplying per-backend SQL fragments (column types, etc.) that parameterised bodies need. - Move test_annotations() and assert_annotated_span() from per-backend duplicates into tests/common.rs, parameterising the latter over &Dialect so the three backends share one definition. Add assert_one_annotated_span() for the recurring trailing assertion block. - Add fresh_table! macro: DROP IF EXISTS + CREATE TABLE so parameterised bodies behave identically against fresh sqlite :memory: pools and shared postgres / mysql containers. - Migrate execute_creates_span_via_pool to a test_execute_creates_span_via_pool! macro, proving the macro_rules approach works across all three backends. --- tests/common.rs | 169 +++++++++++++++++++++++++ tests/mysql.rs | 289 ++++++++++++++++++------------------------ tests/postgres.rs | 289 ++++++++++++++++++------------------------ tests/sqlite.rs | 310 +++++++++++++++++++--------------------------- 4 files changed, 539 insertions(+), 518 deletions(-) diff --git a/tests/common.rs b/tests/common.rs index ac823fe..9e5c7c0 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -3,6 +3,7 @@ use opentelemetry::trace::{SpanKind, Status}; use opentelemetry_sdk::metrics::{InMemoryMetricExporter, PeriodicReader, SdkMeterProvider}; use opentelemetry_sdk::trace::{InMemorySpanExporter, SdkTracerProvider, SpanData}; +use sqlx_otel::QueryAnnotations; /// Test harness that installs in-memory span and metric exporters as the global providers, /// collects telemetry in-process, and cleans up on drop. @@ -171,3 +172,171 @@ pub fn assert_error_span(span: &SpanData) { "exception.message should be a non-empty string, got {exception_message:?}", ); } + +// --------------------------------------------------------------------------- +// Backend parameterisation +// --------------------------------------------------------------------------- +// +// Why macros and not generic functions: the library's `impl_executor!` macro at +// `src/executor.rs` instantiates `Executor` impls for `&Pool`, `&mut +// PoolConnection`, `&mut Transaction<'_, DB>`, and the matching `Annotated` / +// `AnnotatedMut` wrappers, each gated by `for<'a> &'a mut DB::Connection: Executor<'a, +// Database = DB>`. Test bodies generic over `DB` (with that HRTB declared) trigger +// trait-resolution overflow on stable rustc — the compiler tries to satisfy the bound +// against multiple wrapper impls and recurses. Bumping `recursion_limit` does not +// help; the chain genuinely diverges. +// +// `macro_rules!` sidesteps the issue entirely: each invocation expands at the call +// site with concrete types, so the bound chain resolves directly against the upstream +// `sqlx::Sqlite` / `sqlx::Postgres` / `sqlx::MySql` impls. Trade-off: error messages +// point at the expansion site instead of the macro definition; mitigated by keeping +// each macro short and well-documented. + +/// Per-backend SQL fragments and metadata used by parameterised test bodies. +/// +/// Test bodies that vary only in SQL syntax (column types, upsert form, string concat) +/// read the relevant field from a `&Dialect` argument instead of being duplicated across +/// three backend files. Each backend file passes the matching constant +/// (`SQLITE_DIALECT`, `POSTGRES_DIALECT`, `MYSQL_DIALECT`) to the shared body. +pub struct Dialect { + /// The OpenTelemetry `db.system.name` value (`"sqlite"`, `"postgresql"`, `"mysql"`). + pub system: &'static str, + /// Column definition for an integer primary key in CREATE TABLE: e.g. `"INTEGER + /// PRIMARY KEY"` for sqlite, `"INT PRIMARY KEY"` for postgres / mysql. + pub id_pk_column: &'static str, + /// Column definition for a non-null text column: e.g. `"TEXT NOT NULL"` for sqlite + /// and postgres, `"VARCHAR(255) NOT NULL"` for mysql. + pub text_column: &'static str, +} + +pub const SQLITE_DIALECT: Dialect = Dialect { + system: "sqlite", + id_pk_column: "INTEGER PRIMARY KEY", + text_column: "TEXT NOT NULL", +}; + +pub const POSTGRES_DIALECT: Dialect = Dialect { + system: "postgresql", + id_pk_column: "INT PRIMARY KEY", + text_column: "TEXT NOT NULL", +}; + +pub const MYSQL_DIALECT: Dialect = Dialect { + system: "mysql", + id_pk_column: "INT PRIMARY KEY", + text_column: "VARCHAR(255) NOT NULL", +}; + +/// `DROP TABLE IF EXISTS` then `CREATE TABLE` at the supplied pool. Used at the top of +/// every parameterised body so sqlite (fresh `:memory:` per pool) and postgres / mysql +/// (shared container) behave identically. Expanded inline at the call site via the +/// `fresh_table!` macro so it operates on concrete pool types without HRTB issues. +#[macro_export] +macro_rules! fresh_table { + ($pool:expr, $table:expr, $columns:expr) => {{ + use sqlx::Executor as _; + let drop_sql = format!("DROP TABLE IF EXISTS {}", $table); + $pool.execute(drop_sql.as_str()).await.unwrap(); + let create_sql = format!("CREATE TABLE {} ({})", $table, $columns); + $pool.execute(create_sql.as_str()).await.unwrap(); + }}; +} + +/// The standard annotation set used across most annotation tests: +/// `db.operation.name = "SELECT"`, `db.collection.name = "users"`. +pub fn test_annotations() -> QueryAnnotations { + QueryAnnotations::new() + .operation("SELECT") + .collection("users") +} + +/// Assert that a span carries the attributes set by [`test_annotations`]. +/// +/// The `db.system.name` value is taken from the supplied `Dialect`, so this helper works +/// for every backend without per-file duplication. +pub fn assert_annotated_span(span: &SpanData, dialect: &Dialect) { + assert_eq!(span.span_kind, SpanKind::Client); + assert_eq!(span.name, "SELECT users"); + assert_eq!( + attr(span, "db.system.name"), + Some(opentelemetry::Value::String(dialect.system.into())), + ); + assert_eq!( + attr(span, "db.operation.name"), + Some(opentelemetry::Value::String("SELECT".into())), + ); + assert_eq!( + attr(span, "db.collection.name"), + Some(opentelemetry::Value::String("users".into())), + ); +} + +/// Assert that the exporter contains exactly one span and that it matches the standard +/// annotation shape. Used to collapse the recurring trailing assertion block at the end +/// of every annotation-style test (including the `sqlx::query!()` macro tests, whose +/// bodies must remain backend-specific but whose assertions can reuse this helper). +pub fn assert_one_annotated_span(tel: &TestTelemetry, dialect: &Dialect) { + let spans = tel.spans(); + assert_eq!( + spans.len(), + 1, + "expected exactly one span, got {}", + spans.len() + ); + assert_annotated_span(&spans[0], dialect); +} + +// --------------------------------------------------------------------------- +// Parameterised test bodies (macro_rules) +// --------------------------------------------------------------------------- +// +// Each macro takes a pool factory expression and a dialect constant and expands to +// the full test body. Backend wrappers invoke with their factory + dialect: +// +// #[tokio::test] +// #[serial] +// async fn execute_creates_span_via_pool() { +// test_execute_creates_span_via_pool!(test_pool().await, common::SQLITE_DIALECT); +// } + +/// Bound-chain proof: exercises `&Pool: Executor` (plain), `Annotated<'_, +/// Pool>: Executor` (`with_annotations`), and the same via the `with_operation` +/// shorthand. Each `pool.execute(...)` runs against a freshly created table, so the +/// macro is safe to invoke against shared postgres / mysql containers. +#[macro_export] +macro_rules! test_execute_creates_span_via_pool { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx::Executor as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + $crate::fresh_table!( + &pool, + "exec_pool_test", + &format!("id {}", $dialect.id_pk_column) + ); + tel.reset(); + + (&pool) + .execute("INSERT INTO exec_pool_test (id) VALUES (1)") + .await + .unwrap(); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_common_span_attributes(&spans[0], $dialect.system); + assert!($crate::common::attr(&spans[0], "db.response.returned_rows").is_none()); + assert!($crate::common::attr(&spans[0], "db.response.affected_rows").is_some()); + + pool.with_annotations($crate::common::test_annotations()) + .execute("INSERT INTO exec_pool_test (id) VALUES (2)") + .await + .unwrap(); + $crate::common::assert_annotated_span(tel.spans().last().unwrap(), &$dialect); + + pool.with_operation("SELECT", "users") + .execute("INSERT INTO exec_pool_test (id) VALUES (3)") + .await + .unwrap(); + $crate::common::assert_annotated_span(tel.spans().last().unwrap(), &$dialect); + }}; +} diff --git a/tests/mysql.rs b/tests/mysql.rs index 13dcb1d..aee765b 100644 --- a/tests/mysql.rs +++ b/tests/mysql.rs @@ -5,7 +5,9 @@ mod common; use std::sync::OnceLock; use std::time::Duration; -use common::{assert_common_span_attributes, assert_error_span, attr}; +use common::{ + assert_annotated_span, assert_common_span_attributes, assert_error_span, attr, test_annotations, +}; use futures::StreamExt; use opentelemetry::trace::SpanKind; use serial_test::serial; @@ -62,31 +64,6 @@ async fn test_pool() -> Pool { PoolBuilder::from(raw).build() } -/// Standard annotations used across annotation assertions. -fn test_annotations() -> QueryAnnotations { - QueryAnnotations::new() - .operation("SELECT") - .collection("users") -} - -/// Assert that the span carries the standard annotation attributes. -fn assert_annotated_span(span: &opentelemetry_sdk::trace::SpanData) { - assert_eq!(span.span_kind, SpanKind::Client); - assert_eq!(span.name, "SELECT users"); - assert_eq!( - attr(span, "db.system.name"), - Some(opentelemetry::Value::String(SYSTEM.to_owned().into())), - ); - assert_eq!( - attr(span, "db.operation.name"), - Some(opentelemetry::Value::String("SELECT".into())), - ); - assert_eq!( - attr(span, "db.collection.name"), - Some(opentelemetry::Value::String("users".into())), - ); -} - // =========================================================================== // execute // =========================================================================== @@ -94,33 +71,7 @@ fn assert_annotated_span(span: &opentelemetry_sdk::trace::SpanData) { #[tokio::test] #[serial] async fn execute_creates_span_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - sqlx::query("CREATE TABLE IF NOT EXISTS exec_pool (id INT AUTO_INCREMENT PRIMARY KEY)") - .execute(&pool) - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - assert!(attr(&spans[0], "db.response.affected_rows").is_some()); - - // With annotations - pool.with_annotations(test_annotations()) - .execute("CREATE TABLE IF NOT EXISTS exec_pool (id INT AUTO_INCREMENT PRIMARY KEY)") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); - - // With shorthand - pool.with_operation("SELECT", "users") - .execute("CREATE TABLE IF NOT EXISTS exec_pool3 (id INT AUTO_INCREMENT PRIMARY KEY)") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + test_execute_creates_span_via_pool!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] @@ -146,14 +97,14 @@ async fn execute_creates_span_via_connection() { .execute("CREATE TABLE IF NOT EXISTS exec_conn (id INT AUTO_INCREMENT PRIMARY KEY)") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); // With shorthand conn.with_operation("SELECT", "users") .execute("CREATE TABLE IF NOT EXISTS exec_conn3 (id INT AUTO_INCREMENT PRIMARY KEY)") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); } #[tokio::test] @@ -186,7 +137,7 @@ async fn execute_creates_span_via_transaction() { assert_common_span_attributes(&spans[0], SYSTEM); assert!(attr(&spans[0], "db.response.returned_rows").is_none()); assert!(attr(&spans[0], "db.response.affected_rows").is_some()); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); } #[tokio::test] @@ -212,7 +163,7 @@ async fn execute_records_error() { assert!(result.is_err()); let spans = tel.spans(); let last = spans.last().unwrap(); - assert_annotated_span(last); + assert_annotated_span(last, &common::MYSQL_DIALECT); assert_error_span(last); // With shorthand (error path) @@ -222,7 +173,7 @@ async fn execute_records_error() { .await; assert!(result.is_err()); let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last); + assert_annotated_span(&last, &common::MYSQL_DIALECT); assert_error_span(&last); } @@ -355,7 +306,7 @@ async fn execute_many_via_pool() { .execute_many("SELECT 1; SELECT 2"); while stream.next().await.is_some() {} drop(stream); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); // With shorthand let mut stream = pool @@ -363,7 +314,7 @@ async fn execute_many_via_pool() { .execute_many("SELECT 1; SELECT 2"); while stream.next().await.is_some() {} drop(stream); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); } #[tokio::test] @@ -391,7 +342,7 @@ async fn execute_many_via_connection() { .execute_many("SELECT 1; SELECT 2"); while stream.next().await.is_some() {} drop(stream); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); // With shorthand let mut stream = conn @@ -399,7 +350,7 @@ async fn execute_many_via_connection() { .execute_many("SELECT 1; SELECT 2"); while stream.next().await.is_some() {} drop(stream); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); } #[tokio::test] @@ -436,7 +387,7 @@ async fn execute_many_via_transaction() { attr(&spans[0], "db.response.returned_rows"), Some(opentelemetry::Value::I64(0)) ); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); } #[tokio::test] @@ -468,7 +419,7 @@ async fn execute_many_records_error() { drop(stream); let spans = tel.spans(); let last = spans.last().unwrap(); - assert_annotated_span(last); + assert_annotated_span(last, &common::MYSQL_DIALECT); assert_error_span(last); // With shorthand (error path) @@ -479,7 +430,7 @@ async fn execute_many_records_error() { assert!(result.is_some_and(|r| r.is_err())); drop(stream); let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last); + assert_annotated_span(&last, &common::MYSQL_DIALECT); assert_error_span(&last); } @@ -515,7 +466,7 @@ async fn fetch_via_pool() { .fetch("SELECT 1 UNION ALL SELECT 2"); while stream.next().await.is_some() {} drop(stream); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); // With shorthand let mut stream = pool @@ -523,7 +474,7 @@ async fn fetch_via_pool() { .fetch("SELECT 1 UNION ALL SELECT 2"); while stream.next().await.is_some() {} drop(stream); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); } #[tokio::test] @@ -551,7 +502,7 @@ async fn fetch_via_connection() { .fetch("SELECT 1 UNION ALL SELECT 2"); while stream.next().await.is_some() {} drop(stream); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); // With shorthand let mut stream = conn @@ -559,7 +510,7 @@ async fn fetch_via_connection() { .fetch("SELECT 1 UNION ALL SELECT 2"); while stream.next().await.is_some() {} drop(stream); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); } #[tokio::test] @@ -596,7 +547,7 @@ async fn fetch_via_transaction() { attr(&spans[0], "db.response.returned_rows"), Some(opentelemetry::Value::I64(2)) ); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); } #[tokio::test] @@ -648,7 +599,7 @@ async fn fetch_stream_records_error() { drop(stream); let spans = tel.spans(); let last = spans.last().unwrap(); - assert_annotated_span(last); + assert_annotated_span(last, &common::MYSQL_DIALECT); assert_error_span(last); // With shorthand (error path) @@ -657,7 +608,7 @@ async fn fetch_stream_records_error() { assert!(result.is_some_and(|r| r.is_err())); drop(stream); let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last); + assert_annotated_span(&last, &common::MYSQL_DIALECT); assert_error_span(&last); } @@ -695,7 +646,7 @@ async fn fetch_many_via_pool() { .fetch_many("SELECT 1 UNION ALL SELECT 2"); while stream.next().await.is_some() {} drop(stream); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); // With shorthand let mut stream = pool @@ -703,7 +654,7 @@ async fn fetch_many_via_pool() { .fetch_many("SELECT 1 UNION ALL SELECT 2"); while stream.next().await.is_some() {} drop(stream); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); } #[tokio::test] @@ -731,7 +682,7 @@ async fn fetch_many_via_connection() { .fetch_many("SELECT 1 UNION ALL SELECT 2"); while stream.next().await.is_some() {} drop(stream); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); // With shorthand let mut stream = conn @@ -739,7 +690,7 @@ async fn fetch_many_via_connection() { .fetch_many("SELECT 1 UNION ALL SELECT 2"); while stream.next().await.is_some() {} drop(stream); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); } #[tokio::test] @@ -776,7 +727,7 @@ async fn fetch_many_via_transaction() { attr(&spans[0], "db.response.returned_rows"), Some(opentelemetry::Value::I64(2)) ); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); } #[tokio::test] @@ -828,7 +779,7 @@ async fn fetch_many_records_error() { drop(stream); let spans = tel.spans(); let last = spans.last().unwrap(); - assert_annotated_span(last); + assert_annotated_span(last, &common::MYSQL_DIALECT); assert_error_span(last); // With shorthand (error path) @@ -839,7 +790,7 @@ async fn fetch_many_records_error() { assert!(result.is_some_and(|r| r.is_err())); drop(stream); let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last); + assert_annotated_span(&last, &common::MYSQL_DIALECT); assert_error_span(&last); } @@ -872,14 +823,14 @@ async fn fetch_all_via_pool() { .fetch_all("SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); // With shorthand pool.with_operation("SELECT", "users") .fetch_all("SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); } #[tokio::test] @@ -908,14 +859,14 @@ async fn fetch_all_via_connection() { .fetch_all("SELECT 1 UNION ALL SELECT 2") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); // With shorthand conn.with_operation("SELECT", "users") .fetch_all("SELECT 1 UNION ALL SELECT 2") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); } #[tokio::test] @@ -951,7 +902,7 @@ async fn fetch_all_via_transaction() { attr(&spans[0], "db.response.returned_rows"), Some(opentelemetry::Value::I64(2)) ); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); } #[tokio::test] @@ -977,7 +928,7 @@ async fn fetch_all_records_error() { assert!(result.is_err()); let spans = tel.spans(); let last = spans.last().unwrap(); - assert_annotated_span(last); + assert_annotated_span(last, &common::MYSQL_DIALECT); assert_error_span(last); // With shorthand (error path) @@ -987,7 +938,7 @@ async fn fetch_all_records_error() { .await; assert!(result.is_err()); let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last); + assert_annotated_span(&last, &common::MYSQL_DIALECT); assert_error_span(&last); } @@ -1016,14 +967,14 @@ async fn fetch_one_via_pool() { .fetch_one("SELECT 1") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); // With shorthand pool.with_operation("SELECT", "users") .fetch_one("SELECT 1") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); } #[tokio::test] @@ -1048,14 +999,14 @@ async fn fetch_one_via_connection() { .fetch_one("SELECT 1") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); // With shorthand conn.with_operation("SELECT", "users") .fetch_one("SELECT 1") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); } #[tokio::test] @@ -1087,7 +1038,7 @@ async fn fetch_one_via_transaction() { attr(&spans[0], "db.response.returned_rows"), Some(opentelemetry::Value::I64(1)) ); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); } #[tokio::test] @@ -1113,7 +1064,7 @@ async fn fetch_one_records_error() { assert!(result.is_err()); let spans = tel.spans(); let last = spans.last().unwrap(); - assert_annotated_span(last); + assert_annotated_span(last, &common::MYSQL_DIALECT); assert_error_span(last); // With shorthand (error path) @@ -1123,7 +1074,7 @@ async fn fetch_one_records_error() { .await; assert!(result.is_err()); let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last); + assert_annotated_span(&last, &common::MYSQL_DIALECT); assert_error_span(&last); } @@ -1153,14 +1104,14 @@ async fn fetch_optional_records_one_row() { .fetch_optional("SELECT 1") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); // With shorthand pool.with_operation("SELECT", "users") .fetch_optional("SELECT 1") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); } #[tokio::test] @@ -1217,14 +1168,14 @@ async fn fetch_optional_via_connection() { .fetch_optional("SELECT 42") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); // With shorthand conn.with_operation("SELECT", "users") .fetch_optional("SELECT 42") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); } #[tokio::test] @@ -1257,7 +1208,7 @@ async fn fetch_optional_via_transaction() { attr(&spans[0], "db.response.returned_rows"), Some(opentelemetry::Value::I64(1)) ); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); } #[tokio::test] @@ -1283,7 +1234,7 @@ async fn fetch_optional_records_error() { assert!(result.is_err()); let spans = tel.spans(); let last = spans.last().unwrap(); - assert_annotated_span(last); + assert_annotated_span(last, &common::MYSQL_DIALECT); assert_error_span(last); // With shorthand (error path) @@ -1293,7 +1244,7 @@ async fn fetch_optional_records_error() { .await; assert!(result.is_err()); let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last); + assert_annotated_span(&last, &common::MYSQL_DIALECT); assert_error_span(&last); } @@ -1319,14 +1270,14 @@ async fn prepare_via_pool() { .prepare("SELECT 1") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); // With shorthand pool.with_operation("SELECT", "users") .prepare("SELECT 1") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); } #[tokio::test] @@ -1348,14 +1299,14 @@ async fn prepare_via_connection() { .prepare("SELECT 1") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); // With shorthand conn.with_operation("SELECT", "users") .prepare("SELECT 1") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); } #[tokio::test] @@ -1384,7 +1335,7 @@ async fn prepare_via_transaction() { assert_eq!(spans.len(), 3); assert_common_span_attributes(&spans[0], SYSTEM); assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); } #[tokio::test] @@ -1411,7 +1362,7 @@ async fn prepare_records_error() { assert!(result.is_err()); let spans = tel.spans(); let last = spans.last().unwrap(); - assert_annotated_span(last); + assert_annotated_span(last, &common::MYSQL_DIALECT); assert_error_span(last); // With shorthand (error path) @@ -1421,7 +1372,7 @@ async fn prepare_records_error() { .await; assert!(result.is_err()); let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last); + assert_annotated_span(&last, &common::MYSQL_DIALECT); assert_error_span(&last); } @@ -1447,14 +1398,14 @@ async fn prepare_with_via_pool() { .prepare_with("SELECT ?", &[]) .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); // With shorthand pool.with_operation("SELECT", "users") .prepare_with("SELECT ?", &[]) .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); } #[tokio::test] @@ -1476,14 +1427,14 @@ async fn prepare_with_via_connection() { .prepare_with("SELECT ?", &[]) .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); // With shorthand conn.with_operation("SELECT", "users") .prepare_with("SELECT ?", &[]) .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); } #[tokio::test] @@ -1512,7 +1463,7 @@ async fn prepare_with_via_transaction() { assert_eq!(spans.len(), 3); assert_common_span_attributes(&spans[0], SYSTEM); assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); } #[tokio::test] @@ -1539,7 +1490,7 @@ async fn prepare_with_records_error() { assert!(result.is_err()); let spans = tel.spans(); let last = spans.last().unwrap(); - assert_annotated_span(last); + assert_annotated_span(last, &common::MYSQL_DIALECT); assert_error_span(last); // With shorthand (error path) @@ -1549,7 +1500,7 @@ async fn prepare_with_records_error() { .await; assert!(result.is_err()); let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last); + assert_annotated_span(&last, &common::MYSQL_DIALECT); assert_error_span(&last); } @@ -1575,14 +1526,14 @@ async fn describe_via_pool() { .describe("SELECT 1") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); // With shorthand pool.with_operation("SELECT", "users") .describe("SELECT 1") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); } #[tokio::test] @@ -1604,14 +1555,14 @@ async fn describe_via_connection() { .describe("SELECT 1") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); // With shorthand conn.with_operation("SELECT", "users") .describe("SELECT 1") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); } #[tokio::test] @@ -1640,7 +1591,7 @@ async fn describe_via_transaction() { assert_eq!(spans.len(), 3); assert_common_span_attributes(&spans[0], SYSTEM); assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); } #[tokio::test] @@ -1667,7 +1618,7 @@ async fn describe_records_error() { assert!(result.is_err()); let spans = tel.spans(); let last = spans.last().unwrap(); - assert_annotated_span(last); + assert_annotated_span(last, &common::MYSQL_DIALECT); assert_error_span(last); // With shorthand (error path) @@ -1677,7 +1628,7 @@ async fn describe_records_error() { .await; assert!(result.is_err()); let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last); + assert_annotated_span(&last, &common::MYSQL_DIALECT); assert_error_span(&last); } @@ -2079,7 +2030,7 @@ async fn query_execute_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); assert!(attr(&spans[0], "db.response.affected_rows").is_some()); } @@ -2099,7 +2050,7 @@ async fn query_execute_many_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); } #[tokio::test] @@ -2116,7 +2067,7 @@ async fn query_fetch_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); assert_eq!( attr(&spans[0], "db.response.returned_rows"), Some(opentelemetry::Value::I64(2)) @@ -2138,7 +2089,7 @@ async fn query_fetch_many_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); } #[tokio::test] @@ -2156,7 +2107,7 @@ async fn query_fetch_all_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); assert_eq!( attr(&spans[0], "db.response.returned_rows"), Some(opentelemetry::Value::I64(3)) @@ -2177,7 +2128,7 @@ async fn query_fetch_one_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); assert_eq!( attr(&spans[0], "db.response.returned_rows"), Some(opentelemetry::Value::I64(1)) @@ -2206,7 +2157,7 @@ async fn query_fetch_optional_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); assert_eq!( attr(&spans[0], "db.response.returned_rows"), Some(opentelemetry::Value::I64(0)) @@ -2231,7 +2182,7 @@ async fn query_bind_first_then_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); } #[tokio::test] @@ -2252,7 +2203,7 @@ async fn query_annotations_first_then_bind_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); } #[tokio::test] @@ -2269,7 +2220,7 @@ async fn query_with_operation_shorthand_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); } #[tokio::test] @@ -2287,7 +2238,7 @@ async fn query_execute_with_annotations_via_connection() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); } #[tokio::test] @@ -2306,7 +2257,7 @@ async fn query_execute_with_annotations_via_transaction() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); } #[tokio::test] @@ -2323,7 +2274,7 @@ async fn query_execute_with_annotations_records_error() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); assert_error_span(&spans[0]); } @@ -2343,7 +2294,7 @@ async fn query_as_fetch_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); } #[tokio::test] @@ -2361,7 +2312,7 @@ async fn query_as_fetch_many_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); } #[tokio::test] @@ -2379,7 +2330,7 @@ async fn query_as_fetch_all_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); } #[tokio::test] @@ -2397,7 +2348,7 @@ async fn query_as_fetch_one_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); } #[tokio::test] @@ -2415,7 +2366,7 @@ async fn query_as_fetch_optional_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); } #[tokio::test] @@ -2432,7 +2383,7 @@ async fn query_as_fetch_one_with_annotations_records_error() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); assert_error_span(&spans[0]); } @@ -2452,7 +2403,7 @@ async fn query_scalar_fetch_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); } #[tokio::test] @@ -2470,7 +2421,7 @@ async fn query_scalar_fetch_many_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); } #[tokio::test] @@ -2488,7 +2439,7 @@ async fn query_scalar_fetch_all_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); } #[tokio::test] @@ -2506,7 +2457,7 @@ async fn query_scalar_fetch_one_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); } #[tokio::test] @@ -2524,7 +2475,7 @@ async fn query_scalar_fetch_optional_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); } // =========================================================================== @@ -2554,7 +2505,7 @@ async fn query_map_position_1_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); } #[tokio::test] @@ -2574,7 +2525,7 @@ async fn query_map_position_2_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); } #[tokio::test] @@ -2594,7 +2545,7 @@ async fn query_map_position_3_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); } #[tokio::test] @@ -2614,7 +2565,7 @@ async fn query_try_map_position_3_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); } // --- Per-method on Map (so each forwarder body is hit) -------------------- @@ -2634,7 +2585,7 @@ async fn map_fetch_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); assert_eq!( attr(&spans[0], "db.response.returned_rows"), Some(opentelemetry::Value::I64(2)) @@ -2657,7 +2608,7 @@ async fn map_fetch_many_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); } #[tokio::test] @@ -2676,7 +2627,7 @@ async fn map_fetch_all_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); } #[tokio::test] @@ -2695,7 +2646,7 @@ async fn map_fetch_one_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); } #[tokio::test] @@ -2714,7 +2665,7 @@ async fn map_fetch_optional_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); } // --- Composition (multi-map; both branches of step 4) -------------------- @@ -2736,7 +2687,7 @@ async fn map_compose_after_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); } #[tokio::test] @@ -2756,7 +2707,7 @@ async fn map_try_map_compose_after_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); } // --- Other executor receivers (smoke) ------------------------------------- @@ -2778,7 +2729,7 @@ async fn query_map_with_annotations_via_connection() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); } #[tokio::test] @@ -2799,7 +2750,7 @@ async fn query_map_with_annotations_via_transaction() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); } // --- Error paths ---------------------------------------------------------- @@ -2819,7 +2770,7 @@ async fn query_map_with_annotations_records_error() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); assert_error_span(&spans[0]); } @@ -2847,7 +2798,7 @@ async fn query_try_map_with_annotations_propagates_mapper_error() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); } // =========================================================================== @@ -2885,7 +2836,7 @@ async fn query_macro_execute_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); } #[tokio::test] @@ -2913,7 +2864,7 @@ async fn query_macro_fetch_one_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); } #[tokio::test] @@ -2946,7 +2897,7 @@ async fn query_macro_fetch_all_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); } #[tokio::test] @@ -2969,7 +2920,7 @@ async fn query_macro_fetch_optional_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); } type MacroUser = common::MacroUser; @@ -3003,7 +2954,7 @@ async fn query_as_macro_fetch_one_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); } #[tokio::test] @@ -3035,7 +2986,7 @@ async fn query_as_macro_fetch_all_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); } #[tokio::test] @@ -3062,7 +3013,7 @@ async fn query_as_macro_fetch_optional_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); } #[tokio::test] @@ -3089,7 +3040,7 @@ async fn query_scalar_macro_fetch_one_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); } #[tokio::test] @@ -3120,5 +3071,5 @@ async fn query_scalar_macro_fetch_all_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); } diff --git a/tests/postgres.rs b/tests/postgres.rs index 42efe96..afad9c2 100644 --- a/tests/postgres.rs +++ b/tests/postgres.rs @@ -5,7 +5,9 @@ mod common; use std::sync::OnceLock; use std::time::Duration; -use common::{assert_common_span_attributes, assert_error_span, attr}; +use common::{ + assert_annotated_span, assert_common_span_attributes, assert_error_span, attr, test_annotations, +}; use futures::StreamExt; use opentelemetry::trace::SpanKind; use serial_test::serial; @@ -62,31 +64,6 @@ async fn test_pool() -> Pool { PoolBuilder::from(raw).build() } -/// Standard annotations used across annotation assertions. -fn test_annotations() -> QueryAnnotations { - QueryAnnotations::new() - .operation("SELECT") - .collection("users") -} - -/// Assert that the span carries the standard annotation attributes. -fn assert_annotated_span(span: &opentelemetry_sdk::trace::SpanData) { - assert_eq!(span.span_kind, SpanKind::Client); - assert_eq!(span.name, "SELECT users"); - assert_eq!( - attr(span, "db.system.name"), - Some(opentelemetry::Value::String(SYSTEM.to_owned().into())), - ); - assert_eq!( - attr(span, "db.operation.name"), - Some(opentelemetry::Value::String("SELECT".into())), - ); - assert_eq!( - attr(span, "db.collection.name"), - Some(opentelemetry::Value::String("users".into())), - ); -} - // =========================================================================== // execute // =========================================================================== @@ -94,33 +71,7 @@ fn assert_annotated_span(span: &opentelemetry_sdk::trace::SpanData) { #[tokio::test] #[serial] async fn execute_creates_span_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - sqlx::query("CREATE TABLE IF NOT EXISTS exec_pool (id SERIAL PRIMARY KEY)") - .execute(&pool) - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - assert!(attr(&spans[0], "db.response.affected_rows").is_some()); - - // With annotations - pool.with_annotations(test_annotations()) - .execute("CREATE TABLE IF NOT EXISTS exec_pool (id SERIAL PRIMARY KEY)") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); - - // With shorthand - pool.with_operation("SELECT", "users") - .execute("CREATE TABLE IF NOT EXISTS exec_pool3 (id SERIAL PRIMARY KEY)") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + test_execute_creates_span_via_pool!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] @@ -146,14 +97,14 @@ async fn execute_creates_span_via_connection() { .execute("CREATE TABLE IF NOT EXISTS exec_conn (id SERIAL PRIMARY KEY)") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); // With shorthand conn.with_operation("SELECT", "users") .execute("CREATE TABLE IF NOT EXISTS exec_conn3 (id SERIAL PRIMARY KEY)") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); } #[tokio::test] @@ -186,7 +137,7 @@ async fn execute_creates_span_via_transaction() { assert_common_span_attributes(&spans[0], SYSTEM); assert!(attr(&spans[0], "db.response.returned_rows").is_none()); assert!(attr(&spans[0], "db.response.affected_rows").is_some()); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); } #[tokio::test] @@ -212,7 +163,7 @@ async fn execute_records_error() { assert!(result.is_err()); let spans = tel.spans(); let last = spans.last().unwrap(); - assert_annotated_span(last); + assert_annotated_span(last, &common::POSTGRES_DIALECT); assert_error_span(last); // With shorthand (error path) @@ -222,7 +173,7 @@ async fn execute_records_error() { .await; assert!(result.is_err()); let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last); + assert_annotated_span(&last, &common::POSTGRES_DIALECT); assert_error_span(&last); } @@ -353,7 +304,7 @@ async fn execute_many_via_pool() { .execute_many("SELECT 1; SELECT 2"); while stream.next().await.is_some() {} drop(stream); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); // With shorthand let mut stream = pool @@ -361,7 +312,7 @@ async fn execute_many_via_pool() { .execute_many("SELECT 1; SELECT 2"); while stream.next().await.is_some() {} drop(stream); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); } #[tokio::test] @@ -389,7 +340,7 @@ async fn execute_many_via_connection() { .execute_many("SELECT 1; SELECT 2"); while stream.next().await.is_some() {} drop(stream); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); // With shorthand let mut stream = conn @@ -397,7 +348,7 @@ async fn execute_many_via_connection() { .execute_many("SELECT 1; SELECT 2"); while stream.next().await.is_some() {} drop(stream); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); } #[tokio::test] @@ -434,7 +385,7 @@ async fn execute_many_via_transaction() { attr(&spans[0], "db.response.returned_rows"), Some(opentelemetry::Value::I64(0)) ); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); } #[tokio::test] @@ -466,7 +417,7 @@ async fn execute_many_records_error() { drop(stream); let spans = tel.spans(); let last = spans.last().unwrap(); - assert_annotated_span(last); + assert_annotated_span(last, &common::POSTGRES_DIALECT); assert_error_span(last); // With shorthand (error path) @@ -477,7 +428,7 @@ async fn execute_many_records_error() { assert!(result.is_some_and(|r| r.is_err())); drop(stream); let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last); + assert_annotated_span(&last, &common::POSTGRES_DIALECT); assert_error_span(&last); } @@ -513,7 +464,7 @@ async fn fetch_via_pool() { .fetch("SELECT 1 UNION ALL SELECT 2"); while stream.next().await.is_some() {} drop(stream); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); // With shorthand let mut stream = pool @@ -521,7 +472,7 @@ async fn fetch_via_pool() { .fetch("SELECT 1 UNION ALL SELECT 2"); while stream.next().await.is_some() {} drop(stream); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); } #[tokio::test] @@ -549,7 +500,7 @@ async fn fetch_via_connection() { .fetch("SELECT 1 UNION ALL SELECT 2"); while stream.next().await.is_some() {} drop(stream); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); // With shorthand let mut stream = conn @@ -557,7 +508,7 @@ async fn fetch_via_connection() { .fetch("SELECT 1 UNION ALL SELECT 2"); while stream.next().await.is_some() {} drop(stream); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); } #[tokio::test] @@ -594,7 +545,7 @@ async fn fetch_via_transaction() { attr(&spans[0], "db.response.returned_rows"), Some(opentelemetry::Value::I64(2)) ); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); } #[tokio::test] @@ -646,7 +597,7 @@ async fn fetch_stream_records_error() { drop(stream); let spans = tel.spans(); let last = spans.last().unwrap(); - assert_annotated_span(last); + assert_annotated_span(last, &common::POSTGRES_DIALECT); assert_error_span(last); // With shorthand (error path) @@ -655,7 +606,7 @@ async fn fetch_stream_records_error() { assert!(result.is_some_and(|r| r.is_err())); drop(stream); let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last); + assert_annotated_span(&last, &common::POSTGRES_DIALECT); assert_error_span(&last); } @@ -693,7 +644,7 @@ async fn fetch_many_via_pool() { .fetch_many("SELECT 1 UNION ALL SELECT 2"); while stream.next().await.is_some() {} drop(stream); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); // With shorthand let mut stream = pool @@ -701,7 +652,7 @@ async fn fetch_many_via_pool() { .fetch_many("SELECT 1 UNION ALL SELECT 2"); while stream.next().await.is_some() {} drop(stream); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); } #[tokio::test] @@ -729,7 +680,7 @@ async fn fetch_many_via_connection() { .fetch_many("SELECT 1 UNION ALL SELECT 2"); while stream.next().await.is_some() {} drop(stream); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); // With shorthand let mut stream = conn @@ -737,7 +688,7 @@ async fn fetch_many_via_connection() { .fetch_many("SELECT 1 UNION ALL SELECT 2"); while stream.next().await.is_some() {} drop(stream); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); } #[tokio::test] @@ -774,7 +725,7 @@ async fn fetch_many_via_transaction() { attr(&spans[0], "db.response.returned_rows"), Some(opentelemetry::Value::I64(2)) ); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); } #[tokio::test] @@ -826,7 +777,7 @@ async fn fetch_many_records_error() { drop(stream); let spans = tel.spans(); let last = spans.last().unwrap(); - assert_annotated_span(last); + assert_annotated_span(last, &common::POSTGRES_DIALECT); assert_error_span(last); // With shorthand (error path) @@ -837,7 +788,7 @@ async fn fetch_many_records_error() { assert!(result.is_some_and(|r| r.is_err())); drop(stream); let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last); + assert_annotated_span(&last, &common::POSTGRES_DIALECT); assert_error_span(&last); } @@ -870,14 +821,14 @@ async fn fetch_all_via_pool() { .fetch_all("SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); // With shorthand pool.with_operation("SELECT", "users") .fetch_all("SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); } #[tokio::test] @@ -906,14 +857,14 @@ async fn fetch_all_via_connection() { .fetch_all("SELECT 1 UNION ALL SELECT 2") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); // With shorthand conn.with_operation("SELECT", "users") .fetch_all("SELECT 1 UNION ALL SELECT 2") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); } #[tokio::test] @@ -949,7 +900,7 @@ async fn fetch_all_via_transaction() { attr(&spans[0], "db.response.returned_rows"), Some(opentelemetry::Value::I64(2)) ); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); } #[tokio::test] @@ -975,7 +926,7 @@ async fn fetch_all_records_error() { assert!(result.is_err()); let spans = tel.spans(); let last = spans.last().unwrap(); - assert_annotated_span(last); + assert_annotated_span(last, &common::POSTGRES_DIALECT); assert_error_span(last); // With shorthand (error path) @@ -985,7 +936,7 @@ async fn fetch_all_records_error() { .await; assert!(result.is_err()); let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last); + assert_annotated_span(&last, &common::POSTGRES_DIALECT); assert_error_span(&last); } @@ -1014,14 +965,14 @@ async fn fetch_one_via_pool() { .fetch_one("SELECT 1") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); // With shorthand pool.with_operation("SELECT", "users") .fetch_one("SELECT 1") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); } #[tokio::test] @@ -1046,14 +997,14 @@ async fn fetch_one_via_connection() { .fetch_one("SELECT 1") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); // With shorthand conn.with_operation("SELECT", "users") .fetch_one("SELECT 1") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); } #[tokio::test] @@ -1085,7 +1036,7 @@ async fn fetch_one_via_transaction() { attr(&spans[0], "db.response.returned_rows"), Some(opentelemetry::Value::I64(1)) ); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); } #[tokio::test] @@ -1111,7 +1062,7 @@ async fn fetch_one_records_error() { assert!(result.is_err()); let spans = tel.spans(); let last = spans.last().unwrap(); - assert_annotated_span(last); + assert_annotated_span(last, &common::POSTGRES_DIALECT); assert_error_span(last); // With shorthand (error path) @@ -1121,7 +1072,7 @@ async fn fetch_one_records_error() { .await; assert!(result.is_err()); let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last); + assert_annotated_span(&last, &common::POSTGRES_DIALECT); assert_error_span(&last); } @@ -1151,14 +1102,14 @@ async fn fetch_optional_records_one_row() { .fetch_optional("SELECT 1") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); // With shorthand pool.with_operation("SELECT", "users") .fetch_optional("SELECT 1") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); } #[tokio::test] @@ -1215,14 +1166,14 @@ async fn fetch_optional_via_connection() { .fetch_optional("SELECT 42") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); // With shorthand conn.with_operation("SELECT", "users") .fetch_optional("SELECT 42") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); } #[tokio::test] @@ -1255,7 +1206,7 @@ async fn fetch_optional_via_transaction() { attr(&spans[0], "db.response.returned_rows"), Some(opentelemetry::Value::I64(1)) ); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); } #[tokio::test] @@ -1281,7 +1232,7 @@ async fn fetch_optional_records_error() { assert!(result.is_err()); let spans = tel.spans(); let last = spans.last().unwrap(); - assert_annotated_span(last); + assert_annotated_span(last, &common::POSTGRES_DIALECT); assert_error_span(last); // With shorthand (error path) @@ -1291,7 +1242,7 @@ async fn fetch_optional_records_error() { .await; assert!(result.is_err()); let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last); + assert_annotated_span(&last, &common::POSTGRES_DIALECT); assert_error_span(&last); } @@ -1317,14 +1268,14 @@ async fn prepare_via_pool() { .prepare("SELECT 1") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); // With shorthand pool.with_operation("SELECT", "users") .prepare("SELECT 1") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); } #[tokio::test] @@ -1346,14 +1297,14 @@ async fn prepare_via_connection() { .prepare("SELECT 1") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); // With shorthand conn.with_operation("SELECT", "users") .prepare("SELECT 1") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); } #[tokio::test] @@ -1382,7 +1333,7 @@ async fn prepare_via_transaction() { assert_eq!(spans.len(), 3); assert_common_span_attributes(&spans[0], SYSTEM); assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); } #[tokio::test] @@ -1409,7 +1360,7 @@ async fn prepare_records_error() { assert!(result.is_err()); let spans = tel.spans(); let last = spans.last().unwrap(); - assert_annotated_span(last); + assert_annotated_span(last, &common::POSTGRES_DIALECT); assert_error_span(last); // With shorthand (error path) @@ -1419,7 +1370,7 @@ async fn prepare_records_error() { .await; assert!(result.is_err()); let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last); + assert_annotated_span(&last, &common::POSTGRES_DIALECT); assert_error_span(&last); } @@ -1445,14 +1396,14 @@ async fn prepare_with_via_pool() { .prepare_with("SELECT $1", &[]) .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); // With shorthand pool.with_operation("SELECT", "users") .prepare_with("SELECT $1", &[]) .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); } #[tokio::test] @@ -1474,14 +1425,14 @@ async fn prepare_with_via_connection() { .prepare_with("SELECT $1", &[]) .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); // With shorthand conn.with_operation("SELECT", "users") .prepare_with("SELECT $1", &[]) .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); } #[tokio::test] @@ -1510,7 +1461,7 @@ async fn prepare_with_via_transaction() { assert_eq!(spans.len(), 3); assert_common_span_attributes(&spans[0], SYSTEM); assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); } #[tokio::test] @@ -1537,7 +1488,7 @@ async fn prepare_with_records_error() { assert!(result.is_err()); let spans = tel.spans(); let last = spans.last().unwrap(); - assert_annotated_span(last); + assert_annotated_span(last, &common::POSTGRES_DIALECT); assert_error_span(last); // With shorthand (error path) @@ -1547,7 +1498,7 @@ async fn prepare_with_records_error() { .await; assert!(result.is_err()); let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last); + assert_annotated_span(&last, &common::POSTGRES_DIALECT); assert_error_span(&last); } @@ -1573,14 +1524,14 @@ async fn describe_via_pool() { .describe("SELECT 1") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); // With shorthand pool.with_operation("SELECT", "users") .describe("SELECT 1") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); } #[tokio::test] @@ -1602,14 +1553,14 @@ async fn describe_via_connection() { .describe("SELECT 1") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); // With shorthand conn.with_operation("SELECT", "users") .describe("SELECT 1") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); } #[tokio::test] @@ -1638,7 +1589,7 @@ async fn describe_via_transaction() { assert_eq!(spans.len(), 3); assert_common_span_attributes(&spans[0], SYSTEM); assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); } #[tokio::test] @@ -1665,7 +1616,7 @@ async fn describe_records_error() { assert!(result.is_err()); let spans = tel.spans(); let last = spans.last().unwrap(); - assert_annotated_span(last); + assert_annotated_span(last, &common::POSTGRES_DIALECT); assert_error_span(last); // With shorthand (error path) @@ -1675,7 +1626,7 @@ async fn describe_records_error() { .await; assert!(result.is_err()); let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last); + assert_annotated_span(&last, &common::POSTGRES_DIALECT); assert_error_span(&last); } @@ -2077,7 +2028,7 @@ async fn query_execute_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); assert!(attr(&spans[0], "db.response.affected_rows").is_some()); } @@ -2097,7 +2048,7 @@ async fn query_execute_many_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); } #[tokio::test] @@ -2114,7 +2065,7 @@ async fn query_fetch_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); assert_eq!( attr(&spans[0], "db.response.returned_rows"), Some(opentelemetry::Value::I64(2)) @@ -2136,7 +2087,7 @@ async fn query_fetch_many_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); } #[tokio::test] @@ -2154,7 +2105,7 @@ async fn query_fetch_all_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); assert_eq!( attr(&spans[0], "db.response.returned_rows"), Some(opentelemetry::Value::I64(3)) @@ -2175,7 +2126,7 @@ async fn query_fetch_one_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); assert_eq!( attr(&spans[0], "db.response.returned_rows"), Some(opentelemetry::Value::I64(1)) @@ -2204,7 +2155,7 @@ async fn query_fetch_optional_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); assert_eq!( attr(&spans[0], "db.response.returned_rows"), Some(opentelemetry::Value::I64(0)) @@ -2229,7 +2180,7 @@ async fn query_bind_first_then_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); } #[tokio::test] @@ -2250,7 +2201,7 @@ async fn query_annotations_first_then_bind_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); } #[tokio::test] @@ -2267,7 +2218,7 @@ async fn query_with_operation_shorthand_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); } #[tokio::test] @@ -2285,7 +2236,7 @@ async fn query_execute_with_annotations_via_connection() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); } #[tokio::test] @@ -2304,7 +2255,7 @@ async fn query_execute_with_annotations_via_transaction() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); } #[tokio::test] @@ -2321,7 +2272,7 @@ async fn query_execute_with_annotations_records_error() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); assert_error_span(&spans[0]); } @@ -2341,7 +2292,7 @@ async fn query_as_fetch_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); } #[tokio::test] @@ -2359,7 +2310,7 @@ async fn query_as_fetch_many_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); } #[tokio::test] @@ -2377,7 +2328,7 @@ async fn query_as_fetch_all_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); } #[tokio::test] @@ -2395,7 +2346,7 @@ async fn query_as_fetch_one_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); } #[tokio::test] @@ -2413,7 +2364,7 @@ async fn query_as_fetch_optional_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); } #[tokio::test] @@ -2430,7 +2381,7 @@ async fn query_as_fetch_one_with_annotations_records_error() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); assert_error_span(&spans[0]); } @@ -2450,7 +2401,7 @@ async fn query_scalar_fetch_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); } #[tokio::test] @@ -2468,7 +2419,7 @@ async fn query_scalar_fetch_many_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); } #[tokio::test] @@ -2486,7 +2437,7 @@ async fn query_scalar_fetch_all_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); } #[tokio::test] @@ -2504,7 +2455,7 @@ async fn query_scalar_fetch_one_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); } #[tokio::test] @@ -2522,7 +2473,7 @@ async fn query_scalar_fetch_optional_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); } // =========================================================================== @@ -2548,7 +2499,7 @@ async fn query_map_position_1_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); } #[tokio::test] @@ -2568,7 +2519,7 @@ async fn query_map_position_2_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); } #[tokio::test] @@ -2588,7 +2539,7 @@ async fn query_map_position_3_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); } #[tokio::test] @@ -2608,7 +2559,7 @@ async fn query_try_map_position_3_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); } // --- Per-method on Map (so each forwarder body is hit) -------------------- @@ -2628,7 +2579,7 @@ async fn map_fetch_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); assert_eq!( attr(&spans[0], "db.response.returned_rows"), Some(opentelemetry::Value::I64(2)) @@ -2651,7 +2602,7 @@ async fn map_fetch_many_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); } #[tokio::test] @@ -2671,7 +2622,7 @@ async fn map_fetch_all_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); } #[tokio::test] @@ -2690,7 +2641,7 @@ async fn map_fetch_one_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); } #[tokio::test] @@ -2709,7 +2660,7 @@ async fn map_fetch_optional_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); } // --- Composition (multi-map; both branches of step 4) -------------------- @@ -2731,7 +2682,7 @@ async fn map_compose_after_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); } #[tokio::test] @@ -2751,7 +2702,7 @@ async fn map_try_map_compose_after_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); } // --- Other executor receivers (smoke) ------------------------------------- @@ -2773,7 +2724,7 @@ async fn query_map_with_annotations_via_connection() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); } #[tokio::test] @@ -2794,7 +2745,7 @@ async fn query_map_with_annotations_via_transaction() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); } // --- Error paths ---------------------------------------------------------- @@ -2814,7 +2765,7 @@ async fn query_map_with_annotations_records_error() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); assert_error_span(&spans[0]); } @@ -2842,7 +2793,7 @@ async fn query_try_map_with_annotations_propagates_mapper_error() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); } // =========================================================================== @@ -2880,7 +2831,7 @@ async fn query_macro_execute_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); } #[tokio::test] @@ -2910,7 +2861,7 @@ async fn query_macro_fetch_one_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); } #[tokio::test] @@ -2943,7 +2894,7 @@ async fn query_macro_fetch_all_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); } #[tokio::test] @@ -2966,7 +2917,7 @@ async fn query_macro_fetch_optional_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); } type MacroUser = common::MacroUser; @@ -3002,7 +2953,7 @@ async fn query_as_macro_fetch_one_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); } #[tokio::test] @@ -3034,7 +2985,7 @@ async fn query_as_macro_fetch_all_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); } #[tokio::test] @@ -3061,7 +3012,7 @@ async fn query_as_macro_fetch_optional_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); } #[tokio::test] @@ -3090,7 +3041,7 @@ async fn query_scalar_macro_fetch_one_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); } #[tokio::test] @@ -3121,5 +3072,5 @@ async fn query_scalar_macro_fetch_all_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); } diff --git a/tests/sqlite.rs b/tests/sqlite.rs index a3b5873..8d9c823 100644 --- a/tests/sqlite.rs +++ b/tests/sqlite.rs @@ -2,7 +2,9 @@ mod common; -use common::{assert_common_span_attributes, assert_error_span, attr}; +use common::{ + assert_annotated_span, assert_common_span_attributes, assert_error_span, attr, test_annotations, +}; use futures::StreamExt; use opentelemetry::trace::SpanKind; use serial_test::serial; @@ -19,32 +21,6 @@ async fn test_pool() -> Pool { PoolBuilder::from(raw).build() } -/// Standard annotations used across most annotation tests. -fn test_annotations() -> QueryAnnotations { - QueryAnnotations::new() - .operation("SELECT") - .collection("users") -} - -/// Assert that the span carries the standard annotation attributes set by -/// [`test_annotations`]. -fn assert_annotated_span(span: &opentelemetry_sdk::trace::SpanData) { - assert_eq!(span.span_kind, SpanKind::Client); - assert_eq!(span.name, "SELECT users"); - assert_eq!( - attr(span, "db.system.name"), - Some(opentelemetry::Value::String(SYSTEM.to_owned().into())), - ); - assert_eq!( - attr(span, "db.operation.name"), - Some(opentelemetry::Value::String("SELECT".into())), - ); - assert_eq!( - attr(span, "db.collection.name"), - Some(opentelemetry::Value::String("users".into())), - ); -} - // =========================================================================== // execute // =========================================================================== @@ -52,33 +28,7 @@ fn assert_annotated_span(span: &opentelemetry_sdk::trace::SpanData) { #[tokio::test] #[serial] async fn execute_creates_span_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - (&pool) - .execute("CREATE TABLE exec_pool (id INTEGER PRIMARY KEY)") - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - assert!(attr(&spans[0], "db.response.affected_rows").is_some()); - - // With annotations - pool.with_annotations(test_annotations()) - .execute("CREATE TABLE exec_pool2 (id INTEGER PRIMARY KEY)") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); - - // With shorthand - pool.with_operation("SELECT", "users") - .execute("CREATE TABLE exec_pool3 (id INTEGER PRIMARY KEY)") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + test_execute_creates_span_via_pool!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] @@ -104,14 +54,14 @@ async fn execute_creates_span_via_connection() { .execute("CREATE TABLE exec_conn2 (id INTEGER PRIMARY KEY)") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); // With shorthand conn.with_operation("SELECT", "users") .execute("CREATE TABLE exec_conn3 (id INTEGER PRIMARY KEY)") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); } #[tokio::test] @@ -145,8 +95,8 @@ async fn execute_creates_span_via_transaction() { assert_common_span_attributes(&spans[0], SYSTEM); assert!(attr(&spans[0], "db.response.returned_rows").is_none()); assert!(attr(&spans[0], "db.response.affected_rows").is_some()); - assert_annotated_span(&spans[1]); - assert_annotated_span(&spans[2]); + assert_annotated_span(&spans[1], &common::SQLITE_DIALECT); + assert_annotated_span(&spans[2], &common::SQLITE_DIALECT); } #[tokio::test] @@ -260,7 +210,7 @@ async fn execute_records_error() { .await; assert!(result.is_err()); let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last); + assert_annotated_span(&last, &common::SQLITE_DIALECT); assert_error_span(&last); // With shorthand (error path) @@ -270,7 +220,7 @@ async fn execute_records_error() { .await; assert!(result.is_err()); let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last); + assert_annotated_span(&last, &common::SQLITE_DIALECT); assert_error_span(&last); } @@ -302,7 +252,7 @@ async fn execute_many_via_pool() { .execute_many("SELECT 1; SELECT 2"); while stream.next().await.is_some() {} drop(stream); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); // With shorthand let mut stream = pool @@ -310,7 +260,7 @@ async fn execute_many_via_pool() { .execute_many("SELECT 1; SELECT 2"); while stream.next().await.is_some() {} drop(stream); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); } #[tokio::test] @@ -338,7 +288,7 @@ async fn execute_many_via_connection() { .execute_many("SELECT 1; SELECT 2"); while stream.next().await.is_some() {} drop(stream); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); // With shorthand let mut stream = conn @@ -346,7 +296,7 @@ async fn execute_many_via_connection() { .execute_many("SELECT 1; SELECT 2"); while stream.next().await.is_some() {} drop(stream); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); } #[tokio::test] @@ -383,8 +333,8 @@ async fn execute_many_via_transaction() { attr(&spans[0], "db.response.returned_rows"), Some(opentelemetry::Value::I64(0)) ); - assert_annotated_span(&spans[1]); - assert_annotated_span(&spans[2]); + assert_annotated_span(&spans[1], &common::SQLITE_DIALECT); + assert_annotated_span(&spans[2], &common::SQLITE_DIALECT); } #[tokio::test] @@ -415,7 +365,7 @@ async fn execute_many_records_error() { assert!(result.is_some_and(|r| r.is_err())); drop(stream); let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last); + assert_annotated_span(&last, &common::SQLITE_DIALECT); assert_error_span(&last); // With shorthand (error path) @@ -426,7 +376,7 @@ async fn execute_many_records_error() { assert!(result.is_some_and(|r| r.is_err())); drop(stream); let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last); + assert_annotated_span(&last, &common::SQLITE_DIALECT); assert_error_span(&last); } @@ -462,7 +412,7 @@ async fn fetch_via_pool() { .fetch("SELECT 1 UNION ALL SELECT 2"); while stream.next().await.is_some() {} drop(stream); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); // With shorthand let mut stream = pool @@ -470,7 +420,7 @@ async fn fetch_via_pool() { .fetch("SELECT 1 UNION ALL SELECT 2"); while stream.next().await.is_some() {} drop(stream); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); } #[tokio::test] @@ -498,7 +448,7 @@ async fn fetch_via_connection() { .fetch("SELECT 1 UNION ALL SELECT 2"); while stream.next().await.is_some() {} drop(stream); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); // With shorthand let mut stream = conn @@ -506,7 +456,7 @@ async fn fetch_via_connection() { .fetch("SELECT 1 UNION ALL SELECT 2"); while stream.next().await.is_some() {} drop(stream); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); } #[tokio::test] @@ -543,8 +493,8 @@ async fn fetch_via_transaction() { attr(&spans[0], "db.response.returned_rows"), Some(opentelemetry::Value::I64(2)) ); - assert_annotated_span(&spans[1]); - assert_annotated_span(&spans[2]); + assert_annotated_span(&spans[1], &common::SQLITE_DIALECT); + assert_annotated_span(&spans[2], &common::SQLITE_DIALECT); } #[tokio::test] @@ -599,7 +549,7 @@ async fn fetch_stream_records_error() { assert!(result.is_some_and(|r| r.is_err())); drop(stream); let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last); + assert_annotated_span(&last, &common::SQLITE_DIALECT); assert_error_span(&last); // With shorthand (error path) @@ -608,7 +558,7 @@ async fn fetch_stream_records_error() { assert!(result.is_some_and(|r| r.is_err())); drop(stream); let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last); + assert_annotated_span(&last, &common::SQLITE_DIALECT); assert_error_span(&last); } @@ -650,7 +600,7 @@ async fn fetch_many_via_pool() { .fetch_many("SELECT 1 UNION ALL SELECT 2"); while stream.next().await.is_some() {} drop(stream); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); // With shorthand let mut stream = pool @@ -658,7 +608,7 @@ async fn fetch_many_via_pool() { .fetch_many("SELECT 1 UNION ALL SELECT 2"); while stream.next().await.is_some() {} drop(stream); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); } #[tokio::test] @@ -686,7 +636,7 @@ async fn fetch_many_via_connection() { .fetch_many("SELECT 1 UNION ALL SELECT 2"); while stream.next().await.is_some() {} drop(stream); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); // With shorthand let mut stream = conn @@ -694,7 +644,7 @@ async fn fetch_many_via_connection() { .fetch_many("SELECT 1 UNION ALL SELECT 2"); while stream.next().await.is_some() {} drop(stream); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); } #[tokio::test] @@ -731,8 +681,8 @@ async fn fetch_many_via_transaction() { attr(&spans[0], "db.response.returned_rows"), Some(opentelemetry::Value::I64(2)) ); - assert_annotated_span(&spans[1]); - assert_annotated_span(&spans[2]); + assert_annotated_span(&spans[1], &common::SQLITE_DIALECT); + assert_annotated_span(&spans[2], &common::SQLITE_DIALECT); } #[tokio::test] @@ -787,7 +737,7 @@ async fn fetch_many_records_error() { assert!(result.is_some_and(|r| r.is_err())); drop(stream); let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last); + assert_annotated_span(&last, &common::SQLITE_DIALECT); assert_error_span(&last); // With shorthand (error path) @@ -798,7 +748,7 @@ async fn fetch_many_records_error() { assert!(result.is_some_and(|r| r.is_err())); drop(stream); let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last); + assert_annotated_span(&last, &common::SQLITE_DIALECT); assert_error_span(&last); } @@ -831,14 +781,14 @@ async fn fetch_all_records_row_count() { .fetch_all("SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); // With shorthand pool.with_operation("SELECT", "users") .fetch_all("SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); } #[tokio::test] @@ -867,14 +817,14 @@ async fn fetch_all_via_connection() { .fetch_all("SELECT 1 UNION ALL SELECT 2") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); // With shorthand conn.with_operation("SELECT", "users") .fetch_all("SELECT 1 UNION ALL SELECT 2") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); } #[tokio::test] @@ -911,8 +861,8 @@ async fn fetch_all_via_transaction() { attr(&spans[0], "db.response.returned_rows"), Some(opentelemetry::Value::I64(2)) ); - assert_annotated_span(&spans[1]); - assert_annotated_span(&spans[2]); + assert_annotated_span(&spans[1], &common::SQLITE_DIALECT); + assert_annotated_span(&spans[2], &common::SQLITE_DIALECT); } #[tokio::test] @@ -937,7 +887,7 @@ async fn fetch_all_records_error() { .await; assert!(result.is_err()); let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last); + assert_annotated_span(&last, &common::SQLITE_DIALECT); assert_error_span(&last); // With shorthand (error path) @@ -947,7 +897,7 @@ async fn fetch_all_records_error() { .await; assert!(result.is_err()); let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last); + assert_annotated_span(&last, &common::SQLITE_DIALECT); assert_error_span(&last); } @@ -976,14 +926,14 @@ async fn fetch_one_via_pool() { .fetch_one("SELECT 1") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); // With shorthand pool.with_operation("SELECT", "users") .fetch_one("SELECT 1") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); } #[tokio::test] @@ -1008,14 +958,14 @@ async fn fetch_one_via_connection() { .fetch_one("SELECT 1") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); // With shorthand conn.with_operation("SELECT", "users") .fetch_one("SELECT 1") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); } #[tokio::test] @@ -1048,8 +998,8 @@ async fn fetch_one_via_transaction() { attr(&spans[0], "db.response.returned_rows"), Some(opentelemetry::Value::I64(1)) ); - assert_annotated_span(&spans[1]); - assert_annotated_span(&spans[2]); + assert_annotated_span(&spans[1], &common::SQLITE_DIALECT); + assert_annotated_span(&spans[2], &common::SQLITE_DIALECT); } #[tokio::test] @@ -1074,7 +1024,7 @@ async fn fetch_one_records_error() { .await; assert!(result.is_err()); let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last); + assert_annotated_span(&last, &common::SQLITE_DIALECT); assert_error_span(&last); // With shorthand (error path) @@ -1084,7 +1034,7 @@ async fn fetch_one_records_error() { .await; assert!(result.is_err()); let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last); + assert_annotated_span(&last, &common::SQLITE_DIALECT); assert_error_span(&last); } @@ -1114,14 +1064,14 @@ async fn fetch_optional_records_one_row() { .fetch_optional("SELECT 1") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); // With shorthand pool.with_operation("SELECT", "users") .fetch_optional("SELECT 1") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); } #[tokio::test] @@ -1177,14 +1127,14 @@ async fn fetch_optional_via_connection() { .fetch_optional("SELECT 42") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); // With shorthand conn.with_operation("SELECT", "users") .fetch_optional("SELECT 42") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); } #[tokio::test] @@ -1218,8 +1168,8 @@ async fn fetch_optional_via_transaction() { attr(&spans[0], "db.response.returned_rows"), Some(opentelemetry::Value::I64(1)) ); - assert_annotated_span(&spans[1]); - assert_annotated_span(&spans[2]); + assert_annotated_span(&spans[1], &common::SQLITE_DIALECT); + assert_annotated_span(&spans[2], &common::SQLITE_DIALECT); } #[tokio::test] @@ -1244,7 +1194,7 @@ async fn fetch_optional_records_error() { .await; assert!(result.is_err()); let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last); + assert_annotated_span(&last, &common::SQLITE_DIALECT); assert_error_span(&last); // With shorthand (error path) @@ -1254,7 +1204,7 @@ async fn fetch_optional_records_error() { .await; assert!(result.is_err()); let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last); + assert_annotated_span(&last, &common::SQLITE_DIALECT); assert_error_span(&last); } @@ -1280,14 +1230,14 @@ async fn prepare_via_pool() { .prepare("SELECT 1") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); // With shorthand pool.with_operation("SELECT", "users") .prepare("SELECT 1") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); } #[tokio::test] @@ -1309,14 +1259,14 @@ async fn prepare_via_connection() { .prepare("SELECT 1") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); // With shorthand conn.with_operation("SELECT", "users") .prepare("SELECT 1") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); } #[tokio::test] @@ -1346,8 +1296,8 @@ async fn prepare_via_transaction() { assert_eq!(spans.len(), 3); assert_common_span_attributes(&spans[0], SYSTEM); assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - assert_annotated_span(&spans[1]); - assert_annotated_span(&spans[2]); + assert_annotated_span(&spans[1], &common::SQLITE_DIALECT); + assert_annotated_span(&spans[2], &common::SQLITE_DIALECT); } #[tokio::test] @@ -1373,7 +1323,7 @@ async fn prepare_records_error() { .await; assert!(result.is_err()); let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last); + assert_annotated_span(&last, &common::SQLITE_DIALECT); assert_error_span(&last); // With shorthand (error path) @@ -1383,7 +1333,7 @@ async fn prepare_records_error() { .await; assert!(result.is_err()); let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last); + assert_annotated_span(&last, &common::SQLITE_DIALECT); assert_error_span(&last); } @@ -1409,14 +1359,14 @@ async fn prepare_with_via_pool() { .prepare_with("SELECT ?", &[]) .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); // With shorthand pool.with_operation("SELECT", "users") .prepare_with("SELECT ?", &[]) .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); } #[tokio::test] @@ -1438,14 +1388,14 @@ async fn prepare_with_via_connection() { .prepare_with("SELECT ?", &[]) .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); // With shorthand conn.with_operation("SELECT", "users") .prepare_with("SELECT ?", &[]) .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); } #[tokio::test] @@ -1475,8 +1425,8 @@ async fn prepare_with_via_transaction() { assert_eq!(spans.len(), 3); assert_common_span_attributes(&spans[0], SYSTEM); assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - assert_annotated_span(&spans[1]); - assert_annotated_span(&spans[2]); + assert_annotated_span(&spans[1], &common::SQLITE_DIALECT); + assert_annotated_span(&spans[2], &common::SQLITE_DIALECT); } #[tokio::test] @@ -1502,7 +1452,7 @@ async fn prepare_with_records_error() { .await; assert!(result.is_err()); let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last); + assert_annotated_span(&last, &common::SQLITE_DIALECT); assert_error_span(&last); // With shorthand (error path) @@ -1512,7 +1462,7 @@ async fn prepare_with_records_error() { .await; assert!(result.is_err()); let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last); + assert_annotated_span(&last, &common::SQLITE_DIALECT); assert_error_span(&last); } @@ -1538,14 +1488,14 @@ async fn describe_via_pool() { .describe("SELECT 1") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); // With shorthand pool.with_operation("SELECT", "users") .describe("SELECT 1") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); } #[tokio::test] @@ -1567,14 +1517,14 @@ async fn describe_via_connection() { .describe("SELECT 1") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); // With shorthand conn.with_operation("SELECT", "users") .describe("SELECT 1") .await .unwrap(); - assert_annotated_span(tel.spans().last().unwrap()); + assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); } #[tokio::test] @@ -1604,8 +1554,8 @@ async fn describe_via_transaction() { assert_eq!(spans.len(), 3); assert_common_span_attributes(&spans[0], SYSTEM); assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - assert_annotated_span(&spans[1]); - assert_annotated_span(&spans[2]); + assert_annotated_span(&spans[1], &common::SQLITE_DIALECT); + assert_annotated_span(&spans[2], &common::SQLITE_DIALECT); } #[tokio::test] @@ -1631,7 +1581,7 @@ async fn describe_records_error() { .await; assert!(result.is_err()); let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last); + assert_annotated_span(&last, &common::SQLITE_DIALECT); assert_error_span(&last); // With shorthand (error path) @@ -1641,7 +1591,7 @@ async fn describe_records_error() { .await; assert!(result.is_err()); let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last); + assert_annotated_span(&last, &common::SQLITE_DIALECT); assert_error_span(&last); } @@ -1974,7 +1924,7 @@ async fn query_execute_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); assert!(attr(&spans[0], "db.response.affected_rows").is_some()); } @@ -1994,7 +1944,7 @@ async fn query_execute_many_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); } #[tokio::test] @@ -2011,7 +1961,7 @@ async fn query_fetch_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); assert_eq!( attr(&spans[0], "db.response.returned_rows"), Some(opentelemetry::Value::I64(2)) @@ -2033,7 +1983,7 @@ async fn query_fetch_many_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); } #[tokio::test] @@ -2051,7 +2001,7 @@ async fn query_fetch_all_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); assert_eq!( attr(&spans[0], "db.response.returned_rows"), Some(opentelemetry::Value::I64(3)) @@ -2072,7 +2022,7 @@ async fn query_fetch_one_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); assert_eq!( attr(&spans[0], "db.response.returned_rows"), Some(opentelemetry::Value::I64(1)) @@ -2101,7 +2051,7 @@ async fn query_fetch_optional_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); assert_eq!( attr(&spans[0], "db.response.returned_rows"), Some(opentelemetry::Value::I64(0)) @@ -2126,7 +2076,7 @@ async fn query_bind_first_then_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); } #[tokio::test] @@ -2147,7 +2097,7 @@ async fn query_annotations_first_then_bind_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); } #[tokio::test] @@ -2164,7 +2114,7 @@ async fn query_with_operation_shorthand_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); } #[tokio::test] @@ -2182,7 +2132,7 @@ async fn query_execute_with_annotations_via_connection() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); } #[tokio::test] @@ -2201,7 +2151,7 @@ async fn query_execute_with_annotations_via_transaction() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); } #[tokio::test] @@ -2218,7 +2168,7 @@ async fn query_execute_with_annotations_records_error() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); assert_error_span(&spans[0]); } @@ -2238,7 +2188,7 @@ async fn query_as_fetch_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); } #[tokio::test] @@ -2256,7 +2206,7 @@ async fn query_as_fetch_many_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); } #[tokio::test] @@ -2274,7 +2224,7 @@ async fn query_as_fetch_all_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); } #[tokio::test] @@ -2292,7 +2242,7 @@ async fn query_as_fetch_one_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); } #[tokio::test] @@ -2310,7 +2260,7 @@ async fn query_as_fetch_optional_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); } #[tokio::test] @@ -2327,7 +2277,7 @@ async fn query_as_fetch_one_with_annotations_records_error() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); assert_error_span(&spans[0]); } @@ -2347,7 +2297,7 @@ async fn query_scalar_fetch_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); } #[tokio::test] @@ -2365,7 +2315,7 @@ async fn query_scalar_fetch_many_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); } #[tokio::test] @@ -2383,7 +2333,7 @@ async fn query_scalar_fetch_all_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); } #[tokio::test] @@ -2401,7 +2351,7 @@ async fn query_scalar_fetch_one_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); } #[tokio::test] @@ -2419,7 +2369,7 @@ async fn query_scalar_fetch_optional_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); } // =========================================================================== @@ -2445,7 +2395,7 @@ async fn query_map_position_1_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); } #[tokio::test] @@ -2465,7 +2415,7 @@ async fn query_map_position_2_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); } #[tokio::test] @@ -2485,7 +2435,7 @@ async fn query_map_position_3_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); } #[tokio::test] @@ -2505,7 +2455,7 @@ async fn query_try_map_position_3_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); } // --- Per-method on Map (so each forwarder body is hit) -------------------- @@ -2525,7 +2475,7 @@ async fn map_fetch_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); assert_eq!( attr(&spans[0], "db.response.returned_rows"), Some(opentelemetry::Value::I64(2)) @@ -2548,7 +2498,7 @@ async fn map_fetch_many_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); } #[tokio::test] @@ -2567,7 +2517,7 @@ async fn map_fetch_all_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); } #[tokio::test] @@ -2586,7 +2536,7 @@ async fn map_fetch_one_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); } #[tokio::test] @@ -2605,7 +2555,7 @@ async fn map_fetch_optional_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); } // --- Composition (multi-map; both branches of step 4) -------------------- @@ -2627,7 +2577,7 @@ async fn map_compose_after_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); } #[tokio::test] @@ -2647,7 +2597,7 @@ async fn map_try_map_compose_after_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); } // --- Other executor receivers (smoke) ------------------------------------- @@ -2669,7 +2619,7 @@ async fn query_map_with_annotations_via_connection() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); } #[tokio::test] @@ -2690,7 +2640,7 @@ async fn query_map_with_annotations_via_transaction() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); } // --- Error paths ---------------------------------------------------------- @@ -2710,7 +2660,7 @@ async fn query_map_with_annotations_records_error() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); assert_error_span(&spans[0]); } @@ -2739,7 +2689,7 @@ async fn query_try_map_with_annotations_propagates_mapper_error() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); } // =========================================================================== @@ -2779,7 +2729,7 @@ async fn query_macro_execute_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); } #[tokio::test] @@ -2807,7 +2757,7 @@ async fn query_macro_fetch_one_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); } #[tokio::test] @@ -2838,7 +2788,7 @@ async fn query_macro_fetch_all_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); } #[tokio::test] @@ -2861,7 +2811,7 @@ async fn query_macro_fetch_optional_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); } type MacroUser = common::MacroUser; @@ -2895,7 +2845,7 @@ async fn query_as_macro_fetch_one_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); } #[tokio::test] @@ -2927,7 +2877,7 @@ async fn query_as_macro_fetch_all_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); } #[tokio::test] @@ -2954,7 +2904,7 @@ async fn query_as_macro_fetch_optional_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); } #[tokio::test] @@ -2981,7 +2931,7 @@ async fn query_scalar_macro_fetch_one_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); } #[tokio::test] @@ -3012,7 +2962,7 @@ async fn query_scalar_macro_fetch_all_with_annotations_via_pool() { let spans = tel.spans(); assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0]); + assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); } // =========================================================================== From 891a0caa45752ebf671a12060d834e024c5c85ab Mon Sep 17 00:00:00 2001 From: Borislav Borisov Date: Tue, 28 Apr 2026 09:08:23 +0100 Subject: [PATCH 2/4] test: Parameterise truly portable backend tests via shared macros Move 75 backend-portable tests into shared `macro_rules!` macros in tests/common.rs and replace each backend file's body with a one-line wrapper. Categories migrated: execute / execute_many / fetch / fetch_many / fetch_all / fetch_one / fetch_optional (portable subsets), prepare / prepare_with / describe, query_* / query_as_* / query_scalar_* / query_map_* / map_* with annotations, the metric / annotation / span auxiliary tests, and the builder / pool-close / QueryTextMode::Off tests (via a new per-backend `raw_pool()` helper). --- tests/common.rs | 2531 +++++++++++++++++++++++++++++++++++++++++++++ tests/mysql.rs | 2086 ++++--------------------------------- tests/postgres.rs | 2124 +++++-------------------------------- tests/sqlite.rs | 2159 ++++---------------------------------- 4 files changed, 3199 insertions(+), 5701 deletions(-) diff --git a/tests/common.rs b/tests/common.rs index 9e5c7c0..2f81bec 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -340,3 +340,2534 @@ macro_rules! test_execute_creates_span_via_pool { $crate::common::assert_annotated_span(tel.spans().last().unwrap(), &$dialect); }}; } + +/// Counterpart to `test_execute_creates_span_via_pool!` for `&mut PoolConnection`. +/// Acquires a connection from the pool, then exercises plain / annotated / shorthand +/// executes against a freshly created table. +#[macro_export] +macro_rules! test_execute_creates_span_via_connection { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx::Executor as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + $crate::fresh_table!( + &pool, + "exec_conn_test", + &format!("id {}", $dialect.id_pk_column) + ); + tel.reset(); + + let mut conn = pool.acquire().await.unwrap(); + (&mut conn) + .execute("INSERT INTO exec_conn_test (id) VALUES (1)") + .await + .unwrap(); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_common_span_attributes(&spans[0], $dialect.system); + assert!($crate::common::attr(&spans[0], "db.response.returned_rows").is_none()); + assert!($crate::common::attr(&spans[0], "db.response.affected_rows").is_some()); + + conn.with_annotations($crate::common::test_annotations()) + .execute("INSERT INTO exec_conn_test (id) VALUES (2)") + .await + .unwrap(); + $crate::common::assert_annotated_span(tel.spans().last().unwrap(), &$dialect); + + conn.with_operation("SELECT", "users") + .execute("INSERT INTO exec_conn_test (id) VALUES (3)") + .await + .unwrap(); + $crate::common::assert_annotated_span(tel.spans().last().unwrap(), &$dialect); + }}; +} + +/// Counterpart to `test_execute_creates_span_via_pool!` for `&mut Transaction<'_, DB>`. +/// Begins a transaction, runs three executes, and commits before asserting on the +/// collected spans. +#[macro_export] +macro_rules! test_execute_creates_span_via_transaction { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx::Executor as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + $crate::fresh_table!( + &pool, + "exec_tx_test", + &format!("id {}", $dialect.id_pk_column) + ); + tel.reset(); + + let mut tx = pool.begin().await.unwrap(); + (&mut tx) + .execute("INSERT INTO exec_tx_test (id) VALUES (1)") + .await + .unwrap(); + + tx.with_annotations($crate::common::test_annotations()) + .execute("INSERT INTO exec_tx_test (id) VALUES (2)") + .await + .unwrap(); + + tx.with_operation("SELECT", "users") + .execute("INSERT INTO exec_tx_test (id) VALUES (3)") + .await + .unwrap(); + + tx.commit().await.unwrap(); + + let spans = tel.spans(); + assert_eq!(spans.len(), 3); + $crate::common::assert_common_span_attributes(&spans[0], $dialect.system); + assert!($crate::common::attr(&spans[0], "db.response.returned_rows").is_none()); + assert!($crate::common::attr(&spans[0], "db.response.affected_rows").is_some()); + $crate::common::assert_annotated_span(&spans[1], &$dialect); + $crate::common::assert_annotated_span(&spans[2], &$dialect); + }}; +} + +/// `execute` against invalid SQL records an error span. Exercises plain, annotated, and +/// shorthand annotation paths. +#[macro_export] +macro_rules! test_execute_records_error { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx::Executor as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let result = (&pool).execute("INVALID SQL GIBBERISH").await; + assert!(result.is_err()); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_common_span_attributes(&spans[0], $dialect.system); + $crate::common::assert_error_span(&spans[0]); + assert!($crate::common::attr(&spans[0], "db.response.returned_rows").is_none()); + + let result = pool + .with_annotations($crate::common::test_annotations()) + .execute("INVALID SQL GIBBERISH") + .await; + assert!(result.is_err()); + let last = tel.spans().last().unwrap().clone(); + $crate::common::assert_annotated_span(&last, &$dialect); + $crate::common::assert_error_span(&last); + + let result = pool + .with_operation("SELECT", "users") + .execute("INVALID SQL GIBBERISH") + .await; + assert!(result.is_err()); + let last = tel.spans().last().unwrap().clone(); + $crate::common::assert_annotated_span(&last, &$dialect); + $crate::common::assert_error_span(&last); + }}; +} + +/// `execute_many` over a multi-statement query yields one span per stream consumption. +/// Exercises plain, annotated, and shorthand paths against the wrapped pool. +#[macro_export] +macro_rules! test_execute_many_via_pool { + ($pool_factory:expr, $dialect:expr) => {{ + use futures::StreamExt as _; + use sqlx::Executor as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let mut stream = (&pool).execute_many("SELECT 1; SELECT 2"); + while stream.next().await.is_some() {} + drop(stream); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_common_span_attributes(&spans[0], $dialect.system); + assert_eq!( + $crate::common::attr(&spans[0], "db.response.returned_rows"), + Some(opentelemetry::Value::I64(0)) + ); + + let mut stream = pool + .with_annotations($crate::common::test_annotations()) + .execute_many("SELECT 1; SELECT 2"); + while stream.next().await.is_some() {} + drop(stream); + $crate::common::assert_annotated_span(tel.spans().last().unwrap(), &$dialect); + + let mut stream = pool + .with_operation("SELECT", "users") + .execute_many("SELECT 1; SELECT 2"); + while stream.next().await.is_some() {} + drop(stream); + $crate::common::assert_annotated_span(tel.spans().last().unwrap(), &$dialect); + }}; +} + +/// `execute_many` against `&mut PoolConnection`. Same shape as the pool variant +/// but acquires a connection first. +#[macro_export] +macro_rules! test_execute_many_via_connection { + ($pool_factory:expr, $dialect:expr) => {{ + use futures::StreamExt as _; + use sqlx::Executor as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let mut conn = pool.acquire().await.unwrap(); + let mut stream = (&mut conn).execute_many("SELECT 1; SELECT 2"); + while stream.next().await.is_some() {} + drop(stream); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_common_span_attributes(&spans[0], $dialect.system); + assert_eq!( + $crate::common::attr(&spans[0], "db.response.returned_rows"), + Some(opentelemetry::Value::I64(0)) + ); + + let mut stream = conn + .with_annotations($crate::common::test_annotations()) + .execute_many("SELECT 1; SELECT 2"); + while stream.next().await.is_some() {} + drop(stream); + $crate::common::assert_annotated_span(tel.spans().last().unwrap(), &$dialect); + + let mut stream = conn + .with_operation("SELECT", "users") + .execute_many("SELECT 1; SELECT 2"); + while stream.next().await.is_some() {} + drop(stream); + $crate::common::assert_annotated_span(tel.spans().last().unwrap(), &$dialect); + }}; +} + +/// `execute_many` against `&mut Transaction<'_, DB>`. Asserts on all three spans +/// after commit. +#[macro_export] +macro_rules! test_execute_many_via_transaction { + ($pool_factory:expr, $dialect:expr) => {{ + use futures::StreamExt as _; + use sqlx::Executor as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let mut tx = pool.begin().await.unwrap(); + let mut stream = (&mut tx).execute_many("SELECT 1; SELECT 2"); + while stream.next().await.is_some() {} + drop(stream); + + let mut stream = tx + .with_annotations($crate::common::test_annotations()) + .execute_many("SELECT 1; SELECT 2"); + while stream.next().await.is_some() {} + drop(stream); + + let mut stream = tx + .with_operation("SELECT", "users") + .execute_many("SELECT 1; SELECT 2"); + while stream.next().await.is_some() {} + drop(stream); + + tx.commit().await.unwrap(); + + let spans = tel.spans(); + assert_eq!(spans.len(), 3); + $crate::common::assert_common_span_attributes(&spans[0], $dialect.system); + assert_eq!( + $crate::common::attr(&spans[0], "db.response.returned_rows"), + Some(opentelemetry::Value::I64(0)) + ); + $crate::common::assert_annotated_span(&spans[1], &$dialect); + $crate::common::assert_annotated_span(&spans[2], &$dialect); + }}; +} + +/// `execute_many` against invalid SQL records an error span on the streaming path. +#[macro_export] +macro_rules! test_execute_many_records_error { + ($pool_factory:expr, $dialect:expr) => {{ + use futures::StreamExt as _; + use sqlx::Executor as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let mut stream = (&pool).execute_many("INVALID SQL GIBBERISH"); + let result = stream.next().await; + assert!(result.is_some_and(|r| r.is_err())); + drop(stream); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_common_span_attributes(&spans[0], $dialect.system); + $crate::common::assert_error_span(&spans[0]); + assert_eq!( + $crate::common::attr(&spans[0], "db.response.returned_rows"), + Some(opentelemetry::Value::I64(0)) + ); + + let mut stream = pool + .with_annotations($crate::common::test_annotations()) + .execute_many("INVALID SQL GIBBERISH"); + let result = stream.next().await; + assert!(result.is_some_and(|r| r.is_err())); + drop(stream); + let last = tel.spans().last().unwrap().clone(); + $crate::common::assert_annotated_span(&last, &$dialect); + $crate::common::assert_error_span(&last); + + let mut stream = pool + .with_operation("SELECT", "users") + .execute_many("INVALID SQL GIBBERISH"); + let result = stream.next().await; + assert!(result.is_some_and(|r| r.is_err())); + drop(stream); + let last = tel.spans().last().unwrap().clone(); + $crate::common::assert_annotated_span(&last, &$dialect); + $crate::common::assert_error_span(&last); + }}; +} + +/// `fetch` against the wrapped pool. Streams 2 rows, then exercises annotated and +/// shorthand variants. +#[macro_export] +macro_rules! test_fetch_via_pool { + ($pool_factory:expr, $dialect:expr) => {{ + use futures::StreamExt as _; + use sqlx::Executor as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let mut stream = (&pool).fetch("SELECT 1 UNION ALL SELECT 2"); + let mut count = 0u64; + while stream.next().await.is_some() { + count += 1; + } + assert_eq!(count, 2); + drop(stream); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_common_span_attributes(&spans[0], $dialect.system); + assert_eq!( + $crate::common::attr(&spans[0], "db.response.returned_rows"), + Some(opentelemetry::Value::I64(2)) + ); + + let mut stream = pool + .with_annotations($crate::common::test_annotations()) + .fetch("SELECT 1 UNION ALL SELECT 2"); + while stream.next().await.is_some() {} + drop(stream); + $crate::common::assert_annotated_span(tel.spans().last().unwrap(), &$dialect); + + let mut stream = pool + .with_operation("SELECT", "users") + .fetch("SELECT 1 UNION ALL SELECT 2"); + while stream.next().await.is_some() {} + drop(stream); + $crate::common::assert_annotated_span(tel.spans().last().unwrap(), &$dialect); + }}; +} + +/// `fetch` against `&mut PoolConnection`. +#[macro_export] +macro_rules! test_fetch_via_connection { + ($pool_factory:expr, $dialect:expr) => {{ + use futures::StreamExt as _; + use sqlx::Executor as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let mut conn = pool.acquire().await.unwrap(); + let mut stream = (&mut conn).fetch("SELECT 1 UNION ALL SELECT 2"); + while stream.next().await.is_some() {} + drop(stream); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_common_span_attributes(&spans[0], $dialect.system); + assert_eq!( + $crate::common::attr(&spans[0], "db.response.returned_rows"), + Some(opentelemetry::Value::I64(2)) + ); + + let mut stream = conn + .with_annotations($crate::common::test_annotations()) + .fetch("SELECT 1 UNION ALL SELECT 2"); + while stream.next().await.is_some() {} + drop(stream); + $crate::common::assert_annotated_span(tel.spans().last().unwrap(), &$dialect); + + let mut stream = conn + .with_operation("SELECT", "users") + .fetch("SELECT 1 UNION ALL SELECT 2"); + while stream.next().await.is_some() {} + drop(stream); + $crate::common::assert_annotated_span(tel.spans().last().unwrap(), &$dialect); + }}; +} + +/// `fetch` against `&mut Transaction<'_, DB>`. +#[macro_export] +macro_rules! test_fetch_via_transaction { + ($pool_factory:expr, $dialect:expr) => {{ + use futures::StreamExt as _; + use sqlx::Executor as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let mut tx = pool.begin().await.unwrap(); + let mut stream = (&mut tx).fetch("SELECT 1 UNION ALL SELECT 2"); + while stream.next().await.is_some() {} + drop(stream); + + let mut stream = tx + .with_annotations($crate::common::test_annotations()) + .fetch("SELECT 1 UNION ALL SELECT 2"); + while stream.next().await.is_some() {} + drop(stream); + + let mut stream = tx + .with_operation("SELECT", "users") + .fetch("SELECT 1 UNION ALL SELECT 2"); + while stream.next().await.is_some() {} + drop(stream); + + tx.commit().await.unwrap(); + + let spans = tel.spans(); + assert_eq!(spans.len(), 3); + $crate::common::assert_common_span_attributes(&spans[0], $dialect.system); + assert_eq!( + $crate::common::attr(&spans[0], "db.response.returned_rows"), + Some(opentelemetry::Value::I64(2)) + ); + $crate::common::assert_annotated_span(&spans[1], &$dialect); + $crate::common::assert_annotated_span(&spans[2], &$dialect); + }}; +} + +/// Verifies that dropping a `fetch` stream after consuming a single row still +/// finalises and exports the span (with `returned_rows` reflecting the partial read). +#[macro_export] +macro_rules! test_fetch_stream_dropped_early_still_records_span { + ($pool_factory:expr, $dialect:expr) => {{ + use futures::StreamExt as _; + use sqlx::Executor as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + { + let mut stream = (&pool).fetch("SELECT 1 UNION ALL SELECT 2"); + let _ = stream.next().await; + } + + let spans = tel.spans(); + assert_eq!( + spans.len(), + 1, + "span should be recorded even when stream is dropped early" + ); + $crate::common::assert_common_span_attributes(&spans[0], $dialect.system); + assert_eq!( + $crate::common::attr(&spans[0], "db.response.returned_rows"), + Some(opentelemetry::Value::I64(1)) + ); + }}; +} + +/// `fetch` against invalid SQL records an error span on the streaming path. +#[macro_export] +macro_rules! test_fetch_stream_records_error { + ($pool_factory:expr, $dialect:expr) => {{ + use futures::StreamExt as _; + use sqlx::Executor as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let mut stream = (&pool).fetch("INVALID SQL"); + let result = stream.next().await; + assert!(result.is_some_and(|r| r.is_err())); + drop(stream); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_common_span_attributes(&spans[0], $dialect.system); + $crate::common::assert_error_span(&spans[0]); + assert_eq!( + $crate::common::attr(&spans[0], "db.response.returned_rows"), + Some(opentelemetry::Value::I64(0)) + ); + + let mut stream = pool + .with_annotations($crate::common::test_annotations()) + .fetch("INVALID SQL"); + let result = stream.next().await; + assert!(result.is_some_and(|r| r.is_err())); + drop(stream); + let last = tel.spans().last().unwrap().clone(); + $crate::common::assert_annotated_span(&last, &$dialect); + $crate::common::assert_error_span(&last); + + let mut stream = pool.with_operation("SELECT", "users").fetch("INVALID SQL"); + let result = stream.next().await; + assert!(result.is_some_and(|r| r.is_err())); + drop(stream); + let last = tel.spans().last().unwrap().clone(); + $crate::common::assert_annotated_span(&last, &$dialect); + $crate::common::assert_error_span(&last); + }}; +} + +/// `fetch_many` against the wrapped pool. Returns rows + result rows on a stream. +#[macro_export] +macro_rules! test_fetch_many_via_pool { + ($pool_factory:expr, $dialect:expr) => {{ + use futures::StreamExt as _; + use sqlx::Executor as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let mut stream = (&pool).fetch_many("SELECT 1 UNION ALL SELECT 2"); + let mut rows = 0u64; + let mut results = 0u64; + while let Some(item) = stream.next().await { + match item.unwrap() { + sqlx::Either::Left(_) => results += 1, + sqlx::Either::Right(_) => rows += 1, + } + } + drop(stream); + + assert_eq!(rows, 2); + assert!(results >= 1, "should have at least one QueryResult"); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_common_span_attributes(&spans[0], $dialect.system); + assert_eq!( + $crate::common::attr(&spans[0], "db.response.returned_rows"), + Some(opentelemetry::Value::I64(2)) + ); + + let mut stream = pool + .with_annotations($crate::common::test_annotations()) + .fetch_many("SELECT 1 UNION ALL SELECT 2"); + while stream.next().await.is_some() {} + drop(stream); + $crate::common::assert_annotated_span(tel.spans().last().unwrap(), &$dialect); + + let mut stream = pool + .with_operation("SELECT", "users") + .fetch_many("SELECT 1 UNION ALL SELECT 2"); + while stream.next().await.is_some() {} + drop(stream); + $crate::common::assert_annotated_span(tel.spans().last().unwrap(), &$dialect); + }}; +} + +/// `fetch_many` against `&mut PoolConnection`. +#[macro_export] +macro_rules! test_fetch_many_via_connection { + ($pool_factory:expr, $dialect:expr) => {{ + use futures::StreamExt as _; + use sqlx::Executor as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let mut conn = pool.acquire().await.unwrap(); + let mut stream = (&mut conn).fetch_many("SELECT 1 UNION ALL SELECT 2"); + while stream.next().await.is_some() {} + drop(stream); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_common_span_attributes(&spans[0], $dialect.system); + assert_eq!( + $crate::common::attr(&spans[0], "db.response.returned_rows"), + Some(opentelemetry::Value::I64(2)) + ); + + let mut stream = conn + .with_annotations($crate::common::test_annotations()) + .fetch_many("SELECT 1 UNION ALL SELECT 2"); + while stream.next().await.is_some() {} + drop(stream); + $crate::common::assert_annotated_span(tel.spans().last().unwrap(), &$dialect); + + let mut stream = conn + .with_operation("SELECT", "users") + .fetch_many("SELECT 1 UNION ALL SELECT 2"); + while stream.next().await.is_some() {} + drop(stream); + $crate::common::assert_annotated_span(tel.spans().last().unwrap(), &$dialect); + }}; +} + +/// `fetch_many` against `&mut Transaction<'_, DB>`. +#[macro_export] +macro_rules! test_fetch_many_via_transaction { + ($pool_factory:expr, $dialect:expr) => {{ + use futures::StreamExt as _; + use sqlx::Executor as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let mut tx = pool.begin().await.unwrap(); + let mut stream = (&mut tx).fetch_many("SELECT 1 UNION ALL SELECT 2"); + while stream.next().await.is_some() {} + drop(stream); + + let mut stream = tx + .with_annotations($crate::common::test_annotations()) + .fetch_many("SELECT 1 UNION ALL SELECT 2"); + while stream.next().await.is_some() {} + drop(stream); + + let mut stream = tx + .with_operation("SELECT", "users") + .fetch_many("SELECT 1 UNION ALL SELECT 2"); + while stream.next().await.is_some() {} + drop(stream); + + tx.commit().await.unwrap(); + + let spans = tel.spans(); + assert_eq!(spans.len(), 3); + $crate::common::assert_common_span_attributes(&spans[0], $dialect.system); + assert_eq!( + $crate::common::attr(&spans[0], "db.response.returned_rows"), + Some(opentelemetry::Value::I64(2)) + ); + $crate::common::assert_annotated_span(&spans[1], &$dialect); + $crate::common::assert_annotated_span(&spans[2], &$dialect); + }}; +} + +/// Verifies that dropping a `fetch_many` stream after consuming a single row still +/// finalises and exports the span. +#[macro_export] +macro_rules! test_fetch_many_dropped_early_still_records_span { + ($pool_factory:expr, $dialect:expr) => {{ + use futures::StreamExt as _; + use sqlx::Executor as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + { + let mut stream = (&pool).fetch_many("SELECT 1 UNION ALL SELECT 2"); + let _ = stream.next().await; + } + + let spans = tel.spans(); + assert_eq!( + spans.len(), + 1, + "span should be recorded even when stream is dropped early" + ); + $crate::common::assert_common_span_attributes(&spans[0], $dialect.system); + assert_eq!( + $crate::common::attr(&spans[0], "db.response.returned_rows"), + Some(opentelemetry::Value::I64(1)) + ); + }}; +} + +/// `fetch_many` against invalid SQL records an error span on the streaming path. +#[macro_export] +macro_rules! test_fetch_many_records_error { + ($pool_factory:expr, $dialect:expr) => {{ + use futures::StreamExt as _; + use sqlx::Executor as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let mut stream = (&pool).fetch_many("INVALID SQL GIBBERISH"); + let result = stream.next().await; + assert!(result.is_some_and(|r| r.is_err())); + drop(stream); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_common_span_attributes(&spans[0], $dialect.system); + $crate::common::assert_error_span(&spans[0]); + assert_eq!( + $crate::common::attr(&spans[0], "db.response.returned_rows"), + Some(opentelemetry::Value::I64(0)) + ); + + let mut stream = pool + .with_annotations($crate::common::test_annotations()) + .fetch_many("INVALID SQL GIBBERISH"); + let result = stream.next().await; + assert!(result.is_some_and(|r| r.is_err())); + drop(stream); + let last = tel.spans().last().unwrap().clone(); + $crate::common::assert_annotated_span(&last, &$dialect); + $crate::common::assert_error_span(&last); + + let mut stream = pool + .with_operation("SELECT", "users") + .fetch_many("INVALID SQL GIBBERISH"); + let result = stream.next().await; + assert!(result.is_some_and(|r| r.is_err())); + drop(stream); + let last = tel.spans().last().unwrap().clone(); + $crate::common::assert_annotated_span(&last, &$dialect); + $crate::common::assert_error_span(&last); + }}; +} + +/// `fetch_all` against the wrapped pool. Returns 3 rows; exercises plain, annotated, +/// and shorthand variants. +#[macro_export] +macro_rules! test_fetch_all_via_pool { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx::Executor as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let rows = (&pool) + .fetch_all("SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3") + .await + .unwrap(); + assert_eq!(rows.len(), 3); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_common_span_attributes(&spans[0], $dialect.system); + assert_eq!( + $crate::common::attr(&spans[0], "db.response.returned_rows"), + Some(opentelemetry::Value::I64(3)) + ); + + pool.with_annotations($crate::common::test_annotations()) + .fetch_all("SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3") + .await + .unwrap(); + $crate::common::assert_annotated_span(tel.spans().last().unwrap(), &$dialect); + + pool.with_operation("SELECT", "users") + .fetch_all("SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3") + .await + .unwrap(); + $crate::common::assert_annotated_span(tel.spans().last().unwrap(), &$dialect); + }}; +} + +/// `fetch_all` against `&mut PoolConnection`. +#[macro_export] +macro_rules! test_fetch_all_via_connection { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx::Executor as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let mut conn = pool.acquire().await.unwrap(); + let rows = (&mut conn) + .fetch_all("SELECT 1 UNION ALL SELECT 2") + .await + .unwrap(); + assert_eq!(rows.len(), 2); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_common_span_attributes(&spans[0], $dialect.system); + assert_eq!( + $crate::common::attr(&spans[0], "db.response.returned_rows"), + Some(opentelemetry::Value::I64(2)) + ); + + conn.with_annotations($crate::common::test_annotations()) + .fetch_all("SELECT 1 UNION ALL SELECT 2") + .await + .unwrap(); + $crate::common::assert_annotated_span(tel.spans().last().unwrap(), &$dialect); + + conn.with_operation("SELECT", "users") + .fetch_all("SELECT 1 UNION ALL SELECT 2") + .await + .unwrap(); + $crate::common::assert_annotated_span(tel.spans().last().unwrap(), &$dialect); + }}; +} + +/// `fetch_all` against `&mut Transaction<'_, DB>`. +#[macro_export] +macro_rules! test_fetch_all_via_transaction { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx::Executor as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let mut tx = pool.begin().await.unwrap(); + let rows = (&mut tx) + .fetch_all("SELECT 1 UNION ALL SELECT 2") + .await + .unwrap(); + assert_eq!(rows.len(), 2); + + tx.with_annotations($crate::common::test_annotations()) + .fetch_all("SELECT 1 UNION ALL SELECT 2") + .await + .unwrap(); + + tx.with_operation("SELECT", "users") + .fetch_all("SELECT 1 UNION ALL SELECT 2") + .await + .unwrap(); + + tx.commit().await.unwrap(); + + let spans = tel.spans(); + assert_eq!(spans.len(), 3); + $crate::common::assert_common_span_attributes(&spans[0], $dialect.system); + assert_eq!( + $crate::common::attr(&spans[0], "db.response.returned_rows"), + Some(opentelemetry::Value::I64(2)) + ); + $crate::common::assert_annotated_span(&spans[1], &$dialect); + $crate::common::assert_annotated_span(&spans[2], &$dialect); + }}; +} + +/// `fetch_all` against invalid SQL records an error span. +#[macro_export] +macro_rules! test_fetch_all_records_error { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx::Executor as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let result = (&pool).fetch_all("INVALID SQL GIBBERISH").await; + assert!(result.is_err()); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_common_span_attributes(&spans[0], $dialect.system); + $crate::common::assert_error_span(&spans[0]); + assert!($crate::common::attr(&spans[0], "db.response.returned_rows").is_none()); + + let result = pool + .with_annotations($crate::common::test_annotations()) + .fetch_all("INVALID SQL GIBBERISH") + .await; + assert!(result.is_err()); + let last = tel.spans().last().unwrap().clone(); + $crate::common::assert_annotated_span(&last, &$dialect); + $crate::common::assert_error_span(&last); + + let result = pool + .with_operation("SELECT", "users") + .fetch_all("INVALID SQL GIBBERISH") + .await; + assert!(result.is_err()); + let last = tel.spans().last().unwrap().clone(); + $crate::common::assert_annotated_span(&last, &$dialect); + $crate::common::assert_error_span(&last); + }}; +} + +/// `fetch_one` against the wrapped pool. Returns 1 row. +#[macro_export] +macro_rules! test_fetch_one_via_pool { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx::Executor as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let _row = (&pool).fetch_one("SELECT 1").await.unwrap(); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_common_span_attributes(&spans[0], $dialect.system); + assert_eq!( + $crate::common::attr(&spans[0], "db.response.returned_rows"), + Some(opentelemetry::Value::I64(1)) + ); + + pool.with_annotations($crate::common::test_annotations()) + .fetch_one("SELECT 1") + .await + .unwrap(); + $crate::common::assert_annotated_span(tel.spans().last().unwrap(), &$dialect); + + pool.with_operation("SELECT", "users") + .fetch_one("SELECT 1") + .await + .unwrap(); + $crate::common::assert_annotated_span(tel.spans().last().unwrap(), &$dialect); + }}; +} + +/// `fetch_one` against `&mut PoolConnection`. +#[macro_export] +macro_rules! test_fetch_one_via_connection { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx::Executor as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let mut conn = pool.acquire().await.unwrap(); + let _row = (&mut conn).fetch_one("SELECT 1").await.unwrap(); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_common_span_attributes(&spans[0], $dialect.system); + assert_eq!( + $crate::common::attr(&spans[0], "db.response.returned_rows"), + Some(opentelemetry::Value::I64(1)) + ); + + conn.with_annotations($crate::common::test_annotations()) + .fetch_one("SELECT 1") + .await + .unwrap(); + $crate::common::assert_annotated_span(tel.spans().last().unwrap(), &$dialect); + + conn.with_operation("SELECT", "users") + .fetch_one("SELECT 1") + .await + .unwrap(); + $crate::common::assert_annotated_span(tel.spans().last().unwrap(), &$dialect); + }}; +} + +/// `fetch_one` against `&mut Transaction<'_, DB>`. +#[macro_export] +macro_rules! test_fetch_one_via_transaction { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx::Executor as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let mut tx = pool.begin().await.unwrap(); + let _row = (&mut tx).fetch_one("SELECT 1").await.unwrap(); + + tx.with_annotations($crate::common::test_annotations()) + .fetch_one("SELECT 1") + .await + .unwrap(); + + tx.with_operation("SELECT", "users") + .fetch_one("SELECT 1") + .await + .unwrap(); + + tx.commit().await.unwrap(); + + let spans = tel.spans(); + assert_eq!(spans.len(), 3); + $crate::common::assert_common_span_attributes(&spans[0], $dialect.system); + assert_eq!( + $crate::common::attr(&spans[0], "db.response.returned_rows"), + Some(opentelemetry::Value::I64(1)) + ); + $crate::common::assert_annotated_span(&spans[1], &$dialect); + $crate::common::assert_annotated_span(&spans[2], &$dialect); + }}; +} + +/// `fetch_one` against invalid SQL records an error span. +#[macro_export] +macro_rules! test_fetch_one_records_error { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx::Executor as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let result = (&pool).fetch_one("INVALID SQL GIBBERISH").await; + assert!(result.is_err()); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_common_span_attributes(&spans[0], $dialect.system); + $crate::common::assert_error_span(&spans[0]); + assert!($crate::common::attr(&spans[0], "db.response.returned_rows").is_none()); + + let result = pool + .with_annotations($crate::common::test_annotations()) + .fetch_one("INVALID SQL GIBBERISH") + .await; + assert!(result.is_err()); + let last = tel.spans().last().unwrap().clone(); + $crate::common::assert_annotated_span(&last, &$dialect); + $crate::common::assert_error_span(&last); + + let result = pool + .with_operation("SELECT", "users") + .fetch_one("INVALID SQL GIBBERISH") + .await; + assert!(result.is_err()); + let last = tel.spans().last().unwrap().clone(); + $crate::common::assert_annotated_span(&last, &$dialect); + $crate::common::assert_error_span(&last); + }}; +} + +/// `fetch_optional` against the wrapped pool when the query returns one row. +#[macro_export] +macro_rules! test_fetch_optional_records_one_row { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx::Executor as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let result = (&pool).fetch_optional("SELECT 1").await.unwrap(); + assert!(result.is_some()); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_common_span_attributes(&spans[0], $dialect.system); + assert_eq!( + $crate::common::attr(&spans[0], "db.response.returned_rows"), + Some(opentelemetry::Value::I64(1)) + ); + + pool.with_annotations($crate::common::test_annotations()) + .fetch_optional("SELECT 1") + .await + .unwrap(); + $crate::common::assert_annotated_span(tel.spans().last().unwrap(), &$dialect); + + pool.with_operation("SELECT", "users") + .fetch_optional("SELECT 1") + .await + .unwrap(); + $crate::common::assert_annotated_span(tel.spans().last().unwrap(), &$dialect); + }}; +} + +/// `fetch_optional` against `&mut PoolConnection`. +#[macro_export] +macro_rules! test_fetch_optional_via_connection { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx::Executor as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let mut conn = pool.acquire().await.unwrap(); + let result = (&mut conn).fetch_optional("SELECT 1").await.unwrap(); + assert!(result.is_some()); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_common_span_attributes(&spans[0], $dialect.system); + assert_eq!( + $crate::common::attr(&spans[0], "db.response.returned_rows"), + Some(opentelemetry::Value::I64(1)) + ); + + conn.with_annotations($crate::common::test_annotations()) + .fetch_optional("SELECT 1") + .await + .unwrap(); + $crate::common::assert_annotated_span(tel.spans().last().unwrap(), &$dialect); + + conn.with_operation("SELECT", "users") + .fetch_optional("SELECT 1") + .await + .unwrap(); + $crate::common::assert_annotated_span(tel.spans().last().unwrap(), &$dialect); + }}; +} + +/// `fetch_optional` against `&mut Transaction<'_, DB>`. +#[macro_export] +macro_rules! test_fetch_optional_via_transaction { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx::Executor as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let mut tx = pool.begin().await.unwrap(); + let result = (&mut tx).fetch_optional("SELECT 1").await.unwrap(); + assert!(result.is_some()); + + tx.with_annotations($crate::common::test_annotations()) + .fetch_optional("SELECT 1") + .await + .unwrap(); + + tx.with_operation("SELECT", "users") + .fetch_optional("SELECT 1") + .await + .unwrap(); + + tx.commit().await.unwrap(); + + let spans = tel.spans(); + assert_eq!(spans.len(), 3); + $crate::common::assert_common_span_attributes(&spans[0], $dialect.system); + assert_eq!( + $crate::common::attr(&spans[0], "db.response.returned_rows"), + Some(opentelemetry::Value::I64(1)) + ); + $crate::common::assert_annotated_span(&spans[1], &$dialect); + $crate::common::assert_annotated_span(&spans[2], &$dialect); + }}; +} + +/// `fetch_optional` against invalid SQL records an error span. +#[macro_export] +macro_rules! test_fetch_optional_records_error { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx::Executor as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let result = (&pool).fetch_optional("INVALID SQL GIBBERISH").await; + assert!(result.is_err()); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_common_span_attributes(&spans[0], $dialect.system); + $crate::common::assert_error_span(&spans[0]); + assert_eq!( + $crate::common::attr(&spans[0], "db.response.returned_rows"), + None + ); + + let result = pool + .with_annotations($crate::common::test_annotations()) + .fetch_optional("INVALID SQL GIBBERISH") + .await; + assert!(result.is_err()); + let last = tel.spans().last().unwrap().clone(); + $crate::common::assert_annotated_span(&last, &$dialect); + $crate::common::assert_error_span(&last); + + let result = pool + .with_operation("SELECT", "users") + .fetch_optional("INVALID SQL GIBBERISH") + .await; + assert!(result.is_err()); + let last = tel.spans().last().unwrap().clone(); + $crate::common::assert_annotated_span(&last, &$dialect); + $crate::common::assert_error_span(&last); + }}; +} + +// --------------------------------------------------------------------------- +// prepare / prepare_with / describe +// --------------------------------------------------------------------------- + +/// `prepare` against the wrapped pool. No rows returned; just verifies the span shape. +#[macro_export] +macro_rules! test_prepare_via_pool { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx::Executor as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let _stmt = (&pool).prepare("SELECT 1").await.unwrap(); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_common_span_attributes(&spans[0], $dialect.system); + assert!($crate::common::attr(&spans[0], "db.response.returned_rows").is_none()); + + pool.with_annotations($crate::common::test_annotations()) + .prepare("SELECT 1") + .await + .unwrap(); + $crate::common::assert_annotated_span(tel.spans().last().unwrap(), &$dialect); + + pool.with_operation("SELECT", "users") + .prepare("SELECT 1") + .await + .unwrap(); + $crate::common::assert_annotated_span(tel.spans().last().unwrap(), &$dialect); + }}; +} + +/// `prepare` against `&mut PoolConnection`. +#[macro_export] +macro_rules! test_prepare_via_connection { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx::Executor as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let mut conn = pool.acquire().await.unwrap(); + let _stmt = (&mut conn).prepare("SELECT 1").await.unwrap(); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_common_span_attributes(&spans[0], $dialect.system); + assert!($crate::common::attr(&spans[0], "db.response.returned_rows").is_none()); + + conn.with_annotations($crate::common::test_annotations()) + .prepare("SELECT 1") + .await + .unwrap(); + $crate::common::assert_annotated_span(tel.spans().last().unwrap(), &$dialect); + + conn.with_operation("SELECT", "users") + .prepare("SELECT 1") + .await + .unwrap(); + $crate::common::assert_annotated_span(tel.spans().last().unwrap(), &$dialect); + }}; +} + +/// `prepare` against `&mut Transaction<'_, DB>`. +#[macro_export] +macro_rules! test_prepare_via_transaction { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx::Executor as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let mut tx = pool.begin().await.unwrap(); + let _stmt = (&mut tx).prepare("SELECT 1").await.unwrap(); + + tx.with_annotations($crate::common::test_annotations()) + .prepare("SELECT 1") + .await + .unwrap(); + + tx.with_operation("SELECT", "users") + .prepare("SELECT 1") + .await + .unwrap(); + + tx.commit().await.unwrap(); + + let spans = tel.spans(); + assert_eq!(spans.len(), 3); + $crate::common::assert_common_span_attributes(&spans[0], $dialect.system); + assert!($crate::common::attr(&spans[0], "db.response.returned_rows").is_none()); + $crate::common::assert_annotated_span(&spans[1], &$dialect); + $crate::common::assert_annotated_span(&spans[2], &$dialect); + }}; +} + +/// `prepare` against invalid SQL records an error span. +#[macro_export] +macro_rules! test_prepare_records_error { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx::Executor as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let mut conn = pool.acquire().await.unwrap(); + let result = (&mut conn).prepare("INVALID SQL GIBBERISH").await; + assert!(result.is_err()); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_common_span_attributes(&spans[0], $dialect.system); + $crate::common::assert_error_span(&spans[0]); + assert!($crate::common::attr(&spans[0], "db.response.returned_rows").is_none()); + + let result = conn + .with_annotations($crate::common::test_annotations()) + .prepare("INVALID SQL GIBBERISH") + .await; + assert!(result.is_err()); + let last = tel.spans().last().unwrap().clone(); + $crate::common::assert_annotated_span(&last, &$dialect); + $crate::common::assert_error_span(&last); + + let result = conn + .with_operation("SELECT", "users") + .prepare("INVALID SQL GIBBERISH") + .await; + assert!(result.is_err()); + let last = tel.spans().last().unwrap().clone(); + $crate::common::assert_annotated_span(&last, &$dialect); + $crate::common::assert_error_span(&last); + }}; +} + +/// `prepare_with` against the wrapped pool. +#[macro_export] +macro_rules! test_prepare_with_via_pool { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx::Executor as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let _stmt = (&pool).prepare_with("SELECT ?", &[]).await.unwrap(); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_common_span_attributes(&spans[0], $dialect.system); + assert!($crate::common::attr(&spans[0], "db.response.returned_rows").is_none()); + + pool.with_annotations($crate::common::test_annotations()) + .prepare_with("SELECT ?", &[]) + .await + .unwrap(); + $crate::common::assert_annotated_span(tel.spans().last().unwrap(), &$dialect); + + pool.with_operation("SELECT", "users") + .prepare_with("SELECT ?", &[]) + .await + .unwrap(); + $crate::common::assert_annotated_span(tel.spans().last().unwrap(), &$dialect); + }}; +} + +/// `prepare_with` against `&mut PoolConnection`. +#[macro_export] +macro_rules! test_prepare_with_via_connection { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx::Executor as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let mut conn = pool.acquire().await.unwrap(); + let _stmt = (&mut conn).prepare_with("SELECT ?", &[]).await.unwrap(); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_common_span_attributes(&spans[0], $dialect.system); + assert!($crate::common::attr(&spans[0], "db.response.returned_rows").is_none()); + + conn.with_annotations($crate::common::test_annotations()) + .prepare_with("SELECT ?", &[]) + .await + .unwrap(); + $crate::common::assert_annotated_span(tel.spans().last().unwrap(), &$dialect); + + conn.with_operation("SELECT", "users") + .prepare_with("SELECT ?", &[]) + .await + .unwrap(); + $crate::common::assert_annotated_span(tel.spans().last().unwrap(), &$dialect); + }}; +} + +/// `prepare_with` against `&mut Transaction<'_, DB>`. +#[macro_export] +macro_rules! test_prepare_with_via_transaction { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx::Executor as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let mut tx = pool.begin().await.unwrap(); + let _stmt = (&mut tx).prepare_with("SELECT ?", &[]).await.unwrap(); + + tx.with_annotations($crate::common::test_annotations()) + .prepare_with("SELECT ?", &[]) + .await + .unwrap(); + + tx.with_operation("SELECT", "users") + .prepare_with("SELECT ?", &[]) + .await + .unwrap(); + + tx.commit().await.unwrap(); + + let spans = tel.spans(); + assert_eq!(spans.len(), 3); + $crate::common::assert_common_span_attributes(&spans[0], $dialect.system); + assert!($crate::common::attr(&spans[0], "db.response.returned_rows").is_none()); + $crate::common::assert_annotated_span(&spans[1], &$dialect); + $crate::common::assert_annotated_span(&spans[2], &$dialect); + }}; +} + +/// `prepare_with` against invalid SQL records an error span. +#[macro_export] +macro_rules! test_prepare_with_records_error { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx::Executor as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let mut conn = pool.acquire().await.unwrap(); + let result = (&mut conn).prepare_with("INVALID SQL GIBBERISH", &[]).await; + assert!(result.is_err()); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_common_span_attributes(&spans[0], $dialect.system); + $crate::common::assert_error_span(&spans[0]); + assert!($crate::common::attr(&spans[0], "db.response.returned_rows").is_none()); + + let result = conn + .with_annotations($crate::common::test_annotations()) + .prepare_with("INVALID SQL GIBBERISH", &[]) + .await; + assert!(result.is_err()); + let last = tel.spans().last().unwrap().clone(); + $crate::common::assert_annotated_span(&last, &$dialect); + $crate::common::assert_error_span(&last); + + let result = conn + .with_operation("SELECT", "users") + .prepare_with("INVALID SQL GIBBERISH", &[]) + .await; + assert!(result.is_err()); + let last = tel.spans().last().unwrap().clone(); + $crate::common::assert_annotated_span(&last, &$dialect); + $crate::common::assert_error_span(&last); + }}; +} + +/// `describe` against the wrapped pool. +#[macro_export] +macro_rules! test_describe_via_pool { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx::Executor as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let _desc = (&pool).describe("SELECT 1").await.unwrap(); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_common_span_attributes(&spans[0], $dialect.system); + assert!($crate::common::attr(&spans[0], "db.response.returned_rows").is_none()); + + pool.with_annotations($crate::common::test_annotations()) + .describe("SELECT 1") + .await + .unwrap(); + $crate::common::assert_annotated_span(tel.spans().last().unwrap(), &$dialect); + + pool.with_operation("SELECT", "users") + .describe("SELECT 1") + .await + .unwrap(); + $crate::common::assert_annotated_span(tel.spans().last().unwrap(), &$dialect); + }}; +} + +/// `describe` against `&mut PoolConnection`. +#[macro_export] +macro_rules! test_describe_via_connection { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx::Executor as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let mut conn = pool.acquire().await.unwrap(); + let _desc = (&mut conn).describe("SELECT 1").await.unwrap(); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_common_span_attributes(&spans[0], $dialect.system); + assert!($crate::common::attr(&spans[0], "db.response.returned_rows").is_none()); + + conn.with_annotations($crate::common::test_annotations()) + .describe("SELECT 1") + .await + .unwrap(); + $crate::common::assert_annotated_span(tel.spans().last().unwrap(), &$dialect); + + conn.with_operation("SELECT", "users") + .describe("SELECT 1") + .await + .unwrap(); + $crate::common::assert_annotated_span(tel.spans().last().unwrap(), &$dialect); + }}; +} + +/// `describe` against `&mut Transaction<'_, DB>`. +#[macro_export] +macro_rules! test_describe_via_transaction { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx::Executor as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let mut tx = pool.begin().await.unwrap(); + let _desc = (&mut tx).describe("SELECT 1").await.unwrap(); + + tx.with_annotations($crate::common::test_annotations()) + .describe("SELECT 1") + .await + .unwrap(); + + tx.with_operation("SELECT", "users") + .describe("SELECT 1") + .await + .unwrap(); + + tx.commit().await.unwrap(); + + let spans = tel.spans(); + assert_eq!(spans.len(), 3); + $crate::common::assert_common_span_attributes(&spans[0], $dialect.system); + assert!($crate::common::attr(&spans[0], "db.response.returned_rows").is_none()); + $crate::common::assert_annotated_span(&spans[1], &$dialect); + $crate::common::assert_annotated_span(&spans[2], &$dialect); + }}; +} + +/// `describe` against invalid SQL records an error span. +#[macro_export] +macro_rules! test_describe_records_error { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx::Executor as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let mut conn = pool.acquire().await.unwrap(); + let result = (&mut conn).describe("INVALID SQL GIBBERISH").await; + assert!(result.is_err()); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_common_span_attributes(&spans[0], $dialect.system); + $crate::common::assert_error_span(&spans[0]); + assert!($crate::common::attr(&spans[0], "db.response.returned_rows").is_none()); + + let result = conn + .with_annotations($crate::common::test_annotations()) + .describe("INVALID SQL GIBBERISH") + .await; + assert!(result.is_err()); + let last = tel.spans().last().unwrap().clone(); + $crate::common::assert_annotated_span(&last, &$dialect); + $crate::common::assert_error_span(&last); + + let result = conn + .with_operation("SELECT", "users") + .describe("INVALID SQL GIBBERISH") + .await; + assert!(result.is_err()); + let last = tel.spans().last().unwrap().clone(); + $crate::common::assert_annotated_span(&last, &$dialect); + $crate::common::assert_error_span(&last); + }}; +} + +// --------------------------------------------------------------------------- +// Misc: metrics, annotations +// --------------------------------------------------------------------------- + +/// `db.client.operation.duration` histogram is populated for any executed query. +#[macro_export] +macro_rules! test_operation_duration_metric_is_recorded { + ($pool_factory:expr, $dialect:expr) => {{ + use opentelemetry_sdk::metrics::data::{AggregatedMetrics, MetricData}; + let _ = $dialect; // unused — backend doesn't influence the metric shape + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let _: (i32,) = sqlx::query_as("SELECT 1").fetch_one(&pool).await.unwrap(); + + let resource_metrics = tel.metrics(); + assert!(!resource_metrics.is_empty(), "should have metric data"); + + let mut found_duration = false; + for rm in &resource_metrics { + for sm in rm.scope_metrics() { + for metric in sm.metrics() { + if metric.name() == "db.client.operation.duration" { + found_duration = true; + assert_eq!(metric.unit(), "s"); + if let AggregatedMetrics::F64(MetricData::Histogram(hist)) = metric.data() { + let dp: Vec<_> = hist.data_points().collect(); + assert!(!dp.is_empty(), "histogram should have data points"); + assert!(dp[0].count() > 0, "data point count should be > 0"); + let has_system = dp[0] + .attributes() + .any(|kv| kv.key.as_str() == "db.system.name"); + assert!(has_system, "metric should have db.system.name attribute"); + } else { + panic!("db.client.operation.duration should be an f64 histogram"); + } + } + } + } + } + assert!( + found_duration, + "db.client.operation.duration metric not found" + ); + }}; +} + +/// All four annotation fields populated together; summary drives the span name. +#[macro_export] +macro_rules! test_annotation_all_four_fields { + ($pool_factory:expr, $dialect:expr) => {{ + let _ = $dialect; // dialect.system is checked indirectly via assert_common_span_attributes when present + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + pool.with_annotations( + sqlx_otel::QueryAnnotations::new() + .operation("SELECT") + .collection("users") + .query_summary("users by id") + .stored_procedure("sp_get_users"), + ) + .fetch_all("SELECT 1") + .await + .unwrap(); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + assert_eq!(spans[0].name, "users by id"); + assert_eq!( + $crate::common::attr(&spans[0], "db.operation.name"), + Some(opentelemetry::Value::String("SELECT".into())), + ); + assert_eq!( + $crate::common::attr(&spans[0], "db.collection.name"), + Some(opentelemetry::Value::String("users".into())), + ); + assert_eq!( + $crate::common::attr(&spans[0], "db.query.summary"), + Some(opentelemetry::Value::String("users by id".into())), + ); + assert_eq!( + $crate::common::attr(&spans[0], "db.stored_procedure.name"), + Some(opentelemetry::Value::String("sp_get_users".into())), + ); + }}; +} + +/// `db.query.summary` overrides the span name independently of `db.operation.name` and +/// `db.collection.name`, but does not suppress those attributes. +#[macro_export] +macro_rules! test_query_summary_drives_span_name { + ($pool_factory:expr, $dialect:expr) => {{ + let _ = $dialect; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + pool.with_annotations( + sqlx_otel::QueryAnnotations::new() + .operation("SELECT") + .collection("users") + .query_summary("users by tenant"), + ) + .fetch_all("SELECT 1") + .await + .unwrap(); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + assert_eq!(spans[0].name, "users by tenant"); + assert_eq!( + $crate::common::attr(&spans[0], "db.query.summary"), + Some(opentelemetry::Value::String("users by tenant".into())), + ); + assert_eq!( + $crate::common::attr(&spans[0], "db.operation.name"), + Some(opentelemetry::Value::String("SELECT".into())), + ); + assert_eq!( + $crate::common::attr(&spans[0], "db.collection.name"), + Some(opentelemetry::Value::String("users".into())), + ); + }}; +} + +// --------------------------------------------------------------------------- +// query-side annotations: sqlx::query(...).with_annotations(...).(executor) +// --------------------------------------------------------------------------- + +/// `sqlx::query(...).with_annotations(...).execute_many(&pool)`. +#[macro_export] +macro_rules! test_query_execute_many_with_annotations_via_pool { + ($pool_factory:expr, $dialect:expr) => {{ + use futures::StreamExt as _; + use sqlx_otel::QueryAnnotateExt as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + #[allow(deprecated)] + let mut stream = sqlx::query("SELECT 1; SELECT 2") + .with_annotations($crate::common::test_annotations()) + .execute_many(&pool) + .await; + while stream.next().await.is_some() {} + drop(stream); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_annotated_span(&spans[0], &$dialect); + }}; +} + +/// `sqlx::query(...).with_annotations(...).fetch(&pool)`. +#[macro_export] +macro_rules! test_query_fetch_with_annotations_via_pool { + ($pool_factory:expr, $dialect:expr) => {{ + use futures::StreamExt as _; + use sqlx_otel::QueryAnnotateExt as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let mut stream = sqlx::query("SELECT 1 UNION ALL SELECT 2") + .with_annotations($crate::common::test_annotations()) + .fetch(&pool); + while stream.next().await.is_some() {} + drop(stream); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_annotated_span(&spans[0], &$dialect); + assert_eq!( + $crate::common::attr(&spans[0], "db.response.returned_rows"), + Some(opentelemetry::Value::I64(2)) + ); + }}; +} + +/// `sqlx::query(...).with_annotations(...).fetch_many(&pool)`. +#[macro_export] +macro_rules! test_query_fetch_many_with_annotations_via_pool { + ($pool_factory:expr, $dialect:expr) => {{ + use futures::StreamExt as _; + use sqlx_otel::QueryAnnotateExt as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + #[allow(deprecated)] + let mut stream = sqlx::query("SELECT 1 UNION ALL SELECT 2") + .with_annotations($crate::common::test_annotations()) + .fetch_many(&pool); + while stream.next().await.is_some() {} + drop(stream); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_annotated_span(&spans[0], &$dialect); + }}; +} + +/// `sqlx::query(...).with_annotations(...).fetch_all(&pool)`. +#[macro_export] +macro_rules! test_query_fetch_all_with_annotations_via_pool { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx_otel::QueryAnnotateExt as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let rows = sqlx::query("SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3") + .with_annotations($crate::common::test_annotations()) + .fetch_all(&pool) + .await + .unwrap(); + assert_eq!(rows.len(), 3); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_annotated_span(&spans[0], &$dialect); + assert_eq!( + $crate::common::attr(&spans[0], "db.response.returned_rows"), + Some(opentelemetry::Value::I64(3)) + ); + }}; +} + +/// `sqlx::query(...).with_annotations(...).fetch_one(&pool)`. +#[macro_export] +macro_rules! test_query_fetch_one_with_annotations_via_pool { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx_otel::QueryAnnotateExt as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let _row = sqlx::query("SELECT 1") + .with_annotations($crate::common::test_annotations()) + .fetch_one(&pool) + .await + .unwrap(); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_annotated_span(&spans[0], &$dialect); + assert_eq!( + $crate::common::attr(&spans[0], "db.response.returned_rows"), + Some(opentelemetry::Value::I64(1)) + ); + }}; +} + +/// `sqlx::query("INVALID SQL").with_annotations(...).execute(&pool)` records an error span. +#[macro_export] +macro_rules! test_query_execute_with_annotations_records_error { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx_otel::QueryAnnotateExt as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let result = sqlx::query("INVALID SQL GIBBERISH") + .with_annotations($crate::common::test_annotations()) + .execute(&pool) + .await; + assert!(result.is_err()); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_annotated_span(&spans[0], &$dialect); + $crate::common::assert_error_span(&spans[0]); + }}; +} + +/// `sqlx::query_as(...).with_annotations(...).fetch(&pool)`. +#[macro_export] +macro_rules! test_query_as_fetch_with_annotations_via_pool { + ($pool_factory:expr, $dialect:expr) => {{ + use futures::StreamExt as _; + use sqlx_otel::QueryAnnotateExt as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let mut stream = sqlx::query_as::<_, (i32,)>("SELECT 1 UNION ALL SELECT 2") + .with_annotations($crate::common::test_annotations()) + .fetch(&pool); + while stream.next().await.is_some() {} + drop(stream); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_annotated_span(&spans[0], &$dialect); + }}; +} + +/// `sqlx::query_as(...).with_annotations(...).fetch_many(&pool)`. +#[macro_export] +macro_rules! test_query_as_fetch_many_with_annotations_via_pool { + ($pool_factory:expr, $dialect:expr) => {{ + use futures::StreamExt as _; + use sqlx_otel::QueryAnnotateExt as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + #[allow(deprecated)] + let mut stream = sqlx::query_as::<_, (i32,)>("SELECT 1 UNION ALL SELECT 2") + .with_annotations($crate::common::test_annotations()) + .fetch_many(&pool); + while stream.next().await.is_some() {} + drop(stream); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_annotated_span(&spans[0], &$dialect); + }}; +} + +/// `sqlx::query_as(...).with_annotations(...).fetch_all(&pool)`. +#[macro_export] +macro_rules! test_query_as_fetch_all_with_annotations_via_pool { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx_otel::QueryAnnotateExt as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let rows: Vec<(i32,)> = sqlx::query_as("SELECT 1 UNION ALL SELECT 2") + .with_annotations($crate::common::test_annotations()) + .fetch_all(&pool) + .await + .unwrap(); + assert_eq!(rows.len(), 2); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_annotated_span(&spans[0], &$dialect); + }}; +} + +/// `sqlx::query_as(...).with_annotations(...).fetch_one(&pool)`. +#[macro_export] +macro_rules! test_query_as_fetch_one_with_annotations_via_pool { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx_otel::QueryAnnotateExt as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let row: (i32,) = sqlx::query_as("SELECT 7") + .with_annotations($crate::common::test_annotations()) + .fetch_one(&pool) + .await + .unwrap(); + assert_eq!(row.0, 7); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_annotated_span(&spans[0], &$dialect); + }}; +} + +/// `sqlx::query_as(...).with_annotations(...).fetch_optional(&pool)` returning none. +#[macro_export] +macro_rules! test_query_as_fetch_optional_with_annotations_via_pool { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx_otel::QueryAnnotateExt as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let row: Option<(i32,)> = sqlx::query_as("SELECT 1 WHERE 1 = 0") + .with_annotations($crate::common::test_annotations()) + .fetch_optional(&pool) + .await + .unwrap(); + assert!(row.is_none()); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_annotated_span(&spans[0], &$dialect); + }}; +} + +/// `sqlx::query_as("INVALID SQL").with_annotations(...).fetch_one(&pool)` records error. +#[macro_export] +macro_rules! test_query_as_fetch_one_with_annotations_records_error { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx_otel::QueryAnnotateExt as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let result: Result<(i32,), _> = sqlx::query_as("INVALID SQL") + .with_annotations($crate::common::test_annotations()) + .fetch_one(&pool) + .await; + assert!(result.is_err()); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_annotated_span(&spans[0], &$dialect); + $crate::common::assert_error_span(&spans[0]); + }}; +} + +/// `sqlx::query_scalar(...).with_annotations(...).fetch(&pool)`. +#[macro_export] +macro_rules! test_query_scalar_fetch_with_annotations_via_pool { + ($pool_factory:expr, $dialect:expr) => {{ + use futures::StreamExt as _; + use sqlx_otel::QueryAnnotateExt as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let mut stream = sqlx::query_scalar::<_, i32>("SELECT 1 UNION ALL SELECT 2") + .with_annotations($crate::common::test_annotations()) + .fetch(&pool); + while stream.next().await.is_some() {} + drop(stream); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_annotated_span(&spans[0], &$dialect); + }}; +} + +/// `sqlx::query_scalar(...).with_annotations(...).fetch_many(&pool)`. +#[macro_export] +macro_rules! test_query_scalar_fetch_many_with_annotations_via_pool { + ($pool_factory:expr, $dialect:expr) => {{ + use futures::StreamExt as _; + use sqlx_otel::QueryAnnotateExt as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + #[allow(deprecated)] + let mut stream = sqlx::query_scalar::<_, i32>("SELECT 1 UNION ALL SELECT 2") + .with_annotations($crate::common::test_annotations()) + .fetch_many(&pool); + while stream.next().await.is_some() {} + drop(stream); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_annotated_span(&spans[0], &$dialect); + }}; +} + +/// `sqlx::query_scalar(...).with_annotations(...).fetch_all(&pool)`. +#[macro_export] +macro_rules! test_query_scalar_fetch_all_with_annotations_via_pool { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx_otel::QueryAnnotateExt as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let rows: Vec = sqlx::query_scalar("SELECT 1 UNION ALL SELECT 2") + .with_annotations($crate::common::test_annotations()) + .fetch_all(&pool) + .await + .unwrap(); + assert_eq!(rows, vec![1, 2]); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_annotated_span(&spans[0], &$dialect); + }}; +} + +/// `sqlx::query_scalar(...).with_annotations(...).fetch_one(&pool)`. +#[macro_export] +macro_rules! test_query_scalar_fetch_one_with_annotations_via_pool { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx_otel::QueryAnnotateExt as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let value: i32 = sqlx::query_scalar("SELECT 42") + .with_annotations($crate::common::test_annotations()) + .fetch_one(&pool) + .await + .unwrap(); + assert_eq!(value, 42); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_annotated_span(&spans[0], &$dialect); + }}; +} + +/// `sqlx::query_scalar(...).with_annotations(...).fetch_optional(&pool)` returning none. +#[macro_export] +macro_rules! test_query_scalar_fetch_optional_with_annotations_via_pool { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx_otel::QueryAnnotateExt as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let value: Option = sqlx::query_scalar("SELECT 1 WHERE 1 = 0") + .with_annotations($crate::common::test_annotations()) + .fetch_optional(&pool) + .await + .unwrap(); + assert!(value.is_none()); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_annotated_span(&spans[0], &$dialect); + }}; +} + +// --------------------------------------------------------------------------- +// query-side annotations: Map (Query::map / Query::try_map) +// --------------------------------------------------------------------------- +// +// The closure parameter type for `.map(|row| ...)` is inferred from the surrounding +// `Query` chain, so we don't need to spell out per-backend `SqliteRow` / `PgRow` / +// `MySqlRow`. + +/// `Query::with_annotations` before `bind`/`map`. Position-1 in the builder pipeline. +#[macro_export] +macro_rules! test_query_map_position_1_via_pool { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx::Row as _; + use sqlx_otel::QueryAnnotateExt as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let value: i32 = sqlx::query("SELECT 7") + .with_annotations($crate::common::test_annotations()) + .map(|row: Row| row.get::(0)) + .fetch_one(&pool) + .await + .unwrap(); + assert_eq!(value, 7); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_annotated_span(&spans[0], &$dialect); + }}; +} + +/// `Query::with_annotations` after `bind`, before `map`. +#[macro_export] +macro_rules! test_query_map_position_2_via_pool { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx::Row as _; + use sqlx_otel::QueryAnnotateExt as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let value: i32 = sqlx::query("SELECT 11") + .with_annotations($crate::common::test_annotations()) + .map(|row: Row| row.get::(0)) + .fetch_one(&pool) + .await + .unwrap(); + assert_eq!(value, 11); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_annotated_span(&spans[0], &$dialect); + }}; +} + +/// `Query::with_annotations` after `bind` and `map` — last in the pipeline. +#[macro_export] +macro_rules! test_query_map_position_3_via_pool { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx::Row as _; + use sqlx_otel::QueryAnnotateExt as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let value: i32 = sqlx::query("SELECT 13") + .map(|row: Row| row.get::(0)) + .with_annotations($crate::common::test_annotations()) + .fetch_one(&pool) + .await + .unwrap(); + assert_eq!(value, 13); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_annotated_span(&spans[0], &$dialect); + }}; +} + +/// Same as `query_map_position_3` but with `try_map`. +#[macro_export] +macro_rules! test_query_try_map_position_3_via_pool { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx::Row as _; + use sqlx_otel::QueryAnnotateExt as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let value: i32 = sqlx::query("SELECT 17") + .try_map(|row: Row| Ok(row.get::(0))) + .with_annotations($crate::common::test_annotations()) + .fetch_one(&pool) + .await + .unwrap(); + assert_eq!(value, 17); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_annotated_span(&spans[0], &$dialect); + }}; +} + +/// `Query::map` then `with_annotations` then `fetch(&pool)`. +#[macro_export] +macro_rules! test_map_fetch_with_annotations_via_pool { + ($pool_factory:expr, $dialect:expr) => {{ + use futures::StreamExt as _; + use sqlx::Row as _; + use sqlx_otel::QueryAnnotateExt as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let mut stream = sqlx::query("SELECT 1 UNION ALL SELECT 2") + .map(|row: Row| row.get::(0)) + .with_annotations($crate::common::test_annotations()) + .fetch(&pool); + while stream.next().await.is_some() {} + drop(stream); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_annotated_span(&spans[0], &$dialect); + assert_eq!( + $crate::common::attr(&spans[0], "db.response.returned_rows"), + Some(opentelemetry::Value::I64(2)) + ); + }}; +} + +/// `Query::map` then `with_annotations` then `fetch_many(&pool)`. +#[macro_export] +macro_rules! test_map_fetch_many_with_annotations_via_pool { + ($pool_factory:expr, $dialect:expr) => {{ + use futures::StreamExt as _; + use sqlx::Row as _; + use sqlx_otel::QueryAnnotateExt as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + #[allow(deprecated)] + let mut stream = sqlx::query("SELECT 1 UNION ALL SELECT 2") + .map(|row: Row| row.get::(0)) + .with_annotations($crate::common::test_annotations()) + .fetch_many(&pool); + while stream.next().await.is_some() {} + drop(stream); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_annotated_span(&spans[0], &$dialect); + }}; +} + +/// `Query::map` then `with_annotations` then `fetch_all(&pool)`. +#[macro_export] +macro_rules! test_map_fetch_all_with_annotations_via_pool { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx::Row as _; + use sqlx_otel::QueryAnnotateExt as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let rows: Vec = sqlx::query("SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3") + .map(|row: Row| row.get::(0)) + .with_annotations($crate::common::test_annotations()) + .fetch_all(&pool) + .await + .unwrap(); + assert_eq!(rows, vec![1, 2, 3]); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_annotated_span(&spans[0], &$dialect); + }}; +} + +/// `Query::map` then `with_annotations` then `fetch_one(&pool)`. +#[macro_export] +macro_rules! test_map_fetch_one_with_annotations_via_pool { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx::Row as _; + use sqlx_otel::QueryAnnotateExt as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let value: i32 = sqlx::query("SELECT 19") + .map(|row: Row| row.get::(0)) + .with_annotations($crate::common::test_annotations()) + .fetch_one(&pool) + .await + .unwrap(); + assert_eq!(value, 19); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_annotated_span(&spans[0], &$dialect); + }}; +} + +/// `Query::map` then `with_annotations` then `fetch_optional(&pool)`. +#[macro_export] +macro_rules! test_map_fetch_optional_with_annotations_via_pool { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx::Row as _; + use sqlx_otel::QueryAnnotateExt as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let value: Option = sqlx::query("SELECT 1 WHERE 1 = 0") + .map(|row: Row| row.get::(0)) + .with_annotations($crate::common::test_annotations()) + .fetch_optional(&pool) + .await + .unwrap(); + assert!(value.is_none()); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_annotated_span(&spans[0], &$dialect); + }}; +} + +/// Composing two `map` calls, with annotations between them. +#[macro_export] +macro_rules! test_map_compose_after_annotations_via_pool { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx::Row as _; + use sqlx_otel::QueryAnnotateExt as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let value: i32 = sqlx::query("SELECT 5") + .map(|row: Row| row.get::(0)) + .with_annotations($crate::common::test_annotations()) + .map(|n| n * 2) + .fetch_one(&pool) + .await + .unwrap(); + assert_eq!(value, 10); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_annotated_span(&spans[0], &$dialect); + }}; +} + +/// Composing `map` then `try_map`, with annotations between. +#[macro_export] +macro_rules! test_map_try_map_compose_after_annotations_via_pool { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx::Row as _; + use sqlx_otel::QueryAnnotateExt as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let value: i32 = sqlx::query("SELECT 6") + .map(|row: Row| row.get::(0)) + .with_annotations($crate::common::test_annotations()) + .try_map(|n: i32| Ok::<_, sqlx::Error>(n + 100)) + .fetch_one(&pool) + .await + .unwrap(); + assert_eq!(value, 106); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_annotated_span(&spans[0], &$dialect); + }}; +} + +/// `Query::map` against `&mut PoolConnection`. +#[macro_export] +macro_rules! test_query_map_with_annotations_via_connection { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx::Row as _; + use sqlx_otel::QueryAnnotateExt as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let mut conn = pool.acquire().await.unwrap(); + let value: i32 = sqlx::query("SELECT 23") + .map(|row: Row| row.get::(0)) + .with_annotations($crate::common::test_annotations()) + .fetch_one(&mut conn) + .await + .unwrap(); + assert_eq!(value, 23); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_annotated_span(&spans[0], &$dialect); + }}; +} + +/// `Query::map` against `&mut Transaction<'_, DB>`. +#[macro_export] +macro_rules! test_query_map_with_annotations_via_transaction { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx::Row as _; + use sqlx_otel::QueryAnnotateExt as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let mut tx = pool.begin().await.unwrap(); + let value: i32 = sqlx::query("SELECT 29") + .map(|row: Row| row.get::(0)) + .with_annotations($crate::common::test_annotations()) + .fetch_one(&mut tx) + .await + .unwrap(); + assert_eq!(value, 29); + tx.commit().await.unwrap(); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_annotated_span(&spans[0], &$dialect); + }}; +} + +/// `Query::map` against invalid SQL records an error span. +#[macro_export] +macro_rules! test_query_map_with_annotations_records_error { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx::Row as _; + use sqlx_otel::QueryAnnotateExt as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let result: Result = sqlx::query("INVALID SQL") + .map(|row: Row| row.get::(0)) + .with_annotations($crate::common::test_annotations()) + .fetch_one(&pool) + .await; + assert!(result.is_err()); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_annotated_span(&spans[0], &$dialect); + $crate::common::assert_error_span(&spans[0]); + }}; +} + +/// `try_map` returning a mapper-side error: the database round-trip succeeds, the span +/// reports success, but the user-visible `Result` carries the mapper's error. +#[macro_export] +macro_rules! test_query_try_map_with_annotations_propagates_mapper_error { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx_otel::QueryAnnotateExt as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let result: Result = sqlx::query("SELECT 1") + .try_map(|_row: Row| { + Err::(sqlx::Error::Decode( + "intentional decode failure".to_string().into(), + )) + }) + .with_annotations($crate::common::test_annotations()) + .fetch_one(&pool) + .await; + assert!(result.is_err()); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_annotated_span(&spans[0], &$dialect); + }}; +} + +// --------------------------------------------------------------------------- +// PoolBuilder with_* methods +// --------------------------------------------------------------------------- +// +// These macros accept a *raw* pool factory (not the wrapped `Pool`) so the test +// body can configure `PoolBuilder` itself with the override under test. + +/// `PoolBuilder::with_database` overrides the inferred `db.namespace`. +#[macro_export] +macro_rules! test_builder_with_database_overrides_namespace { + ($raw_pool_factory:expr, $dialect:expr) => {{ + use sqlx::Executor as _; + let _ = $dialect; + let tel = $crate::common::TestTelemetry::install(); + let raw = $raw_pool_factory; + let pool = sqlx_otel::PoolBuilder::from(raw) + .with_database("custom_db") + .build(); + + let _ = (&pool).fetch_optional("SELECT 1").await.unwrap(); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + assert_eq!( + $crate::common::attr(&spans[0], "db.namespace"), + Some(opentelemetry::Value::String("custom_db".into())) + ); + }}; +} + +/// `PoolBuilder::with_host` overrides the inferred `server.address`. +#[macro_export] +macro_rules! test_builder_with_host_overrides_server_address { + ($raw_pool_factory:expr, $dialect:expr) => {{ + use sqlx::Executor as _; + let _ = $dialect; + let tel = $crate::common::TestTelemetry::install(); + let raw = $raw_pool_factory; + let pool = sqlx_otel::PoolBuilder::from(raw) + .with_host("custom-host") + .build(); + + let _ = (&pool).fetch_optional("SELECT 1").await.unwrap(); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + assert_eq!( + $crate::common::attr(&spans[0], "server.address"), + Some(opentelemetry::Value::String("custom-host".into())) + ); + }}; +} + +/// `PoolBuilder::with_port` overrides the inferred `server.port`. +#[macro_export] +macro_rules! test_builder_with_port_overrides_server_port { + ($raw_pool_factory:expr, $dialect:expr) => {{ + use sqlx::Executor as _; + let _ = $dialect; + let tel = $crate::common::TestTelemetry::install(); + let raw = $raw_pool_factory; + let pool = sqlx_otel::PoolBuilder::from(raw).with_port(9999).build(); + + let _ = (&pool).fetch_optional("SELECT 1").await.unwrap(); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + assert_eq!( + $crate::common::attr(&spans[0], "server.port"), + Some(opentelemetry::Value::I64(9999)) + ); + }}; +} + +/// `PoolBuilder::with_network_peer_address` populates `network.peer.address`. +#[macro_export] +macro_rules! test_builder_with_network_peer_address { + ($raw_pool_factory:expr, $dialect:expr) => {{ + use sqlx::Executor as _; + let _ = $dialect; + let tel = $crate::common::TestTelemetry::install(); + let raw = $raw_pool_factory; + let pool = sqlx_otel::PoolBuilder::from(raw) + .with_network_peer_address("10.0.0.5") + .build(); + + let _ = (&pool).fetch_optional("SELECT 1").await.unwrap(); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + assert_eq!( + $crate::common::attr(&spans[0], "network.peer.address"), + Some(opentelemetry::Value::String("10.0.0.5".into())) + ); + }}; +} + +/// `PoolBuilder::with_network_peer_port` populates `network.peer.port`. +#[macro_export] +macro_rules! test_builder_with_network_peer_port { + ($raw_pool_factory:expr, $dialect:expr) => {{ + use sqlx::Executor as _; + let _ = $dialect; + let tel = $crate::common::TestTelemetry::install(); + let raw = $raw_pool_factory; + let pool = sqlx_otel::PoolBuilder::from(raw) + .with_network_peer_port(5433) + .build(); + + let _ = (&pool).fetch_optional("SELECT 1").await.unwrap(); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + assert_eq!( + $crate::common::attr(&spans[0], "network.peer.port"), + Some(opentelemetry::Value::I64(5433)) + ); + }}; +} + +/// `Pool::close` and `Pool::is_closed` round-trip. +#[macro_export] +macro_rules! test_pool_close_and_is_closed { + ($pool_factory:expr, $dialect:expr) => {{ + let _ = $dialect; + let _tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + assert!(!pool.is_closed()); + pool.close().await; + assert!(pool.is_closed()); + }}; +} + +/// `QueryTextMode::Off` suppresses the `db.query.text` attribute. +#[macro_export] +macro_rules! test_query_text_mode_off_suppresses_sql { + ($raw_pool_factory:expr, $dialect:expr) => {{ + let tel = $crate::common::TestTelemetry::install(); + let raw = $raw_pool_factory; + let pool = sqlx_otel::PoolBuilder::from(raw) + .with_query_text_mode(sqlx_otel::QueryTextMode::Off) + .build(); + + let _: Option<(i32,)> = sqlx::query_as("SELECT 1") + .fetch_optional(&pool) + .await + .unwrap(); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + assert_eq!(spans[0].span_kind, opentelemetry::trace::SpanKind::Client); + assert_eq!( + $crate::common::attr(&spans[0], "db.system.name"), + Some(opentelemetry::Value::String($dialect.system.into())) + ); + assert!($crate::common::attr(&spans[0], "db.namespace").is_some()); + assert!( + $crate::common::attr(&spans[0], "db.query.text").is_none(), + "db.query.text should not be present when QueryTextMode::Off" + ); + }}; +} diff --git a/tests/mysql.rs b/tests/mysql.rs index aee765b..83cb6e6 100644 --- a/tests/mysql.rs +++ b/tests/mysql.rs @@ -8,13 +8,12 @@ use std::time::Duration; use common::{ assert_annotated_span, assert_common_span_attributes, assert_error_span, attr, test_annotations, }; -use futures::StreamExt; use opentelemetry::trace::SpanKind; use serial_test::serial; use sqlx::Executor as _; use sqlx::MySql; use sqlx::Row as _; -use sqlx_otel::{Pool, PoolBuilder, QueryAnnotateExt, QueryAnnotations, Transaction}; +use sqlx_otel::{Pool, PoolBuilder, QueryAnnotateExt, Transaction}; use testcontainers::core::IntoContainerPort; use testcontainers::runners::AsyncRunner; use testcontainers::{ContainerAsync, GenericImage, ImageExt}; @@ -22,6 +21,9 @@ use tokio::sync::OnceCell; const SYSTEM: &str = "mysql"; +/// Backend row type used by parameterised map test macros (see `tests/sqlite.rs`). +type Row = sqlx::mysql::MySqlRow; + /// Shared container and connection URL, initialised once across all tests. struct SharedContainer { _container: ContainerAsync, @@ -59,9 +61,14 @@ async fn shared_container() -> &'static SharedContainer { /// Return an instrumented pool connected to the shared container. async fn test_pool() -> Pool { + PoolBuilder::from(raw_pool().await).build() +} + +/// Raw (un-instrumented) sqlx pool, used by parameterised builder / query-text-mode +/// tests that need to apply specific `PoolBuilder` configurations themselves. +async fn raw_pool() -> sqlx::MySqlPool { let shared = shared_container().await; - let raw = sqlx::MySqlPool::connect(&shared.url).await.unwrap(); - PoolBuilder::from(raw).build() + sqlx::MySqlPool::connect(&shared.url).await.unwrap() } // =========================================================================== @@ -77,104 +84,19 @@ async fn execute_creates_span_via_pool() { #[tokio::test] #[serial] async fn execute_creates_span_via_connection() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - sqlx::query("CREATE TABLE IF NOT EXISTS exec_conn (id INT AUTO_INCREMENT PRIMARY KEY)") - .execute(&mut conn) - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - assert!(attr(&spans[0], "db.response.affected_rows").is_some()); - - // With annotations - conn.with_annotations(test_annotations()) - .execute("CREATE TABLE IF NOT EXISTS exec_conn (id INT AUTO_INCREMENT PRIMARY KEY)") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); - - // With shorthand - conn.with_operation("SELECT", "users") - .execute("CREATE TABLE IF NOT EXISTS exec_conn3 (id INT AUTO_INCREMENT PRIMARY KEY)") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); + test_execute_creates_span_via_connection!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn execute_creates_span_via_transaction() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut tx: Transaction<'_, MySql> = pool.begin().await.unwrap(); - sqlx::query("CREATE TABLE IF NOT EXISTS exec_tx (id INT AUTO_INCREMENT PRIMARY KEY)") - .execute(&mut tx) - .await - .unwrap(); - - // With annotations - tx.with_annotations(test_annotations()) - .execute("CREATE TABLE IF NOT EXISTS exec_tx (id INT AUTO_INCREMENT PRIMARY KEY)") - .await - .unwrap(); - - // With shorthand - tx.with_operation("SELECT", "users") - .execute("CREATE TABLE IF NOT EXISTS exec_tx3 (id INT AUTO_INCREMENT PRIMARY KEY)") - .await - .unwrap(); - tx.commit().await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 3); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - assert!(attr(&spans[0], "db.response.affected_rows").is_some()); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); + test_execute_creates_span_via_transaction!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn execute_records_error() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let result = sqlx::query("INVALID SQL GIBBERISH").execute(&pool).await; - assert!(result.is_err()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_error_span(&spans[0]); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - - // With annotations (error path) - let result = pool - .with_annotations(test_annotations()) - .execute("INVALID SQL GIBBERISH") - .await; - assert!(result.is_err()); - let spans = tel.spans(); - let last = spans.last().unwrap(); - assert_annotated_span(last, &common::MYSQL_DIALECT); - assert_error_span(last); - - // With shorthand (error path) - let result = pool - .with_operation("SELECT", "users") - .execute("INVALID SQL GIBBERISH") - .await; - assert!(result.is_err()); - let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last, &common::MYSQL_DIALECT); - assert_error_span(&last); + test_execute_records_error!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] @@ -285,153 +207,25 @@ async fn execute_records_affected_rows() { #[tokio::test] #[serial] async fn execute_many_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut stream = (&pool).execute_many("SELECT 1; SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(0)) - ); - - // With annotations - let mut stream = pool - .with_annotations(test_annotations()) - .execute_many("SELECT 1; SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); - - // With shorthand - let mut stream = pool - .with_operation("SELECT", "users") - .execute_many("SELECT 1; SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); + test_execute_many_via_pool!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn execute_many_via_connection() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - let mut stream = (&mut conn).execute_many("SELECT 1; SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(0)) - ); - - // With annotations - let mut stream = conn - .with_annotations(test_annotations()) - .execute_many("SELECT 1; SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); - - // With shorthand - let mut stream = conn - .with_operation("SELECT", "users") - .execute_many("SELECT 1; SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); + test_execute_many_via_connection!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn execute_many_via_transaction() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut tx: Transaction<'_, MySql> = pool.begin().await.unwrap(); - - let mut stream = (&mut tx).execute_many("SELECT 1; SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - - // With annotations - let mut stream = tx - .with_annotations(test_annotations()) - .execute_many("SELECT 1; SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - - // With shorthand - let mut stream = tx - .with_operation("SELECT", "users") - .execute_many("SELECT 1; SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - tx.commit().await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 3); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(0)) - ); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); + test_execute_many_via_transaction!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn execute_many_records_error() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut stream = (&pool).execute_many("INVALID SQL GIBBERISH"); - let result = stream.next().await; - assert!(result.is_some_and(|r| r.is_err())); - drop(stream); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_error_span(&spans[0]); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(0)) - ); - - // With annotations (error path) - let mut stream = pool - .with_annotations(test_annotations()) - .execute_many("INVALID SQL GIBBERISH"); - let result = stream.next().await; - assert!(result.is_some_and(|r| r.is_err())); - drop(stream); - let spans = tel.spans(); - let last = spans.last().unwrap(); - assert_annotated_span(last, &common::MYSQL_DIALECT); - assert_error_span(last); - - // With shorthand (error path) - let mut stream = pool - .with_operation("SELECT", "users") - .execute_many("INVALID SQL GIBBERISH"); - let result = stream.next().await; - assert!(result.is_some_and(|r| r.is_err())); - drop(stream); - let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last, &common::MYSQL_DIALECT); - assert_error_span(&last); + test_execute_many_records_error!(test_pool().await, common::MYSQL_DIALECT); } // =========================================================================== @@ -441,175 +235,31 @@ async fn execute_many_records_error() { #[tokio::test] #[serial] async fn fetch_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut stream = (&pool).fetch("SELECT 1 UNION ALL SELECT 2"); - let mut count = 0u64; - while stream.next().await.is_some() { - count += 1; - } - assert_eq!(count, 2); - drop(stream); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(2)) - ); - - // With annotations - let mut stream = pool - .with_annotations(test_annotations()) - .fetch("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); - - // With shorthand - let mut stream = pool - .with_operation("SELECT", "users") - .fetch("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); + test_fetch_via_pool!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn fetch_via_connection() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - let mut stream = (&mut conn).fetch("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(2)) - ); - - // With annotations - let mut stream = conn - .with_annotations(test_annotations()) - .fetch("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); - - // With shorthand - let mut stream = conn - .with_operation("SELECT", "users") - .fetch("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); + test_fetch_via_connection!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn fetch_via_transaction() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut tx: Transaction<'_, MySql> = pool.begin().await.unwrap(); - - let mut stream = (&mut tx).fetch("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - - // With annotations - let mut stream = tx - .with_annotations(test_annotations()) - .fetch("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - - // With shorthand - let mut stream = tx - .with_operation("SELECT", "users") - .fetch("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - tx.commit().await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 3); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(2)) - ); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); + test_fetch_via_transaction!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn fetch_stream_dropped_early_still_records_span() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - { - let mut stream = (&pool).fetch("SELECT 1 UNION ALL SELECT 2"); - let _ = stream.next().await; - } - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(1)) - ); + test_fetch_stream_dropped_early_still_records_span!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn fetch_stream_records_error() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut stream = (&pool).fetch("INVALID SQL"); - let result = stream.next().await; - assert!(result.is_some_and(|r| r.is_err())); - drop(stream); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(0)) - ); - assert_error_span(&spans[0]); - - // With annotations (error path) - let mut stream = pool - .with_annotations(test_annotations()) - .fetch("INVALID SQL"); - let result = stream.next().await; - assert!(result.is_some_and(|r| r.is_err())); - drop(stream); - let spans = tel.spans(); - let last = spans.last().unwrap(); - assert_annotated_span(last, &common::MYSQL_DIALECT); - assert_error_span(last); - - // With shorthand (error path) - let mut stream = pool.with_operation("SELECT", "users").fetch("INVALID SQL"); - let result = stream.next().await; - assert!(result.is_some_and(|r| r.is_err())); - drop(stream); - let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last, &common::MYSQL_DIALECT); - assert_error_span(&last); + test_fetch_stream_records_error!(test_pool().await, common::MYSQL_DIALECT); } // =========================================================================== @@ -619,179 +269,31 @@ async fn fetch_stream_records_error() { #[tokio::test] #[serial] async fn fetch_many_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut stream = (&pool).fetch_many("SELECT 1 UNION ALL SELECT 2"); - let mut rows = 0u64; - while let Some(item) = stream.next().await { - if let Ok(sqlx::Either::Right(_)) = item { - rows += 1; - } - } - drop(stream); - assert_eq!(rows, 2); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(2)) - ); - - // With annotations - let mut stream = pool - .with_annotations(test_annotations()) - .fetch_many("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); - - // With shorthand - let mut stream = pool - .with_operation("SELECT", "users") - .fetch_many("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); + test_fetch_many_via_pool!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn fetch_many_via_connection() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - let mut stream = (&mut conn).fetch_many("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(2)) - ); - - // With annotations - let mut stream = conn - .with_annotations(test_annotations()) - .fetch_many("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); - - // With shorthand - let mut stream = conn - .with_operation("SELECT", "users") - .fetch_many("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); + test_fetch_many_via_connection!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn fetch_many_via_transaction() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut tx: Transaction<'_, MySql> = pool.begin().await.unwrap(); - - let mut stream = (&mut tx).fetch_many("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - - // With annotations - let mut stream = tx - .with_annotations(test_annotations()) - .fetch_many("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - - // With shorthand - let mut stream = tx - .with_operation("SELECT", "users") - .fetch_many("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - tx.commit().await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 3); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(2)) - ); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); + test_fetch_many_via_transaction!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn fetch_many_dropped_early_still_records_span() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - { - let mut stream = (&pool).fetch_many("SELECT 1 UNION ALL SELECT 2"); - let _ = stream.next().await; - } - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(1)) - ); + test_fetch_many_dropped_early_still_records_span!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn fetch_many_records_error() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut stream = (&pool).fetch_many("INVALID SQL GIBBERISH"); - let result = stream.next().await; - assert!(result.is_some_and(|r| r.is_err())); - drop(stream); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(0)) - ); - assert_error_span(&spans[0]); - - // With annotations (error path) - let mut stream = pool - .with_annotations(test_annotations()) - .fetch_many("INVALID SQL GIBBERISH"); - let result = stream.next().await; - assert!(result.is_some_and(|r| r.is_err())); - drop(stream); - let spans = tel.spans(); - let last = spans.last().unwrap(); - assert_annotated_span(last, &common::MYSQL_DIALECT); - assert_error_span(last); - - // With shorthand (error path) - let mut stream = pool - .with_operation("SELECT", "users") - .fetch_many("INVALID SQL GIBBERISH"); - let result = stream.next().await; - assert!(result.is_some_and(|r| r.is_err())); - drop(stream); - let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last, &common::MYSQL_DIALECT); - assert_error_span(&last); + test_fetch_many_records_error!(test_pool().await, common::MYSQL_DIALECT); } // =========================================================================== @@ -801,145 +303,25 @@ async fn fetch_many_records_error() { #[tokio::test] #[serial] async fn fetch_all_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let rows = (&pool) - .fetch_all("SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3") - .await - .unwrap(); - assert_eq!(rows.len(), 3); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(3)) - ); - - // With annotations - pool.with_annotations(test_annotations()) - .fetch_all("SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); - - // With shorthand - pool.with_operation("SELECT", "users") - .fetch_all("SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); + test_fetch_all_via_pool!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn fetch_all_via_connection() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - let rows = (&mut conn) - .fetch_all("SELECT 1 UNION ALL SELECT 2") - .await - .unwrap(); - assert_eq!(rows.len(), 2); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(2)) - ); - - // With annotations - conn.with_annotations(test_annotations()) - .fetch_all("SELECT 1 UNION ALL SELECT 2") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); - - // With shorthand - conn.with_operation("SELECT", "users") - .fetch_all("SELECT 1 UNION ALL SELECT 2") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); + test_fetch_all_via_connection!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn fetch_all_via_transaction() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut tx: Transaction<'_, MySql> = pool.begin().await.unwrap(); - let rows = (&mut tx) - .fetch_all("SELECT 1 UNION ALL SELECT 2") - .await - .unwrap(); - assert_eq!(rows.len(), 2); - - // With annotations - tx.with_annotations(test_annotations()) - .fetch_all("SELECT 1 UNION ALL SELECT 2") - .await - .unwrap(); - - // With shorthand - tx.with_operation("SELECT", "users") - .fetch_all("SELECT 1 UNION ALL SELECT 2") - .await - .unwrap(); - tx.commit().await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 3); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(2)) - ); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); + test_fetch_all_via_transaction!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn fetch_all_records_error() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let result = (&pool).fetch_all("INVALID SQL GIBBERISH").await; - assert!(result.is_err()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - assert_error_span(&spans[0]); - - // With annotations (error path) - let result = pool - .with_annotations(test_annotations()) - .fetch_all("INVALID SQL GIBBERISH") - .await; - assert!(result.is_err()); - let spans = tel.spans(); - let last = spans.last().unwrap(); - assert_annotated_span(last, &common::MYSQL_DIALECT); - assert_error_span(last); - - // With shorthand (error path) - let result = pool - .with_operation("SELECT", "users") - .fetch_all("INVALID SQL GIBBERISH") - .await; - assert!(result.is_err()); - let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last, &common::MYSQL_DIALECT); - assert_error_span(&last); + test_fetch_all_records_error!(test_pool().await, common::MYSQL_DIALECT); } // =========================================================================== @@ -949,133 +331,25 @@ async fn fetch_all_records_error() { #[tokio::test] #[serial] async fn fetch_one_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let _row = (&pool).fetch_one("SELECT 1").await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(1)) - ); - - // With annotations - pool.with_annotations(test_annotations()) - .fetch_one("SELECT 1") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); - - // With shorthand - pool.with_operation("SELECT", "users") - .fetch_one("SELECT 1") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); + test_fetch_one_via_pool!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn fetch_one_via_connection() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - let _row = (&mut conn).fetch_one("SELECT 1").await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(1)) - ); - - // With annotations - conn.with_annotations(test_annotations()) - .fetch_one("SELECT 1") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); - - // With shorthand - conn.with_operation("SELECT", "users") - .fetch_one("SELECT 1") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); + test_fetch_one_via_connection!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn fetch_one_via_transaction() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut tx: Transaction<'_, MySql> = pool.begin().await.unwrap(); - let _row = (&mut tx).fetch_one("SELECT 1").await.unwrap(); - - // With annotations - tx.with_annotations(test_annotations()) - .fetch_one("SELECT 1") - .await - .unwrap(); - - // With shorthand - tx.with_operation("SELECT", "users") - .fetch_one("SELECT 1") - .await - .unwrap(); - tx.commit().await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 3); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(1)) - ); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); + test_fetch_one_via_transaction!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn fetch_one_records_error() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let result = (&pool).fetch_one("INVALID SQL GIBBERISH").await; - assert!(result.is_err()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - assert_error_span(&spans[0]); - - // With annotations (error path) - let result = pool - .with_annotations(test_annotations()) - .fetch_one("INVALID SQL GIBBERISH") - .await; - assert!(result.is_err()); - let spans = tel.spans(); - let last = spans.last().unwrap(); - assert_annotated_span(last, &common::MYSQL_DIALECT); - assert_error_span(last); - - // With shorthand (error path) - let result = pool - .with_operation("SELECT", "users") - .fetch_one("INVALID SQL GIBBERISH") - .await; - assert!(result.is_err()); - let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last, &common::MYSQL_DIALECT); - assert_error_span(&last); + test_fetch_one_records_error!(test_pool().await, common::MYSQL_DIALECT); } // =========================================================================== @@ -1085,33 +359,7 @@ async fn fetch_one_records_error() { #[tokio::test] #[serial] async fn fetch_optional_records_one_row() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let result = (&pool).fetch_optional("SELECT 1").await.unwrap(); - assert!(result.is_some()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(1)) - ); - - // With annotations - pool.with_annotations(test_annotations()) - .fetch_optional("SELECT 1") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); - - // With shorthand - pool.with_operation("SELECT", "users") - .fetch_optional("SELECT 1") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); + test_fetch_optional_records_one_row!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] @@ -1148,104 +396,19 @@ async fn fetch_optional_records_zero_rows() { #[tokio::test] #[serial] async fn fetch_optional_via_connection() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - let result = (&mut conn).fetch_optional("SELECT 42").await.unwrap(); - assert!(result.is_some()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(1)) - ); - - // With annotations - conn.with_annotations(test_annotations()) - .fetch_optional("SELECT 42") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); - - // With shorthand - conn.with_operation("SELECT", "users") - .fetch_optional("SELECT 42") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); + test_fetch_optional_via_connection!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn fetch_optional_via_transaction() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut tx: Transaction<'_, MySql> = pool.begin().await.unwrap(); - let result = (&mut tx).fetch_optional("SELECT 99").await.unwrap(); - assert!(result.is_some()); - - // With annotations - tx.with_annotations(test_annotations()) - .fetch_optional("SELECT 99") - .await - .unwrap(); - - // With shorthand - tx.with_operation("SELECT", "users") - .fetch_optional("SELECT 99") - .await - .unwrap(); - tx.commit().await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 3); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(1)) - ); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); + test_fetch_optional_via_transaction!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn fetch_optional_records_error() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let result = (&pool).fetch_optional("INVALID SQL GIBBERISH").await; - assert!(result.is_err()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - assert_error_span(&spans[0]); - - // With annotations (error path) - let result = pool - .with_annotations(test_annotations()) - .fetch_optional("INVALID SQL GIBBERISH") - .await; - assert!(result.is_err()); - let spans = tel.spans(); - let last = spans.last().unwrap(); - assert_annotated_span(last, &common::MYSQL_DIALECT); - assert_error_span(last); - - // With shorthand (error path) - let result = pool - .with_operation("SELECT", "users") - .fetch_optional("INVALID SQL GIBBERISH") - .await; - assert!(result.is_err()); - let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last, &common::MYSQL_DIALECT); - assert_error_span(&last); + test_fetch_optional_records_error!(test_pool().await, common::MYSQL_DIALECT); } // =========================================================================== @@ -1255,125 +418,25 @@ async fn fetch_optional_records_error() { #[tokio::test] #[serial] async fn prepare_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let _stmt = (&pool).prepare("SELECT 1").await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - - // With annotations - pool.with_annotations(test_annotations()) - .prepare("SELECT 1") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); - - // With shorthand - pool.with_operation("SELECT", "users") - .prepare("SELECT 1") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); + test_prepare_via_pool!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn prepare_via_connection() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - let _stmt = (&mut conn).prepare("SELECT 1").await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - - // With annotations - conn.with_annotations(test_annotations()) - .prepare("SELECT 1") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); - - // With shorthand - conn.with_operation("SELECT", "users") - .prepare("SELECT 1") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); + test_prepare_via_connection!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn prepare_via_transaction() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut tx: Transaction<'_, MySql> = pool.begin().await.unwrap(); - let _stmt = (&mut tx).prepare("SELECT 1").await.unwrap(); - - // With annotations - tx.with_annotations(test_annotations()) - .prepare("SELECT 1") - .await - .unwrap(); - - // With shorthand - tx.with_operation("SELECT", "users") - .prepare("SELECT 1") - .await - .unwrap(); - tx.commit().await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 3); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); + test_prepare_via_transaction!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn prepare_records_error() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - let result = (&mut conn).prepare("INVALID SQL GIBBERISH").await; - assert!(result.is_err()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - assert_error_span(&spans[0]); - - // With annotations (error path) - let result = conn - .with_annotations(test_annotations()) - .prepare("INVALID SQL GIBBERISH") - .await; - assert!(result.is_err()); - let spans = tel.spans(); - let last = spans.last().unwrap(); - assert_annotated_span(last, &common::MYSQL_DIALECT); - assert_error_span(last); - - // With shorthand (error path) - let result = conn - .with_operation("SELECT", "users") - .prepare("INVALID SQL GIBBERISH") - .await; - assert!(result.is_err()); - let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last, &common::MYSQL_DIALECT); - assert_error_span(&last); + test_prepare_records_error!(test_pool().await, common::MYSQL_DIALECT); } // =========================================================================== @@ -1383,125 +446,25 @@ async fn prepare_records_error() { #[tokio::test] #[serial] async fn prepare_with_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let _stmt = (&pool).prepare_with("SELECT ?", &[]).await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - - // With annotations - pool.with_annotations(test_annotations()) - .prepare_with("SELECT ?", &[]) - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); - - // With shorthand - pool.with_operation("SELECT", "users") - .prepare_with("SELECT ?", &[]) - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); + test_prepare_with_via_pool!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn prepare_with_via_connection() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - let _stmt = (&mut conn).prepare_with("SELECT ?", &[]).await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - - // With annotations - conn.with_annotations(test_annotations()) - .prepare_with("SELECT ?", &[]) - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); - - // With shorthand - conn.with_operation("SELECT", "users") - .prepare_with("SELECT ?", &[]) - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); + test_prepare_with_via_connection!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn prepare_with_via_transaction() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut tx: Transaction<'_, MySql> = pool.begin().await.unwrap(); - let _stmt = (&mut tx).prepare_with("SELECT ?", &[]).await.unwrap(); - - // With annotations - tx.with_annotations(test_annotations()) - .prepare_with("SELECT ?", &[]) - .await - .unwrap(); - - // With shorthand - tx.with_operation("SELECT", "users") - .prepare_with("SELECT ?", &[]) - .await - .unwrap(); - tx.commit().await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 3); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); + test_prepare_with_via_transaction!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn prepare_with_records_error() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - let result = (&mut conn).prepare_with("INVALID SQL GIBBERISH", &[]).await; - assert!(result.is_err()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - assert_error_span(&spans[0]); - - // With annotations (error path) - let result = conn - .with_annotations(test_annotations()) - .prepare_with("INVALID SQL GIBBERISH", &[]) - .await; - assert!(result.is_err()); - let spans = tel.spans(); - let last = spans.last().unwrap(); - assert_annotated_span(last, &common::MYSQL_DIALECT); - assert_error_span(last); - - // With shorthand (error path) - let result = conn - .with_operation("SELECT", "users") - .prepare_with("INVALID SQL GIBBERISH", &[]) - .await; - assert!(result.is_err()); - let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last, &common::MYSQL_DIALECT); - assert_error_span(&last); + test_prepare_with_records_error!(test_pool().await, common::MYSQL_DIALECT); } // =========================================================================== @@ -1511,125 +474,25 @@ async fn prepare_with_records_error() { #[tokio::test] #[serial] async fn describe_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let _desc = (&pool).describe("SELECT 1").await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - - // With annotations - pool.with_annotations(test_annotations()) - .describe("SELECT 1") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); - - // With shorthand - pool.with_operation("SELECT", "users") - .describe("SELECT 1") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); + test_describe_via_pool!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn describe_via_connection() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - let _desc = (&mut conn).describe("SELECT 1").await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - - // With annotations - conn.with_annotations(test_annotations()) - .describe("SELECT 1") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); - - // With shorthand - conn.with_operation("SELECT", "users") - .describe("SELECT 1") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); + test_describe_via_connection!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn describe_via_transaction() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut tx: Transaction<'_, MySql> = pool.begin().await.unwrap(); - let _desc = (&mut tx).describe("SELECT 1").await.unwrap(); - - // With annotations - tx.with_annotations(test_annotations()) - .describe("SELECT 1") - .await - .unwrap(); - - // With shorthand - tx.with_operation("SELECT", "users") - .describe("SELECT 1") - .await - .unwrap(); - tx.commit().await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 3); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - assert_annotated_span(tel.spans().last().unwrap(), &common::MYSQL_DIALECT); + test_describe_via_transaction!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn describe_records_error() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - let result = (&mut conn).describe("INVALID SQL GIBBERISH").await; - assert!(result.is_err()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - assert_error_span(&spans[0]); - - // With annotations (error path) - let result = conn - .with_annotations(test_annotations()) - .describe("INVALID SQL GIBBERISH") - .await; - assert!(result.is_err()); - let spans = tel.spans(); - let last = spans.last().unwrap(); - assert_annotated_span(last, &common::MYSQL_DIALECT); - assert_error_span(last); - - // With shorthand (error path) - let result = conn - .with_operation("SELECT", "users") - .describe("INVALID SQL GIBBERISH") - .await; - assert!(result.is_err()); - let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last, &common::MYSQL_DIALECT); - assert_error_span(&last); + test_describe_records_error!(test_pool().await, common::MYSQL_DIALECT); } // =========================================================================== @@ -1777,93 +640,31 @@ async fn transaction_rollback() { #[tokio::test] #[serial] async fn builder_with_database_overrides_namespace() { - let tel = common::TestTelemetry::install(); - let shared = shared_container().await; - let raw = sqlx::MySqlPool::connect(&shared.url).await.unwrap(); - let pool = PoolBuilder::from(raw).with_database("custom_db").build(); - - let _ = (&pool).fetch_optional("SELECT 1").await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_eq!( - attr(&spans[0], "db.namespace"), - Some(opentelemetry::Value::String("custom_db".into())) - ); + test_builder_with_database_overrides_namespace!(raw_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn builder_with_host_overrides_server_address() { - let tel = common::TestTelemetry::install(); - let shared = shared_container().await; - let raw = sqlx::MySqlPool::connect(&shared.url).await.unwrap(); - let pool = PoolBuilder::from(raw).with_host("custom-host").build(); - - let _ = (&pool).fetch_optional("SELECT 1").await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_eq!( - attr(&spans[0], "server.address"), - Some(opentelemetry::Value::String("custom-host".into())) - ); + test_builder_with_host_overrides_server_address!(raw_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn builder_with_port_overrides_server_port() { - let tel = common::TestTelemetry::install(); - let shared = shared_container().await; - let raw = sqlx::MySqlPool::connect(&shared.url).await.unwrap(); - let pool = PoolBuilder::from(raw).with_port(9999).build(); - - let _ = (&pool).fetch_optional("SELECT 1").await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_eq!( - attr(&spans[0], "server.port"), - Some(opentelemetry::Value::I64(9999)) - ); + test_builder_with_port_overrides_server_port!(raw_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn builder_with_network_peer_address() { - let tel = common::TestTelemetry::install(); - let shared = shared_container().await; - let raw = sqlx::MySqlPool::connect(&shared.url).await.unwrap(); - let pool = PoolBuilder::from(raw) - .with_network_peer_address("10.0.0.5") - .build(); - - let _ = (&pool).fetch_optional("SELECT 1").await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_eq!( - attr(&spans[0], "network.peer.address"), - Some(opentelemetry::Value::String("10.0.0.5".into())) - ); + test_builder_with_network_peer_address!(raw_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn builder_with_network_peer_port() { - let tel = common::TestTelemetry::install(); - let shared = shared_container().await; - let raw = sqlx::MySqlPool::connect(&shared.url).await.unwrap(); - let pool = PoolBuilder::from(raw).with_network_peer_port(3307).build(); - - let _ = (&pool).fetch_optional("SELECT 1").await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_eq!( - attr(&spans[0], "network.peer.port"), - Some(opentelemetry::Value::I64(3307)) - ); + test_builder_with_network_peer_port!(raw_pool().await, common::MYSQL_DIALECT); } // =========================================================================== @@ -1941,75 +742,13 @@ async fn query_text_mode_obfuscated_replaces_literals() { #[tokio::test] #[serial] async fn annotation_all_four_fields() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - pool.with_annotations( - QueryAnnotations::new() - .operation("SELECT") - .collection("users") - .query_summary("users by id") - .stored_procedure("sp_get_users"), - ) - .fetch_all("SELECT 1") - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - // Summary drives the span name (semconv level 1), distinct from "SELECT users" so - // the assertion proves the summary path won rather than coinciding with level 2. - assert_eq!(spans[0].name, "users by id"); - assert_eq!( - attr(&spans[0], "db.operation.name"), - Some(opentelemetry::Value::String("SELECT".into())), - ); - assert_eq!( - attr(&spans[0], "db.collection.name"), - Some(opentelemetry::Value::String("users".into())), - ); - assert_eq!( - attr(&spans[0], "db.query.summary"), - Some(opentelemetry::Value::String("users by id".into())), - ); - assert_eq!( - attr(&spans[0], "db.stored_procedure.name"), - Some(opentelemetry::Value::String("sp_get_users".into())), - ); + test_annotation_all_four_fields!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn query_summary_drives_span_name() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - pool.with_annotations( - QueryAnnotations::new() - .operation("SELECT") - .collection("users") - .query_summary("users by tenant"), - ) - .fetch_all("SELECT 1") - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_eq!(spans[0].name, "users by tenant"); - // Summary drives the *name*, but does not suppress the other attributes. - assert_eq!( - attr(&spans[0], "db.query.summary"), - Some(opentelemetry::Value::String("users by tenant".into())), - ); - assert_eq!( - attr(&spans[0], "db.operation.name"), - Some(opentelemetry::Value::String("SELECT".into())), - ); - assert_eq!( - attr(&spans[0], "db.collection.name"), - Some(opentelemetry::Value::String("users".into())), - ); + test_query_summary_drives_span_name!(test_pool().await, common::MYSQL_DIALECT); } // =========================================================================== @@ -2037,102 +776,31 @@ async fn query_execute_with_annotations_via_pool() { #[tokio::test] #[serial] async fn query_execute_many_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - #[allow(deprecated)] - let mut stream = sqlx::query("SELECT 1; SELECT 2") - .with_annotations(test_annotations()) - .execute_many(&pool) - .await; - while stream.next().await.is_some() {} - drop(stream); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); + test_query_execute_many_with_annotations_via_pool!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn query_fetch_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut stream = sqlx::query("SELECT 1 UNION ALL SELECT 2") - .with_annotations(test_annotations()) - .fetch(&pool); - while stream.next().await.is_some() {} - drop(stream); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(2)) - ); + test_query_fetch_with_annotations_via_pool!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn query_fetch_many_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - #[allow(deprecated)] - let mut stream = sqlx::query("SELECT 1 UNION ALL SELECT 2") - .with_annotations(test_annotations()) - .fetch_many(&pool); - while stream.next().await.is_some() {} - drop(stream); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); -} + test_query_fetch_many_with_annotations_via_pool!(test_pool().await, common::MYSQL_DIALECT); +} #[tokio::test] #[serial] async fn query_fetch_all_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let rows = sqlx::query("SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3") - .with_annotations(test_annotations()) - .fetch_all(&pool) - .await - .unwrap(); - assert_eq!(rows.len(), 3); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(3)) - ); + test_query_fetch_all_with_annotations_via_pool!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn query_fetch_one_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let _row = sqlx::query("SELECT 1") - .with_annotations(test_annotations()) - .fetch_one(&pool) - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(1)) - ); + test_query_fetch_one_with_annotations_via_pool!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] @@ -2187,273 +855,19 @@ async fn query_bind_first_then_annotations_via_pool() { #[tokio::test] #[serial] -async fn query_annotations_first_then_bind_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let row = sqlx::query("SELECT CAST(? + ? AS SIGNED) AS sum") - .with_annotations(test_annotations()) - .bind(10_i32) - .bind(20_i32) - .fetch_one(&pool) - .await - .unwrap(); - let sum: i64 = row.try_get("sum").unwrap(); - assert_eq!(sum, 30); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); -} - -#[tokio::test] -#[serial] -async fn query_with_operation_shorthand_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - sqlx::query("CREATE TABLE IF NOT EXISTS qop_pool (id INT AUTO_INCREMENT PRIMARY KEY)") - .with_operation("SELECT", "users") - .execute(&pool) - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); -} - -#[tokio::test] -#[serial] -async fn query_execute_with_annotations_via_connection() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - sqlx::query("CREATE TABLE IF NOT EXISTS qe_conn (id INT AUTO_INCREMENT PRIMARY KEY)") - .with_annotations(test_annotations()) - .execute(&mut conn) - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); -} - -#[tokio::test] -#[serial] -async fn query_execute_with_annotations_via_transaction() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut tx: Transaction<'_, MySql> = pool.begin().await.unwrap(); - sqlx::query("CREATE TABLE IF NOT EXISTS qe_tx (id INT AUTO_INCREMENT PRIMARY KEY)") - .with_annotations(test_annotations()) - .execute(&mut tx) - .await - .unwrap(); - tx.commit().await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); -} - -#[tokio::test] -#[serial] -async fn query_execute_with_annotations_records_error() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let result = sqlx::query("INVALID SQL GIBBERISH") - .with_annotations(test_annotations()) - .execute(&pool) - .await; - assert!(result.is_err()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); - assert_error_span(&spans[0]); -} - -// --- query_as side --------------------------------------------------------- - -#[tokio::test] -#[serial] -async fn query_as_fetch_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut stream = sqlx::query_as::<_, (i32,)>("SELECT 1 UNION ALL SELECT 2") - .with_annotations(test_annotations()) - .fetch(&pool); - while stream.next().await.is_some() {} - drop(stream); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); -} - -#[tokio::test] -#[serial] -async fn query_as_fetch_many_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - #[allow(deprecated)] - let mut stream = sqlx::query_as::<_, (i32,)>("SELECT 1 UNION ALL SELECT 2") - .with_annotations(test_annotations()) - .fetch_many(&pool); - while stream.next().await.is_some() {} - drop(stream); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); -} - -#[tokio::test] -#[serial] -async fn query_as_fetch_all_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let rows: Vec<(i32,)> = sqlx::query_as("SELECT 1 UNION ALL SELECT 2") - .with_annotations(test_annotations()) - .fetch_all(&pool) - .await - .unwrap(); - assert_eq!(rows.len(), 2); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); -} - -#[tokio::test] -#[serial] -async fn query_as_fetch_one_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let row: (i32,) = sqlx::query_as("SELECT 7") - .with_annotations(test_annotations()) - .fetch_one(&pool) - .await - .unwrap(); - assert_eq!(row.0, 7); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); -} - -#[tokio::test] -#[serial] -async fn query_as_fetch_optional_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let row: Option<(i32,)> = sqlx::query_as("SELECT 1 FROM (SELECT 1) t WHERE 1 = 0") - .with_annotations(test_annotations()) - .fetch_optional(&pool) - .await - .unwrap(); - assert!(row.is_none()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); -} - -#[tokio::test] -#[serial] -async fn query_as_fetch_one_with_annotations_records_error() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let result: Result<(i32,), _> = sqlx::query_as("INVALID SQL") - .with_annotations(test_annotations()) - .fetch_one(&pool) - .await; - assert!(result.is_err()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); - assert_error_span(&spans[0]); -} - -// --- query_scalar side ----------------------------------------------------- - -#[tokio::test] -#[serial] -async fn query_scalar_fetch_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut stream = sqlx::query_scalar::<_, i32>("SELECT 1 UNION ALL SELECT 2") - .with_annotations(test_annotations()) - .fetch(&pool); - while stream.next().await.is_some() {} - drop(stream); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); -} - -#[tokio::test] -#[serial] -async fn query_scalar_fetch_many_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - #[allow(deprecated)] - let mut stream = sqlx::query_scalar::<_, i32>("SELECT 1 UNION ALL SELECT 2") - .with_annotations(test_annotations()) - .fetch_many(&pool); - while stream.next().await.is_some() {} - drop(stream); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); -} - -#[tokio::test] -#[serial] -async fn query_scalar_fetch_all_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let rows: Vec = sqlx::query_scalar("SELECT 1 UNION ALL SELECT 2") - .with_annotations(test_annotations()) - .fetch_all(&pool) - .await - .unwrap(); - assert_eq!(rows, vec![1, 2]); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); -} - -#[tokio::test] -#[serial] -async fn query_scalar_fetch_one_with_annotations_via_pool() { +async fn query_annotations_first_then_bind_via_pool() { let tel = common::TestTelemetry::install(); let pool = test_pool().await; - let value: i32 = sqlx::query_scalar("SELECT 42") + let row = sqlx::query("SELECT CAST(? + ? AS SIGNED) AS sum") .with_annotations(test_annotations()) + .bind(10_i32) + .bind(20_i32) .fetch_one(&pool) .await .unwrap(); - assert_eq!(value, 42); + let sum: i64 = row.try_get("sum").unwrap(); + assert_eq!(sum, 30); let spans = tel.spans(); assert_eq!(spans.len(), 1); @@ -2462,46 +876,33 @@ async fn query_scalar_fetch_one_with_annotations_via_pool() { #[tokio::test] #[serial] -async fn query_scalar_fetch_optional_with_annotations_via_pool() { +async fn query_with_operation_shorthand_via_pool() { let tel = common::TestTelemetry::install(); let pool = test_pool().await; - let value: Option = sqlx::query_scalar("SELECT 1 FROM (SELECT 1) t WHERE 1 = 0") - .with_annotations(test_annotations()) - .fetch_optional(&pool) + sqlx::query("CREATE TABLE IF NOT EXISTS qop_pool (id INT AUTO_INCREMENT PRIMARY KEY)") + .with_operation("SELECT", "users") + .execute(&pool) .await .unwrap(); - assert!(value.is_none()); let spans = tel.spans(); assert_eq!(spans.len(), 1); assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); } -// =========================================================================== -// query-side annotations: Map (Query::map / Query::try_map) -// =========================================================================== -// -// MySQL coerces arithmetic on bind parameters to DOUBLE, so any test that passes a value -// through `?` and decodes it as `i64` wraps the expression in `CAST(? AS SIGNED)`. Plain -// integer literals (`SELECT 19`) are returned as `BIGINT` and decode straight to `i64`. - -// --- Per-position end-to-end ---------------------------------------------- - #[tokio::test] #[serial] -async fn query_map_position_1_via_pool() { +async fn query_execute_with_annotations_via_connection() { let tel = common::TestTelemetry::install(); let pool = test_pool().await; - let value: i64 = sqlx::query("SELECT CAST(? AS SIGNED)") + let mut conn = pool.acquire().await.unwrap(); + sqlx::query("CREATE TABLE IF NOT EXISTS qe_conn (id INT AUTO_INCREMENT PRIMARY KEY)") .with_annotations(test_annotations()) - .bind(7_i64) - .map(|row: sqlx::mysql::MySqlRow| row.get::(0)) - .fetch_one(&pool) + .execute(&mut conn) .await .unwrap(); - assert_eq!(value, 7); let spans = tel.spans(); assert_eq!(spans.len(), 1); @@ -2510,18 +911,17 @@ async fn query_map_position_1_via_pool() { #[tokio::test] #[serial] -async fn query_map_position_2_via_pool() { +async fn query_execute_with_annotations_via_transaction() { let tel = common::TestTelemetry::install(); let pool = test_pool().await; - let value: i64 = sqlx::query("SELECT CAST(? AS SIGNED)") - .bind(11_i64) + let mut tx: Transaction<'_, MySql> = pool.begin().await.unwrap(); + sqlx::query("CREATE TABLE IF NOT EXISTS qe_tx (id INT AUTO_INCREMENT PRIMARY KEY)") .with_annotations(test_annotations()) - .map(|row: sqlx::mysql::MySqlRow| row.get::(0)) - .fetch_one(&pool) + .execute(&mut tx) .await .unwrap(); - assert_eq!(value, 11); + tx.commit().await.unwrap(); let spans = tel.spans(); assert_eq!(spans.len(), 1); @@ -2530,142 +930,162 @@ async fn query_map_position_2_via_pool() { #[tokio::test] #[serial] -async fn query_map_position_3_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; +async fn query_execute_with_annotations_records_error() { + test_query_execute_with_annotations_records_error!(test_pool().await, common::MYSQL_DIALECT); +} - let value: i64 = sqlx::query("SELECT CAST(? AS SIGNED)") - .bind(13_i64) - .map(|row: sqlx::mysql::MySqlRow| row.get::(0)) - .with_annotations(test_annotations()) - .fetch_one(&pool) - .await - .unwrap(); - assert_eq!(value, 13); +// --- query_as side --------------------------------------------------------- - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); +#[tokio::test] +#[serial] +async fn query_as_fetch_with_annotations_via_pool() { + test_query_as_fetch_with_annotations_via_pool!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] -async fn query_try_map_position_3_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; +async fn query_as_fetch_many_with_annotations_via_pool() { + test_query_as_fetch_many_with_annotations_via_pool!(test_pool().await, common::MYSQL_DIALECT); +} - let value: i64 = sqlx::query("SELECT CAST(? AS SIGNED)") - .bind(17_i64) - .try_map(|row: sqlx::mysql::MySqlRow| Ok(row.get::(0))) - .with_annotations(test_annotations()) - .fetch_one(&pool) - .await - .unwrap(); - assert_eq!(value, 17); +#[tokio::test] +#[serial] +async fn query_as_fetch_all_with_annotations_via_pool() { + test_query_as_fetch_all_with_annotations_via_pool!(test_pool().await, common::MYSQL_DIALECT); +} - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); +#[tokio::test] +#[serial] +async fn query_as_fetch_one_with_annotations_via_pool() { + test_query_as_fetch_one_with_annotations_via_pool!(test_pool().await, common::MYSQL_DIALECT); } -// --- Per-method on Map (so each forwarder body is hit) -------------------- +#[tokio::test] +#[serial] +async fn query_as_fetch_optional_with_annotations_via_pool() { + test_query_as_fetch_optional_with_annotations_via_pool!( + test_pool().await, + common::MYSQL_DIALECT + ); +} #[tokio::test] #[serial] -async fn map_fetch_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; +async fn query_as_fetch_one_with_annotations_records_error() { + test_query_as_fetch_one_with_annotations_records_error!( + test_pool().await, + common::MYSQL_DIALECT + ); +} - let mut stream = sqlx::query("SELECT 1 UNION ALL SELECT 2") - .map(|row: sqlx::mysql::MySqlRow| row.get::(0)) - .with_annotations(test_annotations()) - .fetch(&pool); - while stream.next().await.is_some() {} - drop(stream); +// --- query_scalar side ----------------------------------------------------- - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(2)) +#[tokio::test] +#[serial] +async fn query_scalar_fetch_with_annotations_via_pool() { + test_query_scalar_fetch_with_annotations_via_pool!(test_pool().await, common::MYSQL_DIALECT); +} + +#[tokio::test] +#[serial] +async fn query_scalar_fetch_many_with_annotations_via_pool() { + test_query_scalar_fetch_many_with_annotations_via_pool!( + test_pool().await, + common::MYSQL_DIALECT ); } #[tokio::test] #[serial] -async fn map_fetch_many_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; +async fn query_scalar_fetch_all_with_annotations_via_pool() { + test_query_scalar_fetch_all_with_annotations_via_pool!( + test_pool().await, + common::MYSQL_DIALECT + ); +} - #[allow(deprecated)] - let mut stream = sqlx::query("SELECT 1 UNION ALL SELECT 2") - .map(|row: sqlx::mysql::MySqlRow| row.get::(0)) - .with_annotations(test_annotations()) - .fetch_many(&pool); - while stream.next().await.is_some() {} - drop(stream); +#[tokio::test] +#[serial] +async fn query_scalar_fetch_one_with_annotations_via_pool() { + test_query_scalar_fetch_one_with_annotations_via_pool!( + test_pool().await, + common::MYSQL_DIALECT + ); +} - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); +#[tokio::test] +#[serial] +async fn query_scalar_fetch_optional_with_annotations_via_pool() { + test_query_scalar_fetch_optional_with_annotations_via_pool!( + test_pool().await, + common::MYSQL_DIALECT + ); } +// =========================================================================== +// query-side annotations: Map (Query::map / Query::try_map) +// =========================================================================== +// +// MySQL coerces arithmetic on bind parameters to DOUBLE, so any test that passes a value +// through `?` and decodes it as `i64` wraps the expression in `CAST(? AS SIGNED)`. Plain +// integer literals (`SELECT 19`) are returned as `BIGINT` and decode straight to `i64`. + +// --- Per-position end-to-end ---------------------------------------------- + #[tokio::test] #[serial] -async fn map_fetch_all_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; +async fn query_map_position_1_via_pool() { + test_query_map_position_1_via_pool!(test_pool().await, common::MYSQL_DIALECT); +} - let rows: Vec = sqlx::query("SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3") - .map(|row: sqlx::mysql::MySqlRow| row.get::(0)) - .with_annotations(test_annotations()) - .fetch_all(&pool) - .await - .unwrap(); - assert_eq!(rows, vec![1, 2, 3]); +#[tokio::test] +#[serial] +async fn query_map_position_2_via_pool() { + test_query_map_position_2_via_pool!(test_pool().await, common::MYSQL_DIALECT); +} - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); +#[tokio::test] +#[serial] +async fn query_map_position_3_via_pool() { + test_query_map_position_3_via_pool!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] -async fn map_fetch_one_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; +async fn query_try_map_position_3_via_pool() { + test_query_try_map_position_3_via_pool!(test_pool().await, common::MYSQL_DIALECT); +} - let value: i64 = sqlx::query("SELECT 19") - .map(|row: sqlx::mysql::MySqlRow| row.get::(0)) - .with_annotations(test_annotations()) - .fetch_one(&pool) - .await - .unwrap(); - assert_eq!(value, 19); +// --- Per-method on Map (so each forwarder body is hit) -------------------- - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); +#[tokio::test] +#[serial] +async fn map_fetch_with_annotations_via_pool() { + test_map_fetch_with_annotations_via_pool!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] -async fn map_fetch_optional_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; +async fn map_fetch_many_with_annotations_via_pool() { + test_map_fetch_many_with_annotations_via_pool!(test_pool().await, common::MYSQL_DIALECT); +} - let value: Option = sqlx::query("SELECT 1 FROM (SELECT 1) t WHERE 1 = 0") - .map(|row: sqlx::mysql::MySqlRow| row.get::(0)) - .with_annotations(test_annotations()) - .fetch_optional(&pool) - .await - .unwrap(); - assert!(value.is_none()); +#[tokio::test] +#[serial] +async fn map_fetch_all_with_annotations_via_pool() { + test_map_fetch_all_with_annotations_via_pool!(test_pool().await, common::MYSQL_DIALECT); +} - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); +#[tokio::test] +#[serial] +async fn map_fetch_one_with_annotations_via_pool() { + test_map_fetch_one_with_annotations_via_pool!(test_pool().await, common::MYSQL_DIALECT); +} + +#[tokio::test] +#[serial] +async fn map_fetch_optional_with_annotations_via_pool() { + test_map_fetch_optional_with_annotations_via_pool!(test_pool().await, common::MYSQL_DIALECT); } // --- Composition (multi-map; both branches of step 4) -------------------- @@ -2673,41 +1093,13 @@ async fn map_fetch_optional_with_annotations_via_pool() { #[tokio::test] #[serial] async fn map_compose_after_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let value: i64 = sqlx::query("SELECT 5") - .map(|row: sqlx::mysql::MySqlRow| row.get::(0)) - .with_annotations(test_annotations()) - .map(|n| n * 2) - .fetch_one(&pool) - .await - .unwrap(); - assert_eq!(value, 10); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); + test_map_compose_after_annotations_via_pool!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn map_try_map_compose_after_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let value: i64 = sqlx::query("SELECT 6") - .map(|row: sqlx::mysql::MySqlRow| row.get::(0)) - .with_annotations(test_annotations()) - .try_map(|n: i64| Ok::<_, sqlx::Error>(n + 100)) - .fetch_one(&pool) - .await - .unwrap(); - assert_eq!(value, 106); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); + test_map_try_map_compose_after_annotations_via_pool!(test_pool().await, common::MYSQL_DIALECT); } // --- Other executor receivers (smoke) ------------------------------------- @@ -2715,42 +1107,13 @@ async fn map_try_map_compose_after_annotations_via_pool() { #[tokio::test] #[serial] async fn query_map_with_annotations_via_connection() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - let value: i64 = sqlx::query("SELECT 23") - .map(|row: sqlx::mysql::MySqlRow| row.get::(0)) - .with_annotations(test_annotations()) - .fetch_one(&mut conn) - .await - .unwrap(); - assert_eq!(value, 23); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); + test_query_map_with_annotations_via_connection!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn query_map_with_annotations_via_transaction() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut tx: Transaction<'_, MySql> = pool.begin().await.unwrap(); - let value: i64 = sqlx::query("SELECT 29") - .map(|row: sqlx::mysql::MySqlRow| row.get::(0)) - .with_annotations(test_annotations()) - .fetch_one(&mut tx) - .await - .unwrap(); - assert_eq!(value, 29); - tx.commit().await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); + test_query_map_with_annotations_via_transaction!(test_pool().await, common::MYSQL_DIALECT); } // --- Error paths ---------------------------------------------------------- @@ -2758,47 +1121,16 @@ async fn query_map_with_annotations_via_transaction() { #[tokio::test] #[serial] async fn query_map_with_annotations_records_error() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let result: Result = sqlx::query("INVALID SQL") - .map(|row: sqlx::mysql::MySqlRow| row.get::(0)) - .with_annotations(test_annotations()) - .fetch_one(&pool) - .await; - assert!(result.is_err()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); - assert_error_span(&spans[0]); + test_query_map_with_annotations_records_error!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn query_try_map_with_annotations_propagates_mapper_error() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - // The mapper error fires *after* the database round-trip succeeds – the executor - // sees the row arrive and completes the fetch successfully, then the mapper surfaces - // the error to the caller. The span therefore stays at success at the database - // layer; the contract verified here is that the user-visible Err propagates through - // the wrapper and that the annotations were attached to the (successful) span. - let result: Result = sqlx::query("SELECT 1") - .try_map(|_row: sqlx::mysql::MySqlRow| { - Err::(sqlx::Error::Decode( - "intentional decode failure".to_string().into(), - )) - }) - .with_annotations(test_annotations()) - .fetch_one(&pool) - .await; - assert!(result.is_err()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); + test_query_try_map_with_annotations_propagates_mapper_error!( + test_pool().await, + common::MYSQL_DIALECT + ); } // =========================================================================== diff --git a/tests/postgres.rs b/tests/postgres.rs index afad9c2..437ba21 100644 --- a/tests/postgres.rs +++ b/tests/postgres.rs @@ -8,13 +8,12 @@ use std::time::Duration; use common::{ assert_annotated_span, assert_common_span_attributes, assert_error_span, attr, test_annotations, }; -use futures::StreamExt; use opentelemetry::trace::SpanKind; use serial_test::serial; use sqlx::Executor as _; use sqlx::Postgres; use sqlx::Row as _; -use sqlx_otel::{Pool, PoolBuilder, QueryAnnotateExt, QueryAnnotations, Transaction}; +use sqlx_otel::{Pool, PoolBuilder, QueryAnnotateExt, Transaction}; use testcontainers::core::IntoContainerPort; use testcontainers::runners::AsyncRunner; use testcontainers::{ContainerAsync, GenericImage, ImageExt}; @@ -22,6 +21,9 @@ use tokio::sync::OnceCell; const SYSTEM: &str = "postgresql"; +/// Backend row type used by parameterised map test macros (see `tests/sqlite.rs`). +type Row = sqlx::postgres::PgRow; + /// Shared container and connection URL, initialised once across all tests. struct SharedContainer { _container: ContainerAsync, @@ -59,9 +61,14 @@ async fn shared_container() -> &'static SharedContainer { /// Return an instrumented pool connected to the shared container. async fn test_pool() -> Pool { + PoolBuilder::from(raw_pool().await).build() +} + +/// Raw (un-instrumented) sqlx pool, used by parameterised builder / query-text-mode +/// tests that need to apply specific `PoolBuilder` configurations themselves. +async fn raw_pool() -> sqlx::PgPool { let shared = shared_container().await; - let raw = sqlx::PgPool::connect(&shared.url).await.unwrap(); - PoolBuilder::from(raw).build() + sqlx::PgPool::connect(&shared.url).await.unwrap() } // =========================================================================== @@ -77,104 +84,19 @@ async fn execute_creates_span_via_pool() { #[tokio::test] #[serial] async fn execute_creates_span_via_connection() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - sqlx::query("CREATE TABLE IF NOT EXISTS exec_conn (id SERIAL PRIMARY KEY)") - .execute(&mut conn) - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - assert!(attr(&spans[0], "db.response.affected_rows").is_some()); - - // With annotations - conn.with_annotations(test_annotations()) - .execute("CREATE TABLE IF NOT EXISTS exec_conn (id SERIAL PRIMARY KEY)") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); - - // With shorthand - conn.with_operation("SELECT", "users") - .execute("CREATE TABLE IF NOT EXISTS exec_conn3 (id SERIAL PRIMARY KEY)") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); + test_execute_creates_span_via_connection!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] async fn execute_creates_span_via_transaction() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut tx: Transaction<'_, Postgres> = pool.begin().await.unwrap(); - sqlx::query("CREATE TABLE IF NOT EXISTS exec_tx (id SERIAL PRIMARY KEY)") - .execute(&mut tx) - .await - .unwrap(); - - // With annotations - tx.with_annotations(test_annotations()) - .execute("CREATE TABLE IF NOT EXISTS exec_tx (id SERIAL PRIMARY KEY)") - .await - .unwrap(); - - // With shorthand - tx.with_operation("SELECT", "users") - .execute("CREATE TABLE IF NOT EXISTS exec_tx3 (id SERIAL PRIMARY KEY)") - .await - .unwrap(); - tx.commit().await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 3); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - assert!(attr(&spans[0], "db.response.affected_rows").is_some()); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); + test_execute_creates_span_via_transaction!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] async fn execute_records_error() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let result = sqlx::query("INVALID SQL GIBBERISH").execute(&pool).await; - assert!(result.is_err()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_error_span(&spans[0]); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - - // With annotations (error path) - let result = pool - .with_annotations(test_annotations()) - .execute("INVALID SQL GIBBERISH") - .await; - assert!(result.is_err()); - let spans = tel.spans(); - let last = spans.last().unwrap(); - assert_annotated_span(last, &common::POSTGRES_DIALECT); - assert_error_span(last); - - // With shorthand (error path) - let result = pool - .with_operation("SELECT", "users") - .execute("INVALID SQL GIBBERISH") - .await; - assert!(result.is_err()); - let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last, &common::POSTGRES_DIALECT); - assert_error_span(&last); + test_execute_records_error!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] @@ -283,153 +205,25 @@ async fn execute_records_affected_rows() { #[tokio::test] #[serial] async fn execute_many_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut stream = (&pool).execute_many("SELECT 1; SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(0)) - ); - - // With annotations - let mut stream = pool - .with_annotations(test_annotations()) - .execute_many("SELECT 1; SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); - - // With shorthand - let mut stream = pool - .with_operation("SELECT", "users") - .execute_many("SELECT 1; SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); + test_execute_many_via_pool!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] async fn execute_many_via_connection() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - let mut stream = (&mut conn).execute_many("SELECT 1; SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(0)) - ); - - // With annotations - let mut stream = conn - .with_annotations(test_annotations()) - .execute_many("SELECT 1; SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); - - // With shorthand - let mut stream = conn - .with_operation("SELECT", "users") - .execute_many("SELECT 1; SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); + test_execute_many_via_connection!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] async fn execute_many_via_transaction() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut tx: Transaction<'_, Postgres> = pool.begin().await.unwrap(); - - let mut stream = (&mut tx).execute_many("SELECT 1; SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - - // With annotations - let mut stream = tx - .with_annotations(test_annotations()) - .execute_many("SELECT 1; SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - - // With shorthand - let mut stream = tx - .with_operation("SELECT", "users") - .execute_many("SELECT 1; SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - tx.commit().await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 3); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(0)) - ); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); + test_execute_many_via_transaction!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] async fn execute_many_records_error() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut stream = (&pool).execute_many("INVALID SQL GIBBERISH"); - let result = stream.next().await; - assert!(result.is_some_and(|r| r.is_err())); - drop(stream); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_error_span(&spans[0]); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(0)) - ); - - // With annotations (error path) - let mut stream = pool - .with_annotations(test_annotations()) - .execute_many("INVALID SQL GIBBERISH"); - let result = stream.next().await; - assert!(result.is_some_and(|r| r.is_err())); - drop(stream); - let spans = tel.spans(); - let last = spans.last().unwrap(); - assert_annotated_span(last, &common::POSTGRES_DIALECT); - assert_error_span(last); - - // With shorthand (error path) - let mut stream = pool - .with_operation("SELECT", "users") - .execute_many("INVALID SQL GIBBERISH"); - let result = stream.next().await; - assert!(result.is_some_and(|r| r.is_err())); - drop(stream); - let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last, &common::POSTGRES_DIALECT); - assert_error_span(&last); + test_execute_many_records_error!(test_pool().await, common::POSTGRES_DIALECT); } // =========================================================================== @@ -439,175 +233,34 @@ async fn execute_many_records_error() { #[tokio::test] #[serial] async fn fetch_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut stream = (&pool).fetch("SELECT 1 UNION ALL SELECT 2"); - let mut count = 0u64; - while stream.next().await.is_some() { - count += 1; - } - assert_eq!(count, 2); - drop(stream); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(2)) - ); - - // With annotations - let mut stream = pool - .with_annotations(test_annotations()) - .fetch("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); - - // With shorthand - let mut stream = pool - .with_operation("SELECT", "users") - .fetch("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); + test_fetch_via_pool!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] async fn fetch_via_connection() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - let mut stream = (&mut conn).fetch("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(2)) - ); - - // With annotations - let mut stream = conn - .with_annotations(test_annotations()) - .fetch("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); - - // With shorthand - let mut stream = conn - .with_operation("SELECT", "users") - .fetch("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); + test_fetch_via_connection!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] async fn fetch_via_transaction() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut tx: Transaction<'_, Postgres> = pool.begin().await.unwrap(); - - let mut stream = (&mut tx).fetch("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - - // With annotations - let mut stream = tx - .with_annotations(test_annotations()) - .fetch("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - - // With shorthand - let mut stream = tx - .with_operation("SELECT", "users") - .fetch("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - tx.commit().await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 3); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(2)) - ); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); + test_fetch_via_transaction!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] async fn fetch_stream_dropped_early_still_records_span() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - { - let mut stream = (&pool).fetch("SELECT 1 UNION ALL SELECT 2"); - let _ = stream.next().await; - } - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(1)) + test_fetch_stream_dropped_early_still_records_span!( + test_pool().await, + common::POSTGRES_DIALECT ); } #[tokio::test] #[serial] async fn fetch_stream_records_error() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut stream = (&pool).fetch("INVALID SQL"); - let result = stream.next().await; - assert!(result.is_some_and(|r| r.is_err())); - drop(stream); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(0)) - ); - assert_error_span(&spans[0]); - - // With annotations (error path) - let mut stream = pool - .with_annotations(test_annotations()) - .fetch("INVALID SQL"); - let result = stream.next().await; - assert!(result.is_some_and(|r| r.is_err())); - drop(stream); - let spans = tel.spans(); - let last = spans.last().unwrap(); - assert_annotated_span(last, &common::POSTGRES_DIALECT); - assert_error_span(last); - - // With shorthand (error path) - let mut stream = pool.with_operation("SELECT", "users").fetch("INVALID SQL"); - let result = stream.next().await; - assert!(result.is_some_and(|r| r.is_err())); - drop(stream); - let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last, &common::POSTGRES_DIALECT); - assert_error_span(&last); + test_fetch_stream_records_error!(test_pool().await, common::POSTGRES_DIALECT); } // =========================================================================== @@ -617,180 +270,32 @@ async fn fetch_stream_records_error() { #[tokio::test] #[serial] async fn fetch_many_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut stream = (&pool).fetch_many("SELECT 1 UNION ALL SELECT 2"); - let mut rows = 0u64; - while let Some(item) = stream.next().await { - if let Ok(sqlx::Either::Right(_)) = item { - rows += 1; - } - } - drop(stream); - assert_eq!(rows, 2); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(2)) - ); - - // With annotations - let mut stream = pool - .with_annotations(test_annotations()) - .fetch_many("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); - - // With shorthand - let mut stream = pool - .with_operation("SELECT", "users") - .fetch_many("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); + test_fetch_many_via_pool!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] async fn fetch_many_via_connection() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - let mut stream = (&mut conn).fetch_many("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(2)) - ); - - // With annotations - let mut stream = conn - .with_annotations(test_annotations()) - .fetch_many("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); - - // With shorthand - let mut stream = conn - .with_operation("SELECT", "users") - .fetch_many("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); + test_fetch_many_via_connection!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] async fn fetch_many_via_transaction() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut tx: Transaction<'_, Postgres> = pool.begin().await.unwrap(); - - let mut stream = (&mut tx).fetch_many("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - - // With annotations - let mut stream = tx - .with_annotations(test_annotations()) - .fetch_many("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - - // With shorthand - let mut stream = tx - .with_operation("SELECT", "users") - .fetch_many("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - tx.commit().await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 3); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(2)) - ); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); + test_fetch_many_via_transaction!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] async fn fetch_many_dropped_early_still_records_span() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - { - let mut stream = (&pool).fetch_many("SELECT 1 UNION ALL SELECT 2"); - let _ = stream.next().await; - } - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(1)) - ); + test_fetch_many_dropped_early_still_records_span!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] async fn fetch_many_records_error() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut stream = (&pool).fetch_many("INVALID SQL GIBBERISH"); - let result = stream.next().await; - assert!(result.is_some_and(|r| r.is_err())); - drop(stream); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(0)) - ); - assert_error_span(&spans[0]); - - // With annotations (error path) - let mut stream = pool - .with_annotations(test_annotations()) - .fetch_many("INVALID SQL GIBBERISH"); - let result = stream.next().await; - assert!(result.is_some_and(|r| r.is_err())); - drop(stream); - let spans = tel.spans(); - let last = spans.last().unwrap(); - assert_annotated_span(last, &common::POSTGRES_DIALECT); - assert_error_span(last); - - // With shorthand (error path) - let mut stream = pool - .with_operation("SELECT", "users") - .fetch_many("INVALID SQL GIBBERISH"); - let result = stream.next().await; - assert!(result.is_some_and(|r| r.is_err())); - drop(stream); - let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last, &common::POSTGRES_DIALECT); - assert_error_span(&last); -} + test_fetch_many_records_error!(test_pool().await, common::POSTGRES_DIALECT); +} // =========================================================================== // fetch_all @@ -799,145 +304,25 @@ async fn fetch_many_records_error() { #[tokio::test] #[serial] async fn fetch_all_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let rows = (&pool) - .fetch_all("SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3") - .await - .unwrap(); - assert_eq!(rows.len(), 3); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(3)) - ); - - // With annotations - pool.with_annotations(test_annotations()) - .fetch_all("SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); - - // With shorthand - pool.with_operation("SELECT", "users") - .fetch_all("SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); + test_fetch_all_via_pool!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] async fn fetch_all_via_connection() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - let rows = (&mut conn) - .fetch_all("SELECT 1 UNION ALL SELECT 2") - .await - .unwrap(); - assert_eq!(rows.len(), 2); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(2)) - ); - - // With annotations - conn.with_annotations(test_annotations()) - .fetch_all("SELECT 1 UNION ALL SELECT 2") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); - - // With shorthand - conn.with_operation("SELECT", "users") - .fetch_all("SELECT 1 UNION ALL SELECT 2") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); + test_fetch_all_via_connection!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] async fn fetch_all_via_transaction() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut tx: Transaction<'_, Postgres> = pool.begin().await.unwrap(); - let rows = (&mut tx) - .fetch_all("SELECT 1 UNION ALL SELECT 2") - .await - .unwrap(); - assert_eq!(rows.len(), 2); - - // With annotations - tx.with_annotations(test_annotations()) - .fetch_all("SELECT 1 UNION ALL SELECT 2") - .await - .unwrap(); - - // With shorthand - tx.with_operation("SELECT", "users") - .fetch_all("SELECT 1 UNION ALL SELECT 2") - .await - .unwrap(); - tx.commit().await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 3); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(2)) - ); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); + test_fetch_all_via_transaction!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] async fn fetch_all_records_error() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let result = (&pool).fetch_all("INVALID SQL GIBBERISH").await; - assert!(result.is_err()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - assert_error_span(&spans[0]); - - // With annotations (error path) - let result = pool - .with_annotations(test_annotations()) - .fetch_all("INVALID SQL GIBBERISH") - .await; - assert!(result.is_err()); - let spans = tel.spans(); - let last = spans.last().unwrap(); - assert_annotated_span(last, &common::POSTGRES_DIALECT); - assert_error_span(last); - - // With shorthand (error path) - let result = pool - .with_operation("SELECT", "users") - .fetch_all("INVALID SQL GIBBERISH") - .await; - assert!(result.is_err()); - let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last, &common::POSTGRES_DIALECT); - assert_error_span(&last); + test_fetch_all_records_error!(test_pool().await, common::POSTGRES_DIALECT); } // =========================================================================== @@ -947,133 +332,25 @@ async fn fetch_all_records_error() { #[tokio::test] #[serial] async fn fetch_one_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let _row = (&pool).fetch_one("SELECT 1").await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(1)) - ); - - // With annotations - pool.with_annotations(test_annotations()) - .fetch_one("SELECT 1") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); - - // With shorthand - pool.with_operation("SELECT", "users") - .fetch_one("SELECT 1") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); + test_fetch_one_via_pool!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] async fn fetch_one_via_connection() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - let _row = (&mut conn).fetch_one("SELECT 1").await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(1)) - ); - - // With annotations - conn.with_annotations(test_annotations()) - .fetch_one("SELECT 1") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); - - // With shorthand - conn.with_operation("SELECT", "users") - .fetch_one("SELECT 1") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); + test_fetch_one_via_connection!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] async fn fetch_one_via_transaction() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut tx: Transaction<'_, Postgres> = pool.begin().await.unwrap(); - let _row = (&mut tx).fetch_one("SELECT 1").await.unwrap(); - - // With annotations - tx.with_annotations(test_annotations()) - .fetch_one("SELECT 1") - .await - .unwrap(); - - // With shorthand - tx.with_operation("SELECT", "users") - .fetch_one("SELECT 1") - .await - .unwrap(); - tx.commit().await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 3); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(1)) - ); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); + test_fetch_one_via_transaction!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] async fn fetch_one_records_error() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let result = (&pool).fetch_one("INVALID SQL GIBBERISH").await; - assert!(result.is_err()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - assert_error_span(&spans[0]); - - // With annotations (error path) - let result = pool - .with_annotations(test_annotations()) - .fetch_one("INVALID SQL GIBBERISH") - .await; - assert!(result.is_err()); - let spans = tel.spans(); - let last = spans.last().unwrap(); - assert_annotated_span(last, &common::POSTGRES_DIALECT); - assert_error_span(last); - - // With shorthand (error path) - let result = pool - .with_operation("SELECT", "users") - .fetch_one("INVALID SQL GIBBERISH") - .await; - assert!(result.is_err()); - let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last, &common::POSTGRES_DIALECT); - assert_error_span(&last); + test_fetch_one_records_error!(test_pool().await, common::POSTGRES_DIALECT); } // =========================================================================== @@ -1083,33 +360,7 @@ async fn fetch_one_records_error() { #[tokio::test] #[serial] async fn fetch_optional_records_one_row() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let result = (&pool).fetch_optional("SELECT 1").await.unwrap(); - assert!(result.is_some()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(1)) - ); - - // With annotations - pool.with_annotations(test_annotations()) - .fetch_optional("SELECT 1") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); - - // With shorthand - pool.with_operation("SELECT", "users") - .fetch_optional("SELECT 1") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); + test_fetch_optional_records_one_row!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] @@ -1146,104 +397,19 @@ async fn fetch_optional_records_zero_rows() { #[tokio::test] #[serial] async fn fetch_optional_via_connection() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - let result = (&mut conn).fetch_optional("SELECT 42").await.unwrap(); - assert!(result.is_some()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(1)) - ); - - // With annotations - conn.with_annotations(test_annotations()) - .fetch_optional("SELECT 42") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); - - // With shorthand - conn.with_operation("SELECT", "users") - .fetch_optional("SELECT 42") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); + test_fetch_optional_via_connection!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] async fn fetch_optional_via_transaction() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut tx: Transaction<'_, Postgres> = pool.begin().await.unwrap(); - let result = (&mut tx).fetch_optional("SELECT 99").await.unwrap(); - assert!(result.is_some()); - - // With annotations - tx.with_annotations(test_annotations()) - .fetch_optional("SELECT 99") - .await - .unwrap(); - - // With shorthand - tx.with_operation("SELECT", "users") - .fetch_optional("SELECT 99") - .await - .unwrap(); - tx.commit().await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 3); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(1)) - ); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); + test_fetch_optional_via_transaction!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] async fn fetch_optional_records_error() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let result = (&pool).fetch_optional("INVALID SQL GIBBERISH").await; - assert!(result.is_err()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - assert_error_span(&spans[0]); - - // With annotations (error path) - let result = pool - .with_annotations(test_annotations()) - .fetch_optional("INVALID SQL GIBBERISH") - .await; - assert!(result.is_err()); - let spans = tel.spans(); - let last = spans.last().unwrap(); - assert_annotated_span(last, &common::POSTGRES_DIALECT); - assert_error_span(last); - - // With shorthand (error path) - let result = pool - .with_operation("SELECT", "users") - .fetch_optional("INVALID SQL GIBBERISH") - .await; - assert!(result.is_err()); - let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last, &common::POSTGRES_DIALECT); - assert_error_span(&last); + test_fetch_optional_records_error!(test_pool().await, common::POSTGRES_DIALECT); } // =========================================================================== @@ -1253,125 +419,25 @@ async fn fetch_optional_records_error() { #[tokio::test] #[serial] async fn prepare_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let _stmt = (&pool).prepare("SELECT 1").await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - - // With annotations - pool.with_annotations(test_annotations()) - .prepare("SELECT 1") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); - - // With shorthand - pool.with_operation("SELECT", "users") - .prepare("SELECT 1") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); + test_prepare_via_pool!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] async fn prepare_via_connection() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - let _stmt = (&mut conn).prepare("SELECT 1").await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - - // With annotations - conn.with_annotations(test_annotations()) - .prepare("SELECT 1") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); - - // With shorthand - conn.with_operation("SELECT", "users") - .prepare("SELECT 1") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); + test_prepare_via_connection!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] async fn prepare_via_transaction() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut tx: Transaction<'_, Postgres> = pool.begin().await.unwrap(); - let _stmt = (&mut tx).prepare("SELECT 1").await.unwrap(); - - // With annotations - tx.with_annotations(test_annotations()) - .prepare("SELECT 1") - .await - .unwrap(); - - // With shorthand - tx.with_operation("SELECT", "users") - .prepare("SELECT 1") - .await - .unwrap(); - tx.commit().await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 3); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); + test_prepare_via_transaction!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] async fn prepare_records_error() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - let result = (&mut conn).prepare("INVALID SQL GIBBERISH").await; - assert!(result.is_err()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - assert_error_span(&spans[0]); - - // With annotations (error path) - let result = conn - .with_annotations(test_annotations()) - .prepare("INVALID SQL GIBBERISH") - .await; - assert!(result.is_err()); - let spans = tel.spans(); - let last = spans.last().unwrap(); - assert_annotated_span(last, &common::POSTGRES_DIALECT); - assert_error_span(last); - - // With shorthand (error path) - let result = conn - .with_operation("SELECT", "users") - .prepare("INVALID SQL GIBBERISH") - .await; - assert!(result.is_err()); - let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last, &common::POSTGRES_DIALECT); - assert_error_span(&last); + test_prepare_records_error!(test_pool().await, common::POSTGRES_DIALECT); } // =========================================================================== @@ -1381,125 +447,25 @@ async fn prepare_records_error() { #[tokio::test] #[serial] async fn prepare_with_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let _stmt = (&pool).prepare_with("SELECT $1", &[]).await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - - // With annotations - pool.with_annotations(test_annotations()) - .prepare_with("SELECT $1", &[]) - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); - - // With shorthand - pool.with_operation("SELECT", "users") - .prepare_with("SELECT $1", &[]) - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); + test_prepare_with_via_pool!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] async fn prepare_with_via_connection() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - let _stmt = (&mut conn).prepare_with("SELECT $1", &[]).await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - - // With annotations - conn.with_annotations(test_annotations()) - .prepare_with("SELECT $1", &[]) - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); - - // With shorthand - conn.with_operation("SELECT", "users") - .prepare_with("SELECT $1", &[]) - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); + test_prepare_with_via_connection!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] async fn prepare_with_via_transaction() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut tx: Transaction<'_, Postgres> = pool.begin().await.unwrap(); - let _stmt = (&mut tx).prepare_with("SELECT $1", &[]).await.unwrap(); - - // With annotations - tx.with_annotations(test_annotations()) - .prepare_with("SELECT $1", &[]) - .await - .unwrap(); - - // With shorthand - tx.with_operation("SELECT", "users") - .prepare_with("SELECT $1", &[]) - .await - .unwrap(); - tx.commit().await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 3); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); + test_prepare_with_via_transaction!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] async fn prepare_with_records_error() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - let result = (&mut conn).prepare_with("INVALID SQL GIBBERISH", &[]).await; - assert!(result.is_err()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - assert_error_span(&spans[0]); - - // With annotations (error path) - let result = conn - .with_annotations(test_annotations()) - .prepare_with("INVALID SQL GIBBERISH", &[]) - .await; - assert!(result.is_err()); - let spans = tel.spans(); - let last = spans.last().unwrap(); - assert_annotated_span(last, &common::POSTGRES_DIALECT); - assert_error_span(last); - - // With shorthand (error path) - let result = conn - .with_operation("SELECT", "users") - .prepare_with("INVALID SQL GIBBERISH", &[]) - .await; - assert!(result.is_err()); - let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last, &common::POSTGRES_DIALECT); - assert_error_span(&last); + test_prepare_with_records_error!(test_pool().await, common::POSTGRES_DIALECT); } // =========================================================================== @@ -1509,125 +475,25 @@ async fn prepare_with_records_error() { #[tokio::test] #[serial] async fn describe_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let _desc = (&pool).describe("SELECT 1").await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - - // With annotations - pool.with_annotations(test_annotations()) - .describe("SELECT 1") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); - - // With shorthand - pool.with_operation("SELECT", "users") - .describe("SELECT 1") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); + test_describe_via_pool!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] async fn describe_via_connection() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - let _desc = (&mut conn).describe("SELECT 1").await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - - // With annotations - conn.with_annotations(test_annotations()) - .describe("SELECT 1") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); - - // With shorthand - conn.with_operation("SELECT", "users") - .describe("SELECT 1") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); + test_describe_via_connection!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] async fn describe_via_transaction() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut tx: Transaction<'_, Postgres> = pool.begin().await.unwrap(); - let _desc = (&mut tx).describe("SELECT 1").await.unwrap(); - - // With annotations - tx.with_annotations(test_annotations()) - .describe("SELECT 1") - .await - .unwrap(); - - // With shorthand - tx.with_operation("SELECT", "users") - .describe("SELECT 1") - .await - .unwrap(); - tx.commit().await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 3); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - assert_annotated_span(tel.spans().last().unwrap(), &common::POSTGRES_DIALECT); + test_describe_via_transaction!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] async fn describe_records_error() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - let result = (&mut conn).describe("INVALID SQL GIBBERISH").await; - assert!(result.is_err()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - assert_error_span(&spans[0]); - - // With annotations (error path) - let result = conn - .with_annotations(test_annotations()) - .describe("INVALID SQL GIBBERISH") - .await; - assert!(result.is_err()); - let spans = tel.spans(); - let last = spans.last().unwrap(); - assert_annotated_span(last, &common::POSTGRES_DIALECT); - assert_error_span(last); - - // With shorthand (error path) - let result = conn - .with_operation("SELECT", "users") - .describe("INVALID SQL GIBBERISH") - .await; - assert!(result.is_err()); - let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last, &common::POSTGRES_DIALECT); - assert_error_span(&last); + test_describe_records_error!(test_pool().await, common::POSTGRES_DIALECT); } // =========================================================================== @@ -1775,93 +641,31 @@ async fn transaction_rollback() { #[tokio::test] #[serial] async fn builder_with_database_overrides_namespace() { - let tel = common::TestTelemetry::install(); - let shared = shared_container().await; - let raw = sqlx::PgPool::connect(&shared.url).await.unwrap(); - let pool = PoolBuilder::from(raw).with_database("custom_db").build(); - - let _ = (&pool).fetch_optional("SELECT 1").await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_eq!( - attr(&spans[0], "db.namespace"), - Some(opentelemetry::Value::String("custom_db".into())) - ); + test_builder_with_database_overrides_namespace!(raw_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] async fn builder_with_host_overrides_server_address() { - let tel = common::TestTelemetry::install(); - let shared = shared_container().await; - let raw = sqlx::PgPool::connect(&shared.url).await.unwrap(); - let pool = PoolBuilder::from(raw).with_host("custom-host").build(); - - let _ = (&pool).fetch_optional("SELECT 1").await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_eq!( - attr(&spans[0], "server.address"), - Some(opentelemetry::Value::String("custom-host".into())) - ); + test_builder_with_host_overrides_server_address!(raw_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] async fn builder_with_port_overrides_server_port() { - let tel = common::TestTelemetry::install(); - let shared = shared_container().await; - let raw = sqlx::PgPool::connect(&shared.url).await.unwrap(); - let pool = PoolBuilder::from(raw).with_port(9999).build(); - - let _ = (&pool).fetch_optional("SELECT 1").await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_eq!( - attr(&spans[0], "server.port"), - Some(opentelemetry::Value::I64(9999)) - ); + test_builder_with_port_overrides_server_port!(raw_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] async fn builder_with_network_peer_address() { - let tel = common::TestTelemetry::install(); - let shared = shared_container().await; - let raw = sqlx::PgPool::connect(&shared.url).await.unwrap(); - let pool = PoolBuilder::from(raw) - .with_network_peer_address("10.0.0.5") - .build(); - - let _ = (&pool).fetch_optional("SELECT 1").await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_eq!( - attr(&spans[0], "network.peer.address"), - Some(opentelemetry::Value::String("10.0.0.5".into())) - ); + test_builder_with_network_peer_address!(raw_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] async fn builder_with_network_peer_port() { - let tel = common::TestTelemetry::install(); - let shared = shared_container().await; - let raw = sqlx::PgPool::connect(&shared.url).await.unwrap(); - let pool = PoolBuilder::from(raw).with_network_peer_port(5433).build(); - - let _ = (&pool).fetch_optional("SELECT 1").await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_eq!( - attr(&spans[0], "network.peer.port"), - Some(opentelemetry::Value::I64(5433)) - ); + test_builder_with_network_peer_port!(raw_pool().await, common::POSTGRES_DIALECT); } // =========================================================================== @@ -1939,75 +743,13 @@ async fn query_text_mode_obfuscated_replaces_literals() { #[tokio::test] #[serial] async fn annotation_all_four_fields() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - pool.with_annotations( - QueryAnnotations::new() - .operation("SELECT") - .collection("users") - .query_summary("users by id") - .stored_procedure("sp_get_users"), - ) - .fetch_all("SELECT 1") - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - // Summary drives the span name (semconv level 1), distinct from "SELECT users" so - // the assertion proves the summary path won rather than coinciding with level 2. - assert_eq!(spans[0].name, "users by id"); - assert_eq!( - attr(&spans[0], "db.operation.name"), - Some(opentelemetry::Value::String("SELECT".into())), - ); - assert_eq!( - attr(&spans[0], "db.collection.name"), - Some(opentelemetry::Value::String("users".into())), - ); - assert_eq!( - attr(&spans[0], "db.query.summary"), - Some(opentelemetry::Value::String("users by id".into())), - ); - assert_eq!( - attr(&spans[0], "db.stored_procedure.name"), - Some(opentelemetry::Value::String("sp_get_users".into())), - ); + test_annotation_all_four_fields!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] async fn query_summary_drives_span_name() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - pool.with_annotations( - QueryAnnotations::new() - .operation("SELECT") - .collection("users") - .query_summary("users by tenant"), - ) - .fetch_all("SELECT 1") - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_eq!(spans[0].name, "users by tenant"); - // Summary drives the *name*, but does not suppress the other attributes. - assert_eq!( - attr(&spans[0], "db.query.summary"), - Some(opentelemetry::Value::String("users by tenant".into())), - ); - assert_eq!( - attr(&spans[0], "db.operation.name"), - Some(opentelemetry::Value::String("SELECT".into())), - ); - assert_eq!( - attr(&spans[0], "db.collection.name"), - Some(opentelemetry::Value::String("users".into())), - ); + test_query_summary_drives_span_name!(test_pool().await, common::POSTGRES_DIALECT); } // =========================================================================== @@ -2035,102 +777,31 @@ async fn query_execute_with_annotations_via_pool() { #[tokio::test] #[serial] async fn query_execute_many_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - #[allow(deprecated)] - let mut stream = sqlx::query("SELECT 1; SELECT 2") - .with_annotations(test_annotations()) - .execute_many(&pool) - .await; - while stream.next().await.is_some() {} - drop(stream); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); + test_query_execute_many_with_annotations_via_pool!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] async fn query_fetch_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut stream = sqlx::query("SELECT 1 UNION ALL SELECT 2") - .with_annotations(test_annotations()) - .fetch(&pool); - while stream.next().await.is_some() {} - drop(stream); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(2)) - ); + test_query_fetch_with_annotations_via_pool!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] async fn query_fetch_many_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - #[allow(deprecated)] - let mut stream = sqlx::query("SELECT 1 UNION ALL SELECT 2") - .with_annotations(test_annotations()) - .fetch_many(&pool); - while stream.next().await.is_some() {} - drop(stream); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); -} + test_query_fetch_many_with_annotations_via_pool!(test_pool().await, common::POSTGRES_DIALECT); +} #[tokio::test] #[serial] async fn query_fetch_all_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let rows = sqlx::query("SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3") - .with_annotations(test_annotations()) - .fetch_all(&pool) - .await - .unwrap(); - assert_eq!(rows.len(), 3); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(3)) - ); + test_query_fetch_all_with_annotations_via_pool!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] async fn query_fetch_one_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let _row = sqlx::query("SELECT 1") - .with_annotations(test_annotations()) - .fetch_one(&pool) - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(1)) - ); + test_query_fetch_one_with_annotations_via_pool!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] @@ -2148,310 +819,35 @@ async fn query_fetch_optional_with_annotations_via_pool() { let row = sqlx::query("SELECT id FROM qfo_pool WHERE id = 1") .with_annotations(test_annotations()) - .fetch_optional(&pool) - .await - .unwrap(); - assert!(row.is_none()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(0)) - ); -} - -#[tokio::test] -#[serial] -async fn query_bind_first_then_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let row = sqlx::query("SELECT $1::int + $2::int AS sum") - .bind(2_i32) - .bind(3_i32) - .with_annotations(test_annotations()) - .fetch_one(&pool) - .await - .unwrap(); - let sum: i32 = row.try_get("sum").unwrap(); - assert_eq!(sum, 5); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); -} - -#[tokio::test] -#[serial] -async fn query_annotations_first_then_bind_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let row = sqlx::query("SELECT $1::int + $2::int AS sum") - .with_annotations(test_annotations()) - .bind(10_i32) - .bind(20_i32) - .fetch_one(&pool) - .await - .unwrap(); - let sum: i32 = row.try_get("sum").unwrap(); - assert_eq!(sum, 30); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); -} - -#[tokio::test] -#[serial] -async fn query_with_operation_shorthand_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - sqlx::query("CREATE TABLE IF NOT EXISTS qop_pool (id SERIAL PRIMARY KEY)") - .with_operation("SELECT", "users") - .execute(&pool) - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); -} - -#[tokio::test] -#[serial] -async fn query_execute_with_annotations_via_connection() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - sqlx::query("CREATE TABLE IF NOT EXISTS qe_conn (id SERIAL PRIMARY KEY)") - .with_annotations(test_annotations()) - .execute(&mut conn) - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); -} - -#[tokio::test] -#[serial] -async fn query_execute_with_annotations_via_transaction() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut tx: Transaction<'_, Postgres> = pool.begin().await.unwrap(); - sqlx::query("CREATE TABLE IF NOT EXISTS qe_tx (id SERIAL PRIMARY KEY)") - .with_annotations(test_annotations()) - .execute(&mut tx) - .await - .unwrap(); - tx.commit().await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); -} - -#[tokio::test] -#[serial] -async fn query_execute_with_annotations_records_error() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let result = sqlx::query("INVALID SQL GIBBERISH") - .with_annotations(test_annotations()) - .execute(&pool) - .await; - assert!(result.is_err()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); - assert_error_span(&spans[0]); -} - -// --- query_as side --------------------------------------------------------- - -#[tokio::test] -#[serial] -async fn query_as_fetch_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut stream = sqlx::query_as::<_, (i32,)>("SELECT 1 UNION ALL SELECT 2") - .with_annotations(test_annotations()) - .fetch(&pool); - while stream.next().await.is_some() {} - drop(stream); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); -} - -#[tokio::test] -#[serial] -async fn query_as_fetch_many_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - #[allow(deprecated)] - let mut stream = sqlx::query_as::<_, (i32,)>("SELECT 1 UNION ALL SELECT 2") - .with_annotations(test_annotations()) - .fetch_many(&pool); - while stream.next().await.is_some() {} - drop(stream); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); -} - -#[tokio::test] -#[serial] -async fn query_as_fetch_all_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let rows: Vec<(i32,)> = sqlx::query_as("SELECT 1 UNION ALL SELECT 2") - .with_annotations(test_annotations()) - .fetch_all(&pool) - .await - .unwrap(); - assert_eq!(rows.len(), 2); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); -} - -#[tokio::test] -#[serial] -async fn query_as_fetch_one_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let row: (i32,) = sqlx::query_as("SELECT 7") - .with_annotations(test_annotations()) - .fetch_one(&pool) - .await - .unwrap(); - assert_eq!(row.0, 7); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); -} - -#[tokio::test] -#[serial] -async fn query_as_fetch_optional_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let row: Option<(i32,)> = sqlx::query_as("SELECT 1 WHERE 1 = 0") - .with_annotations(test_annotations()) - .fetch_optional(&pool) - .await - .unwrap(); - assert!(row.is_none()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); -} - -#[tokio::test] -#[serial] -async fn query_as_fetch_one_with_annotations_records_error() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let result: Result<(i32,), _> = sqlx::query_as("INVALID SQL") - .with_annotations(test_annotations()) - .fetch_one(&pool) - .await; - assert!(result.is_err()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); - assert_error_span(&spans[0]); -} - -// --- query_scalar side ----------------------------------------------------- - -#[tokio::test] -#[serial] -async fn query_scalar_fetch_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut stream = sqlx::query_scalar::<_, i32>("SELECT 1 UNION ALL SELECT 2") - .with_annotations(test_annotations()) - .fetch(&pool); - while stream.next().await.is_some() {} - drop(stream); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); -} - -#[tokio::test] -#[serial] -async fn query_scalar_fetch_many_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - #[allow(deprecated)] - let mut stream = sqlx::query_scalar::<_, i32>("SELECT 1 UNION ALL SELECT 2") - .with_annotations(test_annotations()) - .fetch_many(&pool); - while stream.next().await.is_some() {} - drop(stream); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); -} - -#[tokio::test] -#[serial] -async fn query_scalar_fetch_all_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let rows: Vec = sqlx::query_scalar("SELECT 1 UNION ALL SELECT 2") - .with_annotations(test_annotations()) - .fetch_all(&pool) + .fetch_optional(&pool) .await .unwrap(); - assert_eq!(rows, vec![1, 2]); + assert!(row.is_none()); let spans = tel.spans(); assert_eq!(spans.len(), 1); assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); + assert_eq!( + attr(&spans[0], "db.response.returned_rows"), + Some(opentelemetry::Value::I64(0)) + ); } #[tokio::test] #[serial] -async fn query_scalar_fetch_one_with_annotations_via_pool() { +async fn query_bind_first_then_annotations_via_pool() { let tel = common::TestTelemetry::install(); let pool = test_pool().await; - let value: i32 = sqlx::query_scalar("SELECT 42") + let row = sqlx::query("SELECT $1::int + $2::int AS sum") + .bind(2_i32) + .bind(3_i32) .with_annotations(test_annotations()) .fetch_one(&pool) .await .unwrap(); - assert_eq!(value, 42); + let sum: i32 = row.try_get("sum").unwrap(); + assert_eq!(sum, 5); let spans = tel.spans(); assert_eq!(spans.len(), 1); @@ -2460,42 +856,36 @@ async fn query_scalar_fetch_one_with_annotations_via_pool() { #[tokio::test] #[serial] -async fn query_scalar_fetch_optional_with_annotations_via_pool() { +async fn query_annotations_first_then_bind_via_pool() { let tel = common::TestTelemetry::install(); let pool = test_pool().await; - let value: Option = sqlx::query_scalar("SELECT 1 WHERE 1 = 0") + let row = sqlx::query("SELECT $1::int + $2::int AS sum") .with_annotations(test_annotations()) - .fetch_optional(&pool) + .bind(10_i32) + .bind(20_i32) + .fetch_one(&pool) .await .unwrap(); - assert!(value.is_none()); + let sum: i32 = row.try_get("sum").unwrap(); + assert_eq!(sum, 30); let spans = tel.spans(); assert_eq!(spans.len(), 1); assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); } -// =========================================================================== -// query-side annotations: Map (Query::map / Query::try_map) -// =========================================================================== - -// --- Per-position end-to-end ---------------------------------------------- - #[tokio::test] #[serial] -async fn query_map_position_1_via_pool() { +async fn query_with_operation_shorthand_via_pool() { let tel = common::TestTelemetry::install(); let pool = test_pool().await; - let value: i64 = sqlx::query("SELECT $1::int8") - .with_annotations(test_annotations()) - .bind(7_i64) - .map(|row: sqlx::postgres::PgRow| row.get::(0)) - .fetch_one(&pool) + sqlx::query("CREATE TABLE IF NOT EXISTS qop_pool (id SERIAL PRIMARY KEY)") + .with_operation("SELECT", "users") + .execute(&pool) .await .unwrap(); - assert_eq!(value, 7); let spans = tel.spans(); assert_eq!(spans.len(), 1); @@ -2504,18 +894,16 @@ async fn query_map_position_1_via_pool() { #[tokio::test] #[serial] -async fn query_map_position_2_via_pool() { +async fn query_execute_with_annotations_via_connection() { let tel = common::TestTelemetry::install(); let pool = test_pool().await; - let value: i64 = sqlx::query("SELECT $1::int8") - .bind(11_i64) + let mut conn = pool.acquire().await.unwrap(); + sqlx::query("CREATE TABLE IF NOT EXISTS qe_conn (id SERIAL PRIMARY KEY)") .with_annotations(test_annotations()) - .map(|row: sqlx::postgres::PgRow| row.get::(0)) - .fetch_one(&pool) + .execute(&mut conn) .await .unwrap(); - assert_eq!(value, 11); let spans = tel.spans(); assert_eq!(spans.len(), 1); @@ -2524,18 +912,17 @@ async fn query_map_position_2_via_pool() { #[tokio::test] #[serial] -async fn query_map_position_3_via_pool() { +async fn query_execute_with_annotations_via_transaction() { let tel = common::TestTelemetry::install(); let pool = test_pool().await; - let value: i64 = sqlx::query("SELECT $1::int8") - .bind(13_i64) - .map(|row: sqlx::postgres::PgRow| row.get::(0)) + let mut tx: Transaction<'_, Postgres> = pool.begin().await.unwrap(); + sqlx::query("CREATE TABLE IF NOT EXISTS qe_tx (id SERIAL PRIMARY KEY)") .with_annotations(test_annotations()) - .fetch_one(&pool) + .execute(&mut tx) .await .unwrap(); - assert_eq!(value, 13); + tx.commit().await.unwrap(); let spans = tel.spans(); assert_eq!(spans.len(), 1); @@ -2544,165 +931,178 @@ async fn query_map_position_3_via_pool() { #[tokio::test] #[serial] -async fn query_try_map_position_3_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; +async fn query_execute_with_annotations_records_error() { + test_query_execute_with_annotations_records_error!(test_pool().await, common::POSTGRES_DIALECT); +} - let value: i64 = sqlx::query("SELECT $1::int8") - .bind(17_i64) - .try_map(|row: sqlx::postgres::PgRow| Ok(row.get::(0))) - .with_annotations(test_annotations()) - .fetch_one(&pool) - .await - .unwrap(); - assert_eq!(value, 17); +// --- query_as side --------------------------------------------------------- - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); +#[tokio::test] +#[serial] +async fn query_as_fetch_with_annotations_via_pool() { + test_query_as_fetch_with_annotations_via_pool!(test_pool().await, common::POSTGRES_DIALECT); } -// --- Per-method on Map (so each forwarder body is hit) -------------------- +#[tokio::test] +#[serial] +async fn query_as_fetch_many_with_annotations_via_pool() { + test_query_as_fetch_many_with_annotations_via_pool!( + test_pool().await, + common::POSTGRES_DIALECT + ); +} #[tokio::test] #[serial] -async fn map_fetch_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; +async fn query_as_fetch_all_with_annotations_via_pool() { + test_query_as_fetch_all_with_annotations_via_pool!(test_pool().await, common::POSTGRES_DIALECT); +} - let mut stream = sqlx::query("SELECT 1::int8 UNION ALL SELECT 2::int8") - .map(|row: sqlx::postgres::PgRow| row.get::(0)) - .with_annotations(test_annotations()) - .fetch(&pool); - while stream.next().await.is_some() {} - drop(stream); +#[tokio::test] +#[serial] +async fn query_as_fetch_one_with_annotations_via_pool() { + test_query_as_fetch_one_with_annotations_via_pool!(test_pool().await, common::POSTGRES_DIALECT); +} - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(2)) +#[tokio::test] +#[serial] +async fn query_as_fetch_optional_with_annotations_via_pool() { + test_query_as_fetch_optional_with_annotations_via_pool!( + test_pool().await, + common::POSTGRES_DIALECT ); } #[tokio::test] #[serial] -async fn map_fetch_many_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; +async fn query_as_fetch_one_with_annotations_records_error() { + test_query_as_fetch_one_with_annotations_records_error!( + test_pool().await, + common::POSTGRES_DIALECT + ); +} - #[allow(deprecated)] - let mut stream = sqlx::query("SELECT 1::int8 UNION ALL SELECT 2::int8") - .map(|row: sqlx::postgres::PgRow| row.get::(0)) - .with_annotations(test_annotations()) - .fetch_many(&pool); - while stream.next().await.is_some() {} - drop(stream); +// --- query_scalar side ----------------------------------------------------- - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); +#[tokio::test] +#[serial] +async fn query_scalar_fetch_with_annotations_via_pool() { + test_query_scalar_fetch_with_annotations_via_pool!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] -async fn map_fetch_all_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; +async fn query_scalar_fetch_many_with_annotations_via_pool() { + test_query_scalar_fetch_many_with_annotations_via_pool!( + test_pool().await, + common::POSTGRES_DIALECT + ); +} - let rows: Vec = - sqlx::query("SELECT 1::int8 UNION ALL SELECT 2::int8 UNION ALL SELECT 3::int8") - .map(|row: sqlx::postgres::PgRow| row.get::(0)) - .with_annotations(test_annotations()) - .fetch_all(&pool) - .await - .unwrap(); - assert_eq!(rows, vec![1, 2, 3]); +#[tokio::test] +#[serial] +async fn query_scalar_fetch_all_with_annotations_via_pool() { + test_query_scalar_fetch_all_with_annotations_via_pool!( + test_pool().await, + common::POSTGRES_DIALECT + ); +} - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); +#[tokio::test] +#[serial] +async fn query_scalar_fetch_one_with_annotations_via_pool() { + test_query_scalar_fetch_one_with_annotations_via_pool!( + test_pool().await, + common::POSTGRES_DIALECT + ); } #[tokio::test] #[serial] -async fn map_fetch_one_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; +async fn query_scalar_fetch_optional_with_annotations_via_pool() { + test_query_scalar_fetch_optional_with_annotations_via_pool!( + test_pool().await, + common::POSTGRES_DIALECT + ); +} - let value: i64 = sqlx::query("SELECT 19::int8") - .map(|row: sqlx::postgres::PgRow| row.get::(0)) - .with_annotations(test_annotations()) - .fetch_one(&pool) - .await - .unwrap(); - assert_eq!(value, 19); +// =========================================================================== +// query-side annotations: Map (Query::map / Query::try_map) +// =========================================================================== - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); +// --- Per-position end-to-end ---------------------------------------------- + +#[tokio::test] +#[serial] +async fn query_map_position_1_via_pool() { + test_query_map_position_1_via_pool!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] -async fn map_fetch_optional_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; +async fn query_map_position_2_via_pool() { + test_query_map_position_2_via_pool!(test_pool().await, common::POSTGRES_DIALECT); +} - let value: Option = sqlx::query("SELECT 1::int8 WHERE 1 = 0") - .map(|row: sqlx::postgres::PgRow| row.get::(0)) - .with_annotations(test_annotations()) - .fetch_optional(&pool) - .await - .unwrap(); - assert!(value.is_none()); +#[tokio::test] +#[serial] +async fn query_map_position_3_via_pool() { + test_query_map_position_3_via_pool!(test_pool().await, common::POSTGRES_DIALECT); +} - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); +#[tokio::test] +#[serial] +async fn query_try_map_position_3_via_pool() { + test_query_try_map_position_3_via_pool!(test_pool().await, common::POSTGRES_DIALECT); } -// --- Composition (multi-map; both branches of step 4) -------------------- +// --- Per-method on Map (so each forwarder body is hit) -------------------- #[tokio::test] #[serial] -async fn map_compose_after_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; +async fn map_fetch_with_annotations_via_pool() { + test_map_fetch_with_annotations_via_pool!(test_pool().await, common::POSTGRES_DIALECT); +} - let value: i64 = sqlx::query("SELECT 5::int8") - .map(|row: sqlx::postgres::PgRow| row.get::(0)) - .with_annotations(test_annotations()) - .map(|n| n * 2) - .fetch_one(&pool) - .await - .unwrap(); - assert_eq!(value, 10); +#[tokio::test] +#[serial] +async fn map_fetch_many_with_annotations_via_pool() { + test_map_fetch_many_with_annotations_via_pool!(test_pool().await, common::POSTGRES_DIALECT); +} - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); +#[tokio::test] +#[serial] +async fn map_fetch_all_with_annotations_via_pool() { + test_map_fetch_all_with_annotations_via_pool!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] -async fn map_try_map_compose_after_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; +async fn map_fetch_one_with_annotations_via_pool() { + test_map_fetch_one_with_annotations_via_pool!(test_pool().await, common::POSTGRES_DIALECT); +} - let value: i64 = sqlx::query("SELECT 6::int8") - .map(|row: sqlx::postgres::PgRow| row.get::(0)) - .with_annotations(test_annotations()) - .try_map(|n: i64| Ok::<_, sqlx::Error>(n + 100)) - .fetch_one(&pool) - .await - .unwrap(); - assert_eq!(value, 106); +#[tokio::test] +#[serial] +async fn map_fetch_optional_with_annotations_via_pool() { + test_map_fetch_optional_with_annotations_via_pool!(test_pool().await, common::POSTGRES_DIALECT); +} - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); +// --- Composition (multi-map; both branches of step 4) -------------------- + +#[tokio::test] +#[serial] +async fn map_compose_after_annotations_via_pool() { + test_map_compose_after_annotations_via_pool!(test_pool().await, common::POSTGRES_DIALECT); +} + +#[tokio::test] +#[serial] +async fn map_try_map_compose_after_annotations_via_pool() { + test_map_try_map_compose_after_annotations_via_pool!( + test_pool().await, + common::POSTGRES_DIALECT + ); } // --- Other executor receivers (smoke) ------------------------------------- @@ -2710,42 +1110,13 @@ async fn map_try_map_compose_after_annotations_via_pool() { #[tokio::test] #[serial] async fn query_map_with_annotations_via_connection() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - let value: i64 = sqlx::query("SELECT 23::int8") - .map(|row: sqlx::postgres::PgRow| row.get::(0)) - .with_annotations(test_annotations()) - .fetch_one(&mut conn) - .await - .unwrap(); - assert_eq!(value, 23); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); + test_query_map_with_annotations_via_connection!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] async fn query_map_with_annotations_via_transaction() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut tx: Transaction<'_, Postgres> = pool.begin().await.unwrap(); - let value: i64 = sqlx::query("SELECT 29::int8") - .map(|row: sqlx::postgres::PgRow| row.get::(0)) - .with_annotations(test_annotations()) - .fetch_one(&mut tx) - .await - .unwrap(); - assert_eq!(value, 29); - tx.commit().await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); + test_query_map_with_annotations_via_transaction!(test_pool().await, common::POSTGRES_DIALECT); } // --- Error paths ---------------------------------------------------------- @@ -2753,47 +1124,16 @@ async fn query_map_with_annotations_via_transaction() { #[tokio::test] #[serial] async fn query_map_with_annotations_records_error() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let result: Result = sqlx::query("INVALID SQL") - .map(|row: sqlx::postgres::PgRow| row.get::(0)) - .with_annotations(test_annotations()) - .fetch_one(&pool) - .await; - assert!(result.is_err()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); - assert_error_span(&spans[0]); + test_query_map_with_annotations_records_error!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] async fn query_try_map_with_annotations_propagates_mapper_error() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - // The mapper error fires *after* the database round-trip succeeds – the executor - // sees the row arrive and completes the fetch successfully, then the mapper surfaces - // the error to the caller. The span therefore stays at success at the database - // layer; the contract verified here is that the user-visible Err propagates through - // the wrapper and that the annotations were attached to the (successful) span. - let result: Result = sqlx::query("SELECT 1::int8") - .try_map(|_row: sqlx::postgres::PgRow| { - Err::(sqlx::Error::Decode( - "intentional decode failure".to_string().into(), - )) - }) - .with_annotations(test_annotations()) - .fetch_one(&pool) - .await; - assert!(result.is_err()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); + test_query_try_map_with_annotations_propagates_mapper_error!( + test_pool().await, + common::POSTGRES_DIALECT + ); } // =========================================================================== diff --git a/tests/sqlite.rs b/tests/sqlite.rs index 8d9c823..47de285 100644 --- a/tests/sqlite.rs +++ b/tests/sqlite.rs @@ -2,23 +2,30 @@ mod common; -use common::{ - assert_annotated_span, assert_common_span_attributes, assert_error_span, attr, test_annotations, -}; -use futures::StreamExt; -use opentelemetry::trace::SpanKind; +use common::{assert_annotated_span, assert_common_span_attributes, attr, test_annotations}; +use futures::StreamExt as _; use serial_test::serial; use sqlx::Executor as _; use sqlx::Row as _; use sqlx::Sqlite; -use sqlx_otel::{Pool, PoolBuilder, QueryAnnotateExt, QueryAnnotations, Transaction}; +use sqlx_otel::{Pool, PoolBuilder, QueryAnnotateExt, Transaction}; const SYSTEM: &str = "sqlite"; +/// Backend row type used by parameterised map test macros: `|row| row.get::(idx)`. +/// Each backend defines its own alias so the shared macros can reference `Row` without +/// hardcoding a per-backend SQL row type. +type Row = sqlx::sqlite::SqliteRow; + /// Helper to create an in-memory Sqlite pool wrapped in our instrumented Pool. async fn test_pool() -> Pool { - let raw = sqlx::SqlitePool::connect(":memory:").await.unwrap(); - PoolBuilder::from(raw).build() + PoolBuilder::from(raw_pool().await).build() +} + +/// Raw (un-instrumented) sqlx pool, used by parameterised builder / query-text-mode +/// tests that need to apply specific `PoolBuilder` configurations themselves. +async fn raw_pool() -> sqlx::SqlitePool { + sqlx::SqlitePool::connect(":memory:").await.unwrap() } // =========================================================================== @@ -34,69 +41,13 @@ async fn execute_creates_span_via_pool() { #[tokio::test] #[serial] async fn execute_creates_span_via_connection() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - sqlx::query("CREATE TABLE exec_conn (id INTEGER PRIMARY KEY)") - .execute(&mut conn) - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - assert!(attr(&spans[0], "db.response.affected_rows").is_some()); - - // With annotations - conn.with_annotations(test_annotations()) - .execute("CREATE TABLE exec_conn2 (id INTEGER PRIMARY KEY)") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); - - // With shorthand - conn.with_operation("SELECT", "users") - .execute("CREATE TABLE exec_conn3 (id INTEGER PRIMARY KEY)") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); + test_execute_creates_span_via_connection!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn execute_creates_span_via_transaction() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut tx: Transaction<'_, Sqlite> = pool.begin().await.unwrap(); - sqlx::query("CREATE TABLE exec_tx (id INTEGER PRIMARY KEY)") - .execute(&mut tx) - .await - .unwrap(); - - // With annotations - tx.with_annotations(test_annotations()) - .execute("CREATE TABLE exec_tx2 (id INTEGER PRIMARY KEY)") - .await - .unwrap(); - - // With shorthand - tx.with_operation("SELECT", "users") - .execute("CREATE TABLE exec_tx3 (id INTEGER PRIMARY KEY)") - .await - .unwrap(); - - tx.commit().await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 3); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - assert!(attr(&spans[0], "db.response.affected_rows").is_some()); - assert_annotated_span(&spans[1], &common::SQLITE_DIALECT); - assert_annotated_span(&spans[2], &common::SQLITE_DIALECT); + test_execute_creates_span_via_transaction!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] @@ -191,37 +142,7 @@ async fn execute_records_affected_rows() { #[tokio::test] #[serial] async fn execute_records_error() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let result = sqlx::query("INVALID SQL GIBBERISH").execute(&pool).await; - assert!(result.is_err()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_error_span(&spans[0]); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - - // With annotations (error path) - let result = pool - .with_annotations(test_annotations()) - .execute("INVALID SQL GIBBERISH") - .await; - assert!(result.is_err()); - let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last, &common::SQLITE_DIALECT); - assert_error_span(&last); - - // With shorthand (error path) - let result = pool - .with_operation("SELECT", "users") - .execute("INVALID SQL GIBBERISH") - .await; - assert!(result.is_err()); - let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last, &common::SQLITE_DIALECT); - assert_error_span(&last); + test_execute_records_error!(test_pool().await, common::SQLITE_DIALECT); } // =========================================================================== @@ -231,153 +152,25 @@ async fn execute_records_error() { #[tokio::test] #[serial] async fn execute_many_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut stream = (&pool).execute_many("SELECT 1; SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(0)) - ); - - // With annotations - let mut stream = pool - .with_annotations(test_annotations()) - .execute_many("SELECT 1; SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); - - // With shorthand - let mut stream = pool - .with_operation("SELECT", "users") - .execute_many("SELECT 1; SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); + test_execute_many_via_pool!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn execute_many_via_connection() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - let mut stream = (&mut conn).execute_many("SELECT 1; SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(0)) - ); - - // With annotations - let mut stream = conn - .with_annotations(test_annotations()) - .execute_many("SELECT 1; SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); - - // With shorthand - let mut stream = conn - .with_operation("SELECT", "users") - .execute_many("SELECT 1; SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); + test_execute_many_via_connection!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn execute_many_via_transaction() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut tx: Transaction<'_, Sqlite> = pool.begin().await.unwrap(); - let mut stream = (&mut tx).execute_many("SELECT 1; SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - - // With annotations - let mut stream = tx - .with_annotations(test_annotations()) - .execute_many("SELECT 1; SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - - // With shorthand - let mut stream = tx - .with_operation("SELECT", "users") - .execute_many("SELECT 1; SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - - tx.commit().await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 3); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(0)) - ); - assert_annotated_span(&spans[1], &common::SQLITE_DIALECT); - assert_annotated_span(&spans[2], &common::SQLITE_DIALECT); + test_execute_many_via_transaction!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn execute_many_records_error() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut stream = (&pool).execute_many("INVALID SQL GIBBERISH"); - let result = stream.next().await; - assert!(result.is_some_and(|r| r.is_err())); - drop(stream); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_error_span(&spans[0]); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(0)) - ); - - // With annotations (error path) - let mut stream = pool - .with_annotations(test_annotations()) - .execute_many("INVALID SQL GIBBERISH"); - let result = stream.next().await; - assert!(result.is_some_and(|r| r.is_err())); - drop(stream); - let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last, &common::SQLITE_DIALECT); - assert_error_span(&last); - - // With shorthand (error path) - let mut stream = pool - .with_operation("SELECT", "users") - .execute_many("INVALID SQL GIBBERISH"); - let result = stream.next().await; - assert!(result.is_some_and(|r| r.is_err())); - drop(stream); - let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last, &common::SQLITE_DIALECT); - assert_error_span(&last); + test_execute_many_records_error!(test_pool().await, common::SQLITE_DIALECT); } // =========================================================================== @@ -387,179 +180,31 @@ async fn execute_many_records_error() { #[tokio::test] #[serial] async fn fetch_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut stream = (&pool).fetch("SELECT 1 UNION ALL SELECT 2"); - let mut count = 0u64; - while stream.next().await.is_some() { - count += 1; - } - assert_eq!(count, 2); - drop(stream); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(2)) - ); - - // With annotations - let mut stream = pool - .with_annotations(test_annotations()) - .fetch("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); - - // With shorthand - let mut stream = pool - .with_operation("SELECT", "users") - .fetch("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); + test_fetch_via_pool!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn fetch_via_connection() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - let mut stream = (&mut conn).fetch("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(2)) - ); - - // With annotations - let mut stream = conn - .with_annotations(test_annotations()) - .fetch("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); - - // With shorthand - let mut stream = conn - .with_operation("SELECT", "users") - .fetch("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); + test_fetch_via_connection!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn fetch_via_transaction() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut tx: Transaction<'_, Sqlite> = pool.begin().await.unwrap(); - let mut stream = (&mut tx).fetch("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - - // With annotations - let mut stream = tx - .with_annotations(test_annotations()) - .fetch("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - - // With shorthand - let mut stream = tx - .with_operation("SELECT", "users") - .fetch("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - - tx.commit().await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 3); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(2)) - ); - assert_annotated_span(&spans[1], &common::SQLITE_DIALECT); - assert_annotated_span(&spans[2], &common::SQLITE_DIALECT); + test_fetch_via_transaction!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn fetch_stream_dropped_early_still_records_span() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - { - let mut stream = (&pool).fetch("SELECT 1 UNION ALL SELECT 2"); - let _ = stream.next().await; - } - - let spans = tel.spans(); - assert_eq!( - spans.len(), - 1, - "span should be recorded even when stream is dropped early" - ); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(1)) - ); + test_fetch_stream_dropped_early_still_records_span!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn fetch_stream_records_error() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut stream = (&pool).fetch("INVALID SQL"); - let result = stream.next().await; - assert!(result.is_some_and(|r| r.is_err())); - drop(stream); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_error_span(&spans[0]); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(0)) - ); - - // With annotations (error path) - let mut stream = pool - .with_annotations(test_annotations()) - .fetch("INVALID SQL"); - let result = stream.next().await; - assert!(result.is_some_and(|r| r.is_err())); - drop(stream); - let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last, &common::SQLITE_DIALECT); - assert_error_span(&last); - - // With shorthand (error path) - let mut stream = pool.with_operation("SELECT", "users").fetch("INVALID SQL"); - let result = stream.next().await; - assert!(result.is_some_and(|r| r.is_err())); - drop(stream); - let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last, &common::SQLITE_DIALECT); - assert_error_span(&last); + test_fetch_stream_records_error!(test_pool().await, common::SQLITE_DIALECT); } // =========================================================================== @@ -569,336 +214,59 @@ async fn fetch_stream_records_error() { #[tokio::test] #[serial] async fn fetch_many_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut stream = (&pool).fetch_many("SELECT 1 UNION ALL SELECT 2"); - let mut rows = 0u64; - let mut results = 0u64; - while let Some(item) = stream.next().await { - match item.unwrap() { - sqlx::Either::Left(_) => results += 1, - sqlx::Either::Right(_) => rows += 1, - } - } - drop(stream); - - assert_eq!(rows, 2); - assert!(results >= 1, "should have at least one QueryResult"); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(2)) - ); - - // With annotations - let mut stream = pool - .with_annotations(test_annotations()) - .fetch_many("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); - - // With shorthand - let mut stream = pool - .with_operation("SELECT", "users") - .fetch_many("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); + test_fetch_many_via_pool!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn fetch_many_via_connection() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - let mut stream = (&mut conn).fetch_many("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(2)) - ); - - // With annotations - let mut stream = conn - .with_annotations(test_annotations()) - .fetch_many("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); - - // With shorthand - let mut stream = conn - .with_operation("SELECT", "users") - .fetch_many("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); + test_fetch_many_via_connection!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn fetch_many_via_transaction() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut tx: Transaction<'_, Sqlite> = pool.begin().await.unwrap(); - let mut stream = (&mut tx).fetch_many("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - - // With annotations - let mut stream = tx - .with_annotations(test_annotations()) - .fetch_many("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - - // With shorthand - let mut stream = tx - .with_operation("SELECT", "users") - .fetch_many("SELECT 1 UNION ALL SELECT 2"); - while stream.next().await.is_some() {} - drop(stream); - - tx.commit().await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 3); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(2)) - ); - assert_annotated_span(&spans[1], &common::SQLITE_DIALECT); - assert_annotated_span(&spans[2], &common::SQLITE_DIALECT); + test_fetch_many_via_transaction!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn fetch_many_dropped_early_still_records_span() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - { - let mut stream = (&pool).fetch_many("SELECT 1 UNION ALL SELECT 2"); - let _ = stream.next().await; - } - - let spans = tel.spans(); - assert_eq!( - spans.len(), - 1, - "span should be recorded even when stream is dropped early" - ); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(1)) - ); + test_fetch_many_dropped_early_still_records_span!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn fetch_many_records_error() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; + test_fetch_many_records_error!(test_pool().await, common::SQLITE_DIALECT); +} - let mut stream = (&pool).fetch_many("INVALID SQL GIBBERISH"); - let result = stream.next().await; - assert!(result.is_some_and(|r| r.is_err())); - drop(stream); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_error_span(&spans[0]); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(0)) - ); - - // With annotations (error path) - let mut stream = pool - .with_annotations(test_annotations()) - .fetch_many("INVALID SQL GIBBERISH"); - let result = stream.next().await; - assert!(result.is_some_and(|r| r.is_err())); - drop(stream); - let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last, &common::SQLITE_DIALECT); - assert_error_span(&last); - - // With shorthand (error path) - let mut stream = pool - .with_operation("SELECT", "users") - .fetch_many("INVALID SQL GIBBERISH"); - let result = stream.next().await; - assert!(result.is_some_and(|r| r.is_err())); - drop(stream); - let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last, &common::SQLITE_DIALECT); - assert_error_span(&last); -} - -// =========================================================================== -// fetch_all -// =========================================================================== +// =========================================================================== +// fetch_all +// =========================================================================== #[tokio::test] #[serial] -async fn fetch_all_records_row_count() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let rows = (&pool) - .fetch_all("SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3") - .await - .unwrap(); - assert_eq!(rows.len(), 3); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(3)) - ); - - // With annotations - pool.with_annotations(test_annotations()) - .fetch_all("SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); - - // With shorthand - pool.with_operation("SELECT", "users") - .fetch_all("SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); +async fn fetch_all_via_pool() { + test_fetch_all_via_pool!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn fetch_all_via_connection() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - let rows = (&mut conn) - .fetch_all("SELECT 1 UNION ALL SELECT 2") - .await - .unwrap(); - assert_eq!(rows.len(), 2); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(2)) - ); - - // With annotations - conn.with_annotations(test_annotations()) - .fetch_all("SELECT 1 UNION ALL SELECT 2") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); - - // With shorthand - conn.with_operation("SELECT", "users") - .fetch_all("SELECT 1 UNION ALL SELECT 2") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); + test_fetch_all_via_connection!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn fetch_all_via_transaction() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut tx: Transaction<'_, Sqlite> = pool.begin().await.unwrap(); - let rows = (&mut tx) - .fetch_all("SELECT 1 UNION ALL SELECT 2") - .await - .unwrap(); - assert_eq!(rows.len(), 2); - - // With annotations - tx.with_annotations(test_annotations()) - .fetch_all("SELECT 1 UNION ALL SELECT 2") - .await - .unwrap(); - - // With shorthand - tx.with_operation("SELECT", "users") - .fetch_all("SELECT 1 UNION ALL SELECT 2") - .await - .unwrap(); - - tx.commit().await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 3); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(2)) - ); - assert_annotated_span(&spans[1], &common::SQLITE_DIALECT); - assert_annotated_span(&spans[2], &common::SQLITE_DIALECT); + test_fetch_all_via_transaction!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn fetch_all_records_error() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let result = (&pool).fetch_all("INVALID SQL GIBBERISH").await; - assert!(result.is_err()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_error_span(&spans[0]); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - - // With annotations (error path) - let result = pool - .with_annotations(test_annotations()) - .fetch_all("INVALID SQL GIBBERISH") - .await; - assert!(result.is_err()); - let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last, &common::SQLITE_DIALECT); - assert_error_span(&last); - - // With shorthand (error path) - let result = pool - .with_operation("SELECT", "users") - .fetch_all("INVALID SQL GIBBERISH") - .await; - assert!(result.is_err()); - let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last, &common::SQLITE_DIALECT); - assert_error_span(&last); + test_fetch_all_records_error!(test_pool().await, common::SQLITE_DIALECT); } // =========================================================================== @@ -908,134 +276,25 @@ async fn fetch_all_records_error() { #[tokio::test] #[serial] async fn fetch_one_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let _row = (&pool).fetch_one("SELECT 1").await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(1)) - ); - - // With annotations - pool.with_annotations(test_annotations()) - .fetch_one("SELECT 1") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); - - // With shorthand - pool.with_operation("SELECT", "users") - .fetch_one("SELECT 1") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); + test_fetch_one_via_pool!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn fetch_one_via_connection() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - let _row = (&mut conn).fetch_one("SELECT 1").await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(1)) - ); - - // With annotations - conn.with_annotations(test_annotations()) - .fetch_one("SELECT 1") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); - - // With shorthand - conn.with_operation("SELECT", "users") - .fetch_one("SELECT 1") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); + test_fetch_one_via_connection!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn fetch_one_via_transaction() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut tx: Transaction<'_, Sqlite> = pool.begin().await.unwrap(); - let _row = (&mut tx).fetch_one("SELECT 1").await.unwrap(); - - // With annotations - tx.with_annotations(test_annotations()) - .fetch_one("SELECT 1") - .await - .unwrap(); - - // With shorthand - tx.with_operation("SELECT", "users") - .fetch_one("SELECT 1") - .await - .unwrap(); - - tx.commit().await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 3); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(1)) - ); - assert_annotated_span(&spans[1], &common::SQLITE_DIALECT); - assert_annotated_span(&spans[2], &common::SQLITE_DIALECT); + test_fetch_one_via_transaction!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn fetch_one_records_error() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let result = (&pool).fetch_one("INVALID SQL GIBBERISH").await; - assert!(result.is_err()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_error_span(&spans[0]); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - - // With annotations (error path) - let result = pool - .with_annotations(test_annotations()) - .fetch_one("INVALID SQL GIBBERISH") - .await; - assert!(result.is_err()); - let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last, &common::SQLITE_DIALECT); - assert_error_span(&last); - - // With shorthand (error path) - let result = pool - .with_operation("SELECT", "users") - .fetch_one("INVALID SQL GIBBERISH") - .await; - assert!(result.is_err()); - let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last, &common::SQLITE_DIALECT); - assert_error_span(&last); + test_fetch_one_records_error!(test_pool().await, common::SQLITE_DIALECT); } // =========================================================================== @@ -1045,33 +304,7 @@ async fn fetch_one_records_error() { #[tokio::test] #[serial] async fn fetch_optional_records_one_row() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let result = (&pool).fetch_optional("SELECT 1").await.unwrap(); - assert!(result.is_some()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(1)) - ); - - // With annotations - pool.with_annotations(test_annotations()) - .fetch_optional("SELECT 1") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); - - // With shorthand - pool.with_operation("SELECT", "users") - .fetch_optional("SELECT 1") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); + test_fetch_optional_records_one_row!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] @@ -1107,105 +340,19 @@ async fn fetch_optional_records_zero_rows() { #[tokio::test] #[serial] async fn fetch_optional_via_connection() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - let result = (&mut conn).fetch_optional("SELECT 42").await.unwrap(); - assert!(result.is_some()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(1)) - ); - - // With annotations - conn.with_annotations(test_annotations()) - .fetch_optional("SELECT 42") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); - - // With shorthand - conn.with_operation("SELECT", "users") - .fetch_optional("SELECT 42") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); + test_fetch_optional_via_connection!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn fetch_optional_via_transaction() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut tx: Transaction<'_, Sqlite> = pool.begin().await.unwrap(); - let result = (&mut tx).fetch_optional("SELECT 99").await.unwrap(); - assert!(result.is_some()); - - // With annotations - tx.with_annotations(test_annotations()) - .fetch_optional("SELECT 99") - .await - .unwrap(); - - // With shorthand - tx.with_operation("SELECT", "users") - .fetch_optional("SELECT 99") - .await - .unwrap(); - - tx.commit().await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 3); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(1)) - ); - assert_annotated_span(&spans[1], &common::SQLITE_DIALECT); - assert_annotated_span(&spans[2], &common::SQLITE_DIALECT); + test_fetch_optional_via_transaction!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn fetch_optional_records_error() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let result = (&pool).fetch_optional("INVALID SQL GIBBERISH").await; - assert!(result.is_err()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_error_span(&spans[0]); - assert_eq!(attr(&spans[0], "db.response.returned_rows"), None); - - // With annotations (error path) - let result = pool - .with_annotations(test_annotations()) - .fetch_optional("INVALID SQL GIBBERISH") - .await; - assert!(result.is_err()); - let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last, &common::SQLITE_DIALECT); - assert_error_span(&last); - - // With shorthand (error path) - let result = pool - .with_operation("SELECT", "users") - .fetch_optional("INVALID SQL GIBBERISH") - .await; - assert!(result.is_err()); - let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last, &common::SQLITE_DIALECT); - assert_error_span(&last); + test_fetch_optional_records_error!(test_pool().await, common::SQLITE_DIALECT); } // =========================================================================== @@ -1215,126 +362,25 @@ async fn fetch_optional_records_error() { #[tokio::test] #[serial] async fn prepare_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let _stmt = (&pool).prepare("SELECT 1").await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - - // With annotations - pool.with_annotations(test_annotations()) - .prepare("SELECT 1") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); - - // With shorthand - pool.with_operation("SELECT", "users") - .prepare("SELECT 1") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); + test_prepare_via_pool!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn prepare_via_connection() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - let _stmt = (&mut conn).prepare("SELECT 1").await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - - // With annotations - conn.with_annotations(test_annotations()) - .prepare("SELECT 1") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); - - // With shorthand - conn.with_operation("SELECT", "users") - .prepare("SELECT 1") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); + test_prepare_via_connection!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn prepare_via_transaction() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut tx: Transaction<'_, Sqlite> = pool.begin().await.unwrap(); - let _stmt = (&mut tx).prepare("SELECT 1").await.unwrap(); - - // With annotations - tx.with_annotations(test_annotations()) - .prepare("SELECT 1") - .await - .unwrap(); - - // With shorthand - tx.with_operation("SELECT", "users") - .prepare("SELECT 1") - .await - .unwrap(); - - tx.commit().await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 3); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - assert_annotated_span(&spans[1], &common::SQLITE_DIALECT); - assert_annotated_span(&spans[2], &common::SQLITE_DIALECT); + test_prepare_via_transaction!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn prepare_records_error() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - let result = (&mut conn).prepare("INVALID SQL GIBBERISH").await; - assert!(result.is_err()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_error_span(&spans[0]); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - - // With annotations (error path) - let result = conn - .with_annotations(test_annotations()) - .prepare("INVALID SQL GIBBERISH") - .await; - assert!(result.is_err()); - let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last, &common::SQLITE_DIALECT); - assert_error_span(&last); - - // With shorthand (error path) - let result = conn - .with_operation("SELECT", "users") - .prepare("INVALID SQL GIBBERISH") - .await; - assert!(result.is_err()); - let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last, &common::SQLITE_DIALECT); - assert_error_span(&last); + test_prepare_records_error!(test_pool().await, common::SQLITE_DIALECT); } // =========================================================================== @@ -1344,126 +390,25 @@ async fn prepare_records_error() { #[tokio::test] #[serial] async fn prepare_with_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let _stmt = (&pool).prepare_with("SELECT ?", &[]).await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - - // With annotations - pool.with_annotations(test_annotations()) - .prepare_with("SELECT ?", &[]) - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); - - // With shorthand - pool.with_operation("SELECT", "users") - .prepare_with("SELECT ?", &[]) - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); + test_prepare_with_via_pool!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn prepare_with_via_connection() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - let _stmt = (&mut conn).prepare_with("SELECT ?", &[]).await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - - // With annotations - conn.with_annotations(test_annotations()) - .prepare_with("SELECT ?", &[]) - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); - - // With shorthand - conn.with_operation("SELECT", "users") - .prepare_with("SELECT ?", &[]) - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); + test_prepare_with_via_connection!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn prepare_with_via_transaction() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut tx: Transaction<'_, Sqlite> = pool.begin().await.unwrap(); - let _stmt = (&mut tx).prepare_with("SELECT ?", &[]).await.unwrap(); - - // With annotations - tx.with_annotations(test_annotations()) - .prepare_with("SELECT ?", &[]) - .await - .unwrap(); - - // With shorthand - tx.with_operation("SELECT", "users") - .prepare_with("SELECT ?", &[]) - .await - .unwrap(); - - tx.commit().await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 3); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - assert_annotated_span(&spans[1], &common::SQLITE_DIALECT); - assert_annotated_span(&spans[2], &common::SQLITE_DIALECT); + test_prepare_with_via_transaction!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn prepare_with_records_error() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - let result = (&mut conn).prepare_with("INVALID SQL GIBBERISH", &[]).await; - assert!(result.is_err()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_error_span(&spans[0]); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - - // With annotations (error path) - let result = conn - .with_annotations(test_annotations()) - .prepare_with("INVALID SQL GIBBERISH", &[]) - .await; - assert!(result.is_err()); - let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last, &common::SQLITE_DIALECT); - assert_error_span(&last); - - // With shorthand (error path) - let result = conn - .with_operation("SELECT", "users") - .prepare_with("INVALID SQL GIBBERISH", &[]) - .await; - assert!(result.is_err()); - let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last, &common::SQLITE_DIALECT); - assert_error_span(&last); + test_prepare_with_records_error!(test_pool().await, common::SQLITE_DIALECT); } // =========================================================================== @@ -1473,126 +418,25 @@ async fn prepare_with_records_error() { #[tokio::test] #[serial] async fn describe_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let _desc = (&pool).describe("SELECT 1").await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - - // With annotations - pool.with_annotations(test_annotations()) - .describe("SELECT 1") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); - - // With shorthand - pool.with_operation("SELECT", "users") - .describe("SELECT 1") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); + test_describe_via_pool!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn describe_via_connection() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - let _desc = (&mut conn).describe("SELECT 1").await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - - // With annotations - conn.with_annotations(test_annotations()) - .describe("SELECT 1") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); - - // With shorthand - conn.with_operation("SELECT", "users") - .describe("SELECT 1") - .await - .unwrap(); - assert_annotated_span(tel.spans().last().unwrap(), &common::SQLITE_DIALECT); + test_describe_via_connection!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn describe_via_transaction() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut tx: Transaction<'_, Sqlite> = pool.begin().await.unwrap(); - let _desc = (&mut tx).describe("SELECT 1").await.unwrap(); - - // With annotations - tx.with_annotations(test_annotations()) - .describe("SELECT 1") - .await - .unwrap(); - - // With shorthand - tx.with_operation("SELECT", "users") - .describe("SELECT 1") - .await - .unwrap(); - - tx.commit().await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 3); - assert_common_span_attributes(&spans[0], SYSTEM); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - assert_annotated_span(&spans[1], &common::SQLITE_DIALECT); - assert_annotated_span(&spans[2], &common::SQLITE_DIALECT); + test_describe_via_transaction!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn describe_records_error() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - let result = (&mut conn).describe("INVALID SQL GIBBERISH").await; - assert!(result.is_err()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_error_span(&spans[0]); - assert!(attr(&spans[0], "db.response.returned_rows").is_none()); - - // With annotations (error path) - let result = conn - .with_annotations(test_annotations()) - .describe("INVALID SQL GIBBERISH") - .await; - assert!(result.is_err()); - let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last, &common::SQLITE_DIALECT); - assert_error_span(&last); - - // With shorthand (error path) - let result = conn - .with_operation("SELECT", "users") - .describe("INVALID SQL GIBBERISH") - .await; - assert!(result.is_err()); - let last = tel.spans().last().unwrap().clone(); - assert_annotated_span(&last, &common::SQLITE_DIALECT); - assert_error_span(&last); + test_describe_records_error!(test_pool().await, common::SQLITE_DIALECT); } // =========================================================================== @@ -1647,29 +491,7 @@ async fn operation_duration_metric_is_recorded() { #[tokio::test] #[serial] async fn query_text_mode_off_suppresses_sql() { - let tel = common::TestTelemetry::install(); - let raw = sqlx::SqlitePool::connect(":memory:").await.unwrap(); - let pool = PoolBuilder::from(raw) - .with_query_text_mode(sqlx_otel::QueryTextMode::Off) - .build(); - - let _: Option<(i32,)> = sqlx::query_as("SELECT 1") - .fetch_optional(&pool) - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_eq!(spans[0].span_kind, SpanKind::Client); - assert_eq!( - attr(&spans[0], "db.system.name"), - Some(opentelemetry::Value::String(SYSTEM.into())) - ); - assert!(attr(&spans[0], "db.namespace").is_some()); - assert!( - attr(&spans[0], "db.query.text").is_none(), - "db.query.text should not be present when QueryTextMode::Off" - ); + test_query_text_mode_off_suppresses_sql!(raw_pool().await, common::SQLITE_DIALECT); } #[tokio::test] @@ -1729,88 +551,31 @@ async fn transaction_rollback() { #[tokio::test] #[serial] async fn builder_with_database_overrides_namespace() { - let tel = common::TestTelemetry::install(); - let raw = sqlx::SqlitePool::connect(":memory:").await.unwrap(); - let pool = PoolBuilder::from(raw).with_database("custom_db").build(); - - let _ = (&pool).fetch_optional("SELECT 1").await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_eq!( - attr(&spans[0], "db.namespace"), - Some(opentelemetry::Value::String("custom_db".into())) - ); + test_builder_with_database_overrides_namespace!(raw_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn builder_with_host_overrides_server_address() { - let tel = common::TestTelemetry::install(); - let raw = sqlx::SqlitePool::connect(":memory:").await.unwrap(); - let pool = PoolBuilder::from(raw).with_host("custom-host").build(); - - let _ = (&pool).fetch_optional("SELECT 1").await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_eq!( - attr(&spans[0], "server.address"), - Some(opentelemetry::Value::String("custom-host".into())) - ); + test_builder_with_host_overrides_server_address!(raw_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn builder_with_port_overrides_server_port() { - let tel = common::TestTelemetry::install(); - let raw = sqlx::SqlitePool::connect(":memory:").await.unwrap(); - let pool = PoolBuilder::from(raw).with_port(9999).build(); - - let _ = (&pool).fetch_optional("SELECT 1").await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_eq!( - attr(&spans[0], "server.port"), - Some(opentelemetry::Value::I64(9999)) - ); + test_builder_with_port_overrides_server_port!(raw_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn builder_with_network_peer_address() { - let tel = common::TestTelemetry::install(); - let raw = sqlx::SqlitePool::connect(":memory:").await.unwrap(); - let pool = PoolBuilder::from(raw) - .with_network_peer_address("10.0.0.5") - .build(); - - let _ = (&pool).fetch_optional("SELECT 1").await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_eq!( - attr(&spans[0], "network.peer.address"), - Some(opentelemetry::Value::String("10.0.0.5".into())) - ); + test_builder_with_network_peer_address!(raw_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn builder_with_network_peer_port() { - let tel = common::TestTelemetry::install(); - let raw = sqlx::SqlitePool::connect(":memory:").await.unwrap(); - let pool = PoolBuilder::from(raw).with_network_peer_port(5433).build(); - - let _ = (&pool).fetch_optional("SELECT 1").await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_eq!( - attr(&spans[0], "network.peer.port"), - Some(opentelemetry::Value::I64(5433)) - ); + test_builder_with_network_peer_port!(raw_pool().await, common::SQLITE_DIALECT); } // =========================================================================== @@ -1835,75 +600,13 @@ async fn pool_close_and_is_closed() { #[tokio::test] #[serial] async fn annotation_all_four_fields() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - pool.with_annotations( - QueryAnnotations::new() - .operation("SELECT") - .collection("users") - .query_summary("users by id") - .stored_procedure("sp_get_users"), - ) - .fetch_all("SELECT 1") - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - // Summary drives the span name (semconv level 1), distinct from "SELECT users" so - // the assertion proves the summary path won rather than coinciding with level 2. - assert_eq!(spans[0].name, "users by id"); - assert_eq!( - attr(&spans[0], "db.operation.name"), - Some(opentelemetry::Value::String("SELECT".into())), - ); - assert_eq!( - attr(&spans[0], "db.collection.name"), - Some(opentelemetry::Value::String("users".into())), - ); - assert_eq!( - attr(&spans[0], "db.query.summary"), - Some(opentelemetry::Value::String("users by id".into())), - ); - assert_eq!( - attr(&spans[0], "db.stored_procedure.name"), - Some(opentelemetry::Value::String("sp_get_users".into())), - ); + test_annotation_all_four_fields!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn query_summary_drives_span_name() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - pool.with_annotations( - QueryAnnotations::new() - .operation("SELECT") - .collection("users") - .query_summary("users by tenant"), - ) - .fetch_all("SELECT 1") - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_eq!(spans[0].name, "users by tenant"); - // Summary drives the *name*, but does not suppress the other attributes. - assert_eq!( - attr(&spans[0], "db.query.summary"), - Some(opentelemetry::Value::String("users by tenant".into())), - ); - assert_eq!( - attr(&spans[0], "db.operation.name"), - Some(opentelemetry::Value::String("SELECT".into())), - ); - assert_eq!( - attr(&spans[0], "db.collection.name"), - Some(opentelemetry::Value::String("users".into())), - ); + test_query_summary_drives_span_name!(test_pool().await, common::SQLITE_DIALECT); } // =========================================================================== @@ -1931,102 +634,31 @@ async fn query_execute_with_annotations_via_pool() { #[tokio::test] #[serial] async fn query_execute_many_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - #[allow(deprecated)] - let mut stream = sqlx::query("SELECT 1; SELECT 2") - .with_annotations(test_annotations()) - .execute_many(&pool) - .await; - while stream.next().await.is_some() {} - drop(stream); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); + test_query_execute_many_with_annotations_via_pool!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn query_fetch_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut stream = sqlx::query("SELECT 1 UNION ALL SELECT 2") - .with_annotations(test_annotations()) - .fetch(&pool); - while stream.next().await.is_some() {} - drop(stream); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(2)) - ); + test_query_fetch_with_annotations_via_pool!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn query_fetch_many_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - #[allow(deprecated)] - let mut stream = sqlx::query("SELECT 1 UNION ALL SELECT 2") - .with_annotations(test_annotations()) - .fetch_many(&pool); - while stream.next().await.is_some() {} - drop(stream); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); + test_query_fetch_many_with_annotations_via_pool!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn query_fetch_all_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let rows = sqlx::query("SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3") - .with_annotations(test_annotations()) - .fetch_all(&pool) - .await - .unwrap(); - assert_eq!(rows.len(), 3); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(3)) - ); + test_query_fetch_all_with_annotations_via_pool!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn query_fetch_one_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let _row = sqlx::query("SELECT 1") - .with_annotations(test_annotations()) - .fetch_one(&pool) - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(1)) - ); + test_query_fetch_one_with_annotations_via_pool!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] @@ -2046,290 +678,33 @@ async fn query_fetch_optional_with_annotations_via_pool() { .with_annotations(test_annotations()) .fetch_optional(&pool) .await - .unwrap(); - assert!(row.is_none()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(0)) - ); -} - -#[tokio::test] -#[serial] -async fn query_bind_first_then_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let row = sqlx::query("SELECT ?1 + ?2 AS sum") - .bind(2_i32) - .bind(3_i32) - .with_annotations(test_annotations()) - .fetch_one(&pool) - .await - .unwrap(); - let sum: i32 = row.try_get("sum").unwrap(); - assert_eq!(sum, 5); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); -} - -#[tokio::test] -#[serial] -async fn query_annotations_first_then_bind_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let row = sqlx::query("SELECT ?1 + ?2 AS sum") - .with_annotations(test_annotations()) - .bind(10_i32) - .bind(20_i32) - .fetch_one(&pool) - .await - .unwrap(); - let sum: i32 = row.try_get("sum").unwrap(); - assert_eq!(sum, 30); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); -} - -#[tokio::test] -#[serial] -async fn query_with_operation_shorthand_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - sqlx::query("CREATE TABLE qop_pool (id INTEGER PRIMARY KEY)") - .with_operation("SELECT", "users") - .execute(&pool) - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); -} - -#[tokio::test] -#[serial] -async fn query_execute_with_annotations_via_connection() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - sqlx::query("CREATE TABLE qe_conn (id INTEGER PRIMARY KEY)") - .with_annotations(test_annotations()) - .execute(&mut conn) - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); -} - -#[tokio::test] -#[serial] -async fn query_execute_with_annotations_via_transaction() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut tx: Transaction<'_, Sqlite> = pool.begin().await.unwrap(); - sqlx::query("CREATE TABLE qe_tx (id INTEGER PRIMARY KEY)") - .with_annotations(test_annotations()) - .execute(&mut tx) - .await - .unwrap(); - tx.commit().await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); -} - -#[tokio::test] -#[serial] -async fn query_execute_with_annotations_records_error() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let result = sqlx::query("INVALID SQL GIBBERISH") - .with_annotations(test_annotations()) - .execute(&pool) - .await; - assert!(result.is_err()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); - assert_error_span(&spans[0]); -} - -// --- query_as side --------------------------------------------------------- - -#[tokio::test] -#[serial] -async fn query_as_fetch_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut stream = sqlx::query_as::<_, (i32,)>("SELECT 1 UNION ALL SELECT 2") - .with_annotations(test_annotations()) - .fetch(&pool); - while stream.next().await.is_some() {} - drop(stream); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); -} - -#[tokio::test] -#[serial] -async fn query_as_fetch_many_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - #[allow(deprecated)] - let mut stream = sqlx::query_as::<_, (i32,)>("SELECT 1 UNION ALL SELECT 2") - .with_annotations(test_annotations()) - .fetch_many(&pool); - while stream.next().await.is_some() {} - drop(stream); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); -} - -#[tokio::test] -#[serial] -async fn query_as_fetch_all_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let rows: Vec<(i32,)> = sqlx::query_as("SELECT 1 UNION ALL SELECT 2") - .with_annotations(test_annotations()) - .fetch_all(&pool) - .await - .unwrap(); - assert_eq!(rows.len(), 2); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); -} - -#[tokio::test] -#[serial] -async fn query_as_fetch_one_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let row: (i32,) = sqlx::query_as("SELECT 7") - .with_annotations(test_annotations()) - .fetch_one(&pool) - .await - .unwrap(); - assert_eq!(row.0, 7); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); -} - -#[tokio::test] -#[serial] -async fn query_as_fetch_optional_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let row: Option<(i32,)> = sqlx::query_as("SELECT 1 WHERE 1 = 0") - .with_annotations(test_annotations()) - .fetch_optional(&pool) - .await - .unwrap(); - assert!(row.is_none()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); -} - -#[tokio::test] -#[serial] -async fn query_as_fetch_one_with_annotations_records_error() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let result: Result<(i32,), _> = sqlx::query_as("INVALID SQL") - .with_annotations(test_annotations()) - .fetch_one(&pool) - .await; - assert!(result.is_err()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); - assert_error_span(&spans[0]); -} - -// --- query_scalar side ----------------------------------------------------- - -#[tokio::test] -#[serial] -async fn query_scalar_fetch_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut stream = sqlx::query_scalar::<_, i32>("SELECT 1 UNION ALL SELECT 2") - .with_annotations(test_annotations()) - .fetch(&pool); - while stream.next().await.is_some() {} - drop(stream); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); -} - -#[tokio::test] -#[serial] -async fn query_scalar_fetch_many_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - #[allow(deprecated)] - let mut stream = sqlx::query_scalar::<_, i32>("SELECT 1 UNION ALL SELECT 2") - .with_annotations(test_annotations()) - .fetch_many(&pool); - while stream.next().await.is_some() {} - drop(stream); + .unwrap(); + assert!(row.is_none()); let spans = tel.spans(); assert_eq!(spans.len(), 1); assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); + assert_eq!( + attr(&spans[0], "db.response.returned_rows"), + Some(opentelemetry::Value::I64(0)) + ); } #[tokio::test] #[serial] -async fn query_scalar_fetch_all_with_annotations_via_pool() { +async fn query_bind_first_then_annotations_via_pool() { let tel = common::TestTelemetry::install(); let pool = test_pool().await; - let rows: Vec = sqlx::query_scalar("SELECT 1 UNION ALL SELECT 2") + let row = sqlx::query("SELECT ?1 + ?2 AS sum") + .bind(2_i32) + .bind(3_i32) .with_annotations(test_annotations()) - .fetch_all(&pool) + .fetch_one(&pool) .await .unwrap(); - assert_eq!(rows, vec![1, 2]); + let sum: i32 = row.try_get("sum").unwrap(); + assert_eq!(sum, 5); let spans = tel.spans(); assert_eq!(spans.len(), 1); @@ -2338,16 +713,19 @@ async fn query_scalar_fetch_all_with_annotations_via_pool() { #[tokio::test] #[serial] -async fn query_scalar_fetch_one_with_annotations_via_pool() { +async fn query_annotations_first_then_bind_via_pool() { let tel = common::TestTelemetry::install(); let pool = test_pool().await; - let value: i32 = sqlx::query_scalar("SELECT 42") + let row = sqlx::query("SELECT ?1 + ?2 AS sum") .with_annotations(test_annotations()) + .bind(10_i32) + .bind(20_i32) .fetch_one(&pool) .await .unwrap(); - assert_eq!(value, 42); + let sum: i32 = row.try_get("sum").unwrap(); + assert_eq!(sum, 30); let spans = tel.spans(); assert_eq!(spans.len(), 1); @@ -2356,42 +734,33 @@ async fn query_scalar_fetch_one_with_annotations_via_pool() { #[tokio::test] #[serial] -async fn query_scalar_fetch_optional_with_annotations_via_pool() { +async fn query_with_operation_shorthand_via_pool() { let tel = common::TestTelemetry::install(); let pool = test_pool().await; - let value: Option = sqlx::query_scalar("SELECT 1 WHERE 1 = 0") - .with_annotations(test_annotations()) - .fetch_optional(&pool) + sqlx::query("CREATE TABLE qop_pool (id INTEGER PRIMARY KEY)") + .with_operation("SELECT", "users") + .execute(&pool) .await .unwrap(); - assert!(value.is_none()); let spans = tel.spans(); assert_eq!(spans.len(), 1); assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); } -// =========================================================================== -// query-side annotations: Map (Query::map / Query::try_map) -// =========================================================================== - -// --- Per-position end-to-end ---------------------------------------------- - #[tokio::test] #[serial] -async fn query_map_position_1_via_pool() { +async fn query_execute_with_annotations_via_connection() { let tel = common::TestTelemetry::install(); let pool = test_pool().await; - let value: i64 = sqlx::query("SELECT ?1") + let mut conn = pool.acquire().await.unwrap(); + sqlx::query("CREATE TABLE qe_conn (id INTEGER PRIMARY KEY)") .with_annotations(test_annotations()) - .bind(7_i64) - .map(|row: sqlx::sqlite::SqliteRow| row.get::(0)) - .fetch_one(&pool) + .execute(&mut conn) .await .unwrap(); - assert_eq!(value, 7); let spans = tel.spans(); assert_eq!(spans.len(), 1); @@ -2400,18 +769,17 @@ async fn query_map_position_1_via_pool() { #[tokio::test] #[serial] -async fn query_map_position_2_via_pool() { +async fn query_execute_with_annotations_via_transaction() { let tel = common::TestTelemetry::install(); let pool = test_pool().await; - let value: i64 = sqlx::query("SELECT ?1") - .bind(11_i64) + let mut tx: Transaction<'_, Sqlite> = pool.begin().await.unwrap(); + sqlx::query("CREATE TABLE qe_tx (id INTEGER PRIMARY KEY)") .with_annotations(test_annotations()) - .map(|row: sqlx::sqlite::SqliteRow| row.get::(0)) - .fetch_one(&pool) + .execute(&mut tx) .await .unwrap(); - assert_eq!(value, 11); + tx.commit().await.unwrap(); let spans = tel.spans(); assert_eq!(spans.len(), 1); @@ -2420,142 +788,158 @@ async fn query_map_position_2_via_pool() { #[tokio::test] #[serial] -async fn query_map_position_3_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; +async fn query_execute_with_annotations_records_error() { + test_query_execute_with_annotations_records_error!(test_pool().await, common::SQLITE_DIALECT); +} - let value: i64 = sqlx::query("SELECT ?1") - .bind(13_i64) - .map(|row: sqlx::sqlite::SqliteRow| row.get::(0)) - .with_annotations(test_annotations()) - .fetch_one(&pool) - .await - .unwrap(); - assert_eq!(value, 13); +// --- query_as side --------------------------------------------------------- - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); +#[tokio::test] +#[serial] +async fn query_as_fetch_with_annotations_via_pool() { + test_query_as_fetch_with_annotations_via_pool!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] -async fn query_try_map_position_3_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; +async fn query_as_fetch_many_with_annotations_via_pool() { + test_query_as_fetch_many_with_annotations_via_pool!(test_pool().await, common::SQLITE_DIALECT); +} - let value: i64 = sqlx::query("SELECT ?1") - .bind(17_i64) - .try_map(|row: sqlx::sqlite::SqliteRow| Ok(row.get::(0))) - .with_annotations(test_annotations()) - .fetch_one(&pool) - .await - .unwrap(); - assert_eq!(value, 17); +#[tokio::test] +#[serial] +async fn query_as_fetch_all_with_annotations_via_pool() { + test_query_as_fetch_all_with_annotations_via_pool!(test_pool().await, common::SQLITE_DIALECT); +} - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); +#[tokio::test] +#[serial] +async fn query_as_fetch_one_with_annotations_via_pool() { + test_query_as_fetch_one_with_annotations_via_pool!(test_pool().await, common::SQLITE_DIALECT); } -// --- Per-method on Map (so each forwarder body is hit) -------------------- +#[tokio::test] +#[serial] +async fn query_as_fetch_optional_with_annotations_via_pool() { + test_query_as_fetch_optional_with_annotations_via_pool!( + test_pool().await, + common::SQLITE_DIALECT + ); +} #[tokio::test] #[serial] -async fn map_fetch_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; +async fn query_as_fetch_one_with_annotations_records_error() { + test_query_as_fetch_one_with_annotations_records_error!( + test_pool().await, + common::SQLITE_DIALECT + ); +} - let mut stream = sqlx::query("SELECT 1 UNION ALL SELECT 2") - .map(|row: sqlx::sqlite::SqliteRow| row.get::(0)) - .with_annotations(test_annotations()) - .fetch(&pool); - while stream.next().await.is_some() {} - drop(stream); +// --- query_scalar side ----------------------------------------------------- - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(2)) +#[tokio::test] +#[serial] +async fn query_scalar_fetch_with_annotations_via_pool() { + test_query_scalar_fetch_with_annotations_via_pool!(test_pool().await, common::SQLITE_DIALECT); +} + +#[tokio::test] +#[serial] +async fn query_scalar_fetch_many_with_annotations_via_pool() { + test_query_scalar_fetch_many_with_annotations_via_pool!( + test_pool().await, + common::SQLITE_DIALECT ); } #[tokio::test] #[serial] -async fn map_fetch_many_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; +async fn query_scalar_fetch_all_with_annotations_via_pool() { + test_query_scalar_fetch_all_with_annotations_via_pool!( + test_pool().await, + common::SQLITE_DIALECT + ); +} - #[allow(deprecated)] - let mut stream = sqlx::query("SELECT 1 UNION ALL SELECT 2") - .map(|row: sqlx::sqlite::SqliteRow| row.get::(0)) - .with_annotations(test_annotations()) - .fetch_many(&pool); - while stream.next().await.is_some() {} - drop(stream); +#[tokio::test] +#[serial] +async fn query_scalar_fetch_one_with_annotations_via_pool() { + test_query_scalar_fetch_one_with_annotations_via_pool!( + test_pool().await, + common::SQLITE_DIALECT + ); +} - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); +#[tokio::test] +#[serial] +async fn query_scalar_fetch_optional_with_annotations_via_pool() { + test_query_scalar_fetch_optional_with_annotations_via_pool!( + test_pool().await, + common::SQLITE_DIALECT + ); } +// =========================================================================== +// query-side annotations: Map (Query::map / Query::try_map) +// =========================================================================== + +// --- Per-position end-to-end ---------------------------------------------- + #[tokio::test] #[serial] -async fn map_fetch_all_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; +async fn query_map_position_1_via_pool() { + test_query_map_position_1_via_pool!(test_pool().await, common::SQLITE_DIALECT); +} - let rows: Vec = sqlx::query("SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3") - .map(|row: sqlx::sqlite::SqliteRow| row.get::(0)) - .with_annotations(test_annotations()) - .fetch_all(&pool) - .await - .unwrap(); - assert_eq!(rows, vec![1, 2, 3]); +#[tokio::test] +#[serial] +async fn query_map_position_2_via_pool() { + test_query_map_position_2_via_pool!(test_pool().await, common::SQLITE_DIALECT); +} - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); +#[tokio::test] +#[serial] +async fn query_map_position_3_via_pool() { + test_query_map_position_3_via_pool!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] -async fn map_fetch_one_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; +async fn query_try_map_position_3_via_pool() { + test_query_try_map_position_3_via_pool!(test_pool().await, common::SQLITE_DIALECT); +} - let value: i64 = sqlx::query("SELECT 19") - .map(|row: sqlx::sqlite::SqliteRow| row.get::(0)) - .with_annotations(test_annotations()) - .fetch_one(&pool) - .await - .unwrap(); - assert_eq!(value, 19); +// --- Per-method on Map (so each forwarder body is hit) -------------------- - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); +#[tokio::test] +#[serial] +async fn map_fetch_with_annotations_via_pool() { + test_map_fetch_with_annotations_via_pool!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] -async fn map_fetch_optional_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; +async fn map_fetch_many_with_annotations_via_pool() { + test_map_fetch_many_with_annotations_via_pool!(test_pool().await, common::SQLITE_DIALECT); +} - let value: Option = sqlx::query("SELECT 1 WHERE 1 = 0") - .map(|row: sqlx::sqlite::SqliteRow| row.get::(0)) - .with_annotations(test_annotations()) - .fetch_optional(&pool) - .await - .unwrap(); - assert!(value.is_none()); +#[tokio::test] +#[serial] +async fn map_fetch_all_with_annotations_via_pool() { + test_map_fetch_all_with_annotations_via_pool!(test_pool().await, common::SQLITE_DIALECT); +} - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); +#[tokio::test] +#[serial] +async fn map_fetch_one_with_annotations_via_pool() { + test_map_fetch_one_with_annotations_via_pool!(test_pool().await, common::SQLITE_DIALECT); +} + +#[tokio::test] +#[serial] +async fn map_fetch_optional_with_annotations_via_pool() { + test_map_fetch_optional_with_annotations_via_pool!(test_pool().await, common::SQLITE_DIALECT); } // --- Composition (multi-map; both branches of step 4) -------------------- @@ -2563,41 +947,13 @@ async fn map_fetch_optional_with_annotations_via_pool() { #[tokio::test] #[serial] async fn map_compose_after_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let value: i64 = sqlx::query("SELECT 5") - .map(|row: sqlx::sqlite::SqliteRow| row.get::(0)) - .with_annotations(test_annotations()) - .map(|n| n * 2) - .fetch_one(&pool) - .await - .unwrap(); - assert_eq!(value, 10); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); + test_map_compose_after_annotations_via_pool!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn map_try_map_compose_after_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let value: i64 = sqlx::query("SELECT 6") - .map(|row: sqlx::sqlite::SqliteRow| row.get::(0)) - .with_annotations(test_annotations()) - .try_map(|n: i64| Ok::<_, sqlx::Error>(n + 100)) - .fetch_one(&pool) - .await - .unwrap(); - assert_eq!(value, 106); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); + test_map_try_map_compose_after_annotations_via_pool!(test_pool().await, common::SQLITE_DIALECT); } // --- Other executor receivers (smoke) ------------------------------------- @@ -2605,42 +961,13 @@ async fn map_try_map_compose_after_annotations_via_pool() { #[tokio::test] #[serial] async fn query_map_with_annotations_via_connection() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - let value: i64 = sqlx::query("SELECT 23") - .map(|row: sqlx::sqlite::SqliteRow| row.get::(0)) - .with_annotations(test_annotations()) - .fetch_one(&mut conn) - .await - .unwrap(); - assert_eq!(value, 23); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); + test_query_map_with_annotations_via_connection!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn query_map_with_annotations_via_transaction() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut tx: Transaction<'_, Sqlite> = pool.begin().await.unwrap(); - let value: i64 = sqlx::query("SELECT 29") - .map(|row: sqlx::sqlite::SqliteRow| row.get::(0)) - .with_annotations(test_annotations()) - .fetch_one(&mut tx) - .await - .unwrap(); - assert_eq!(value, 29); - tx.commit().await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); + test_query_map_with_annotations_via_transaction!(test_pool().await, common::SQLITE_DIALECT); } // --- Error paths ---------------------------------------------------------- @@ -2648,48 +975,16 @@ async fn query_map_with_annotations_via_transaction() { #[tokio::test] #[serial] async fn query_map_with_annotations_records_error() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let result: Result = sqlx::query("INVALID SQL") - .map(|row: sqlx::sqlite::SqliteRow| row.get::(0)) - .with_annotations(test_annotations()) - .fetch_one(&pool) - .await; - assert!(result.is_err()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); - assert_error_span(&spans[0]); + test_query_map_with_annotations_records_error!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn query_try_map_with_annotations_propagates_mapper_error() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - // A `try_map` closure that fails on an otherwise-valid row produces a user-visible - // error, but it happens *after* the database round-trip has already succeeded – the - // executor sees the row arrive, completes the fetch, and only then does the mapper - // surface the error. The span therefore reports success at the database layer; the - // important contract here is that the user-visible Err carries through and that the - // annotations were attached to the (successful) span. - let result: Result = sqlx::query("SELECT 1") - .try_map(|_row: sqlx::sqlite::SqliteRow| { - Err::(sqlx::Error::Decode( - "intentional decode failure".to_string().into(), - )) - }) - .with_annotations(test_annotations()) - .fetch_one(&pool) - .await; - assert!(result.is_err()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); + test_query_try_map_with_annotations_propagates_mapper_error!( + test_pool().await, + common::SQLITE_DIALECT + ); } // =========================================================================== From 797a4dc109e718b4e7cacbaf7e889978b3cba8aa Mon Sep 17 00:00:00 2001 From: Borislav Borisov Date: Tue, 28 Apr 2026 11:18:05 +0100 Subject: [PATCH 3/4] test: Collapse macro-test assertion blocks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the recurring `let spans = tel.spans(); assert_eq!(spans.len(), 1); assert_annotated_span(&spans[0], &);` block at the end of every sqlx::query!() / query_as!() / query_scalar!() macro test with a single `common::assert_one_annotated_span(&tel, &)` call. Macro bodies themselves stay backend-specific (compile-time literal SQL). Also migrates `operation_duration_metric_is_recorded`, `pool_close_and_is_closed`, and the two `query_text_mode_*` tests in postgres.rs and mysql.rs to the shared macros (these were missed by an earlier pattern that required `let tel = …` as the first body line). --- tests/common.rs | 404 ++++++++++++++++++++++++++++++++++++++++++++-- tests/mysql.rs | 384 +++---------------------------------------- tests/postgres.rs | 389 ++++---------------------------------------- tests/sqlite.rs | 349 +++------------------------------------ 4 files changed, 477 insertions(+), 1049 deletions(-) diff --git a/tests/common.rs b/tests/common.rs index 2f81bec..c66c480 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -207,24 +207,61 @@ pub struct Dialect { /// Column definition for a non-null text column: e.g. `"TEXT NOT NULL"` for sqlite /// and postgres, `"VARCHAR(255) NOT NULL"` for mysql. pub text_column: &'static str, + /// Full SQL for an upsert that updates `affected_test`'s row id=1 to a new name. + /// Each backend's syntax differs (`INSERT OR REPLACE` / `ON CONFLICT … DO UPDATE` / + /// `ON DUPLICATE KEY UPDATE`). + pub upsert_sql: &'static str, + /// Expected `db.response.affected_rows` for the upsert above. Sqlite and postgres + /// report `1`; mysql reports `2` (it counts match + update). + pub upsert_affected_rows: i64, + /// Full SQL for an UPDATE that mutates two rows by appending `_updated` to `name` + /// using the dialect's string-concat operator (`||` for sqlite/postgres, `CONCAT(...)` + /// for mysql). + pub string_concat_update_sql: &'static str, + /// Full SQL of the form `SELECT (?1 + ?2) AS sum`, accepting two `i32` binds and + /// returning an `i64` named `sum`. Each backend uses its own placeholder syntax and + /// (for postgres / mysql) explicit casts so the result fits in `i64` uniformly. + pub bind_two_sum_sql: &'static str, + /// Full SQL of the form `SELECT ` for `prepare_with` calls that supply + /// no concrete binds. Each backend uses its own placeholder syntax (`?` for sqlite + /// and mysql, `$1` for postgres). + pub prepare_with_select_sql: &'static str, } pub const SQLITE_DIALECT: Dialect = Dialect { system: "sqlite", id_pk_column: "INTEGER PRIMARY KEY", text_column: "TEXT NOT NULL", + upsert_sql: "INSERT OR REPLACE INTO affected_test (id, name) VALUES (1, 'alice_updated')", + upsert_affected_rows: 1, + string_concat_update_sql: "UPDATE affected_test SET name = name || '_updated' WHERE id IN (2, 3)", + bind_two_sum_sql: "SELECT ?1 + ?2 AS sum", + prepare_with_select_sql: "SELECT ?", }; pub const POSTGRES_DIALECT: Dialect = Dialect { system: "postgresql", id_pk_column: "INT PRIMARY KEY", text_column: "TEXT NOT NULL", + upsert_sql: "INSERT INTO affected_test (id, name) VALUES (1, 'alice_updated') \ + ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name", + upsert_affected_rows: 1, + string_concat_update_sql: "UPDATE affected_test SET name = name || '_updated' WHERE id IN (2, 3)", + bind_two_sum_sql: "SELECT ($1::bigint + $2::bigint) AS sum", + prepare_with_select_sql: "SELECT $1", }; pub const MYSQL_DIALECT: Dialect = Dialect { system: "mysql", id_pk_column: "INT PRIMARY KEY", text_column: "VARCHAR(255) NOT NULL", + upsert_sql: "INSERT INTO affected_test (id, name) VALUES (1, 'alice_updated') \ + ON DUPLICATE KEY UPDATE name = VALUES(name)", + // MySQL counts ON DUPLICATE KEY UPDATE as match (1) + update (1) = 2. + upsert_affected_rows: 2, + string_concat_update_sql: "UPDATE affected_test SET name = CONCAT(name, '_updated') WHERE id IN (2, 3)", + bind_two_sum_sql: "SELECT CAST(? + ? AS SIGNED) AS sum", + prepare_with_select_sql: "SELECT ?", }; /// `DROP TABLE IF EXISTS` then `CREATE TABLE` at the supplied pool. Used at the top of @@ -1590,7 +1627,10 @@ macro_rules! test_prepare_with_via_pool { let tel = $crate::common::TestTelemetry::install(); let pool = $pool_factory; - let _stmt = (&pool).prepare_with("SELECT ?", &[]).await.unwrap(); + let _stmt = (&pool) + .prepare_with($dialect.prepare_with_select_sql, &[]) + .await + .unwrap(); let spans = tel.spans(); assert_eq!(spans.len(), 1); @@ -1598,13 +1638,13 @@ macro_rules! test_prepare_with_via_pool { assert!($crate::common::attr(&spans[0], "db.response.returned_rows").is_none()); pool.with_annotations($crate::common::test_annotations()) - .prepare_with("SELECT ?", &[]) + .prepare_with($dialect.prepare_with_select_sql, &[]) .await .unwrap(); $crate::common::assert_annotated_span(tel.spans().last().unwrap(), &$dialect); pool.with_operation("SELECT", "users") - .prepare_with("SELECT ?", &[]) + .prepare_with($dialect.prepare_with_select_sql, &[]) .await .unwrap(); $crate::common::assert_annotated_span(tel.spans().last().unwrap(), &$dialect); @@ -1620,7 +1660,10 @@ macro_rules! test_prepare_with_via_connection { let pool = $pool_factory; let mut conn = pool.acquire().await.unwrap(); - let _stmt = (&mut conn).prepare_with("SELECT ?", &[]).await.unwrap(); + let _stmt = (&mut conn) + .prepare_with($dialect.prepare_with_select_sql, &[]) + .await + .unwrap(); let spans = tel.spans(); assert_eq!(spans.len(), 1); @@ -1628,13 +1671,13 @@ macro_rules! test_prepare_with_via_connection { assert!($crate::common::attr(&spans[0], "db.response.returned_rows").is_none()); conn.with_annotations($crate::common::test_annotations()) - .prepare_with("SELECT ?", &[]) + .prepare_with($dialect.prepare_with_select_sql, &[]) .await .unwrap(); $crate::common::assert_annotated_span(tel.spans().last().unwrap(), &$dialect); conn.with_operation("SELECT", "users") - .prepare_with("SELECT ?", &[]) + .prepare_with($dialect.prepare_with_select_sql, &[]) .await .unwrap(); $crate::common::assert_annotated_span(tel.spans().last().unwrap(), &$dialect); @@ -1650,15 +1693,18 @@ macro_rules! test_prepare_with_via_transaction { let pool = $pool_factory; let mut tx = pool.begin().await.unwrap(); - let _stmt = (&mut tx).prepare_with("SELECT ?", &[]).await.unwrap(); + let _stmt = (&mut tx) + .prepare_with($dialect.prepare_with_select_sql, &[]) + .await + .unwrap(); tx.with_annotations($crate::common::test_annotations()) - .prepare_with("SELECT ?", &[]) + .prepare_with($dialect.prepare_with_select_sql, &[]) .await .unwrap(); tx.with_operation("SELECT", "users") - .prepare_with("SELECT ?", &[]) + .prepare_with($dialect.prepare_with_select_sql, &[]) .await .unwrap(); @@ -2871,3 +2917,343 @@ macro_rules! test_query_text_mode_off_suppresses_sql { ); }}; } + +// --------------------------------------------------------------------------- +// Dialect-portable test bodies +// --------------------------------------------------------------------------- + +/// `execute` records the correct `db.response.affected_rows` for a sequence of +/// INSERT / upsert / UPDATE / DELETE statements. Uses the dialect's `upsert_sql`, +/// `upsert_affected_rows`, and `string_concat_update_sql` to handle backend-specific +/// upsert syntax and string-concat operators. +#[macro_export] +macro_rules! test_execute_records_affected_rows { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx::Executor as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + $crate::fresh_table!( + &pool, + "affected_test", + &format!("id {}, name {}", $dialect.id_pk_column, $dialect.text_column) + ); + tel.reset(); + + // --- Bulk insert via VALUES list --- + (&pool) + .execute( + "INSERT INTO affected_test (id, name) VALUES (1, 'alice'), (2, 'bob'), (3, 'carol')", + ) + .await + .unwrap(); + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + assert_eq!( + $crate::common::attr(&spans[0], "db.response.affected_rows"), + Some(opentelemetry::Value::I64(3)), + "inserting 3 rows should affect 3 rows" + ); + tel.reset(); + + // --- Upsert (dialect-specific) --- + (&pool).execute($dialect.upsert_sql).await.unwrap(); + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + assert_eq!( + $crate::common::attr(&spans[0], "db.response.affected_rows"), + Some(opentelemetry::Value::I64($dialect.upsert_affected_rows)), + "upsert affected_rows differs per backend" + ); + tel.reset(); + + // --- Update multiple rows (dialect-specific concat) --- + (&pool) + .execute($dialect.string_concat_update_sql) + .await + .unwrap(); + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + assert_eq!( + $crate::common::attr(&spans[0], "db.response.affected_rows"), + Some(opentelemetry::Value::I64(2)), + "updating two rows should affect 2 rows" + ); + tel.reset(); + + // --- Delete multiple rows --- + (&pool) + .execute("DELETE FROM affected_test WHERE id IN (1, 2, 3)") + .await + .unwrap(); + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + assert_eq!( + $crate::common::attr(&spans[0], "db.response.affected_rows"), + Some(opentelemetry::Value::I64(3)), + "deleting three rows should affect 3 rows" + ); + tel.reset(); + + // --- Delete with no matching rows --- + (&pool) + .execute("DELETE FROM affected_test WHERE id = 999") + .await + .unwrap(); + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + assert_eq!( + $crate::common::attr(&spans[0], "db.response.affected_rows"), + Some(opentelemetry::Value::I64(0)), + "deleting non-existent rows should affect 0 rows" + ); + }}; +} + +/// Transaction rollback emits a single CREATE TABLE span and discards the table. +/// Uses `fresh_table!` so the test is repeatable against the shared postgres / mysql +/// containers. +#[macro_export] +macro_rules! test_transaction_rollback { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx::Executor as _; + let pool = $pool_factory; + // Pre-clean any leftover table before installing telemetry, so the rollback test + // sees only its own span. + let drop_sql = "DROP TABLE IF EXISTS rollback_test"; + (&pool).execute(drop_sql).await.unwrap(); + + let tel = $crate::common::TestTelemetry::install(); + + let mut tx = pool.begin().await.unwrap(); + let create_sql = format!("CREATE TABLE rollback_test (id {})", $dialect.id_pk_column); + (&mut tx).execute(create_sql.as_str()).await.unwrap(); + tx.rollback().await.unwrap(); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_common_span_attributes(&spans[0], $dialect.system); + }}; +} + +/// `QueryTextMode::Obfuscated` rewrites string and numeric literals in `db.query.text`. +/// The query under test is dialect-neutral (`SELECT 1, 'alice', 3.14`), so the only +/// dialect input is the raw pool factory used to build a custom-configured pool. +#[macro_export] +macro_rules! test_query_text_mode_obfuscated_replaces_literals { + ($raw_pool_factory:expr, $dialect:expr) => {{ + use sqlx::Executor as _; + let _ = $dialect; + let raw = $raw_pool_factory; + let pool = sqlx_otel::PoolBuilder::from(raw) + .with_query_text_mode(sqlx_otel::QueryTextMode::Obfuscated) + .build(); + + let tel = $crate::common::TestTelemetry::install(); + let _row = (&pool) + .fetch_optional("SELECT 1, 'alice', 3.14") + .await + .unwrap(); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + assert_eq!( + $crate::common::attr(&spans[0], "db.query.text"), + Some(opentelemetry::Value::String("SELECT ?, ?, ?".into())) + ); + }}; +} + +/// `fetch_optional` against an empty table returns `None` and records `returned_rows = 0`. +/// Uses `fresh_table!` to set up a guaranteed-empty table. +#[macro_export] +macro_rules! test_fetch_optional_records_zero_rows { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx::Executor as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + $crate::fresh_table!( + &pool, + "empty_table", + &format!("id {}", $dialect.id_pk_column) + ); + tel.reset(); + + let result = (&pool) + .fetch_optional("SELECT id FROM empty_table") + .await + .unwrap(); + assert!(result.is_none()); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_common_span_attributes(&spans[0], $dialect.system); + assert_eq!( + $crate::common::attr(&spans[0], "db.response.returned_rows"), + Some(opentelemetry::Value::I64(0)) + ); + }}; +} + +/// `sqlx::query(...).bind(...).bind(...).with_annotations(...).fetch_one(&pool)` with a +/// dialect-specific SELECT that adds two bound `i32` arguments and returns an `i64`. +#[macro_export] +macro_rules! test_query_bind_first_then_annotations_via_pool { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx::Row as _; + use sqlx_otel::QueryAnnotateExt as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let row = sqlx::query($dialect.bind_two_sum_sql) + .bind(2_i32) + .bind(3_i32) + .with_annotations($crate::common::test_annotations()) + .fetch_one(&pool) + .await + .unwrap(); + let sum: i64 = row.try_get("sum").unwrap(); + assert_eq!(sum, 5); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_annotated_span(&spans[0], &$dialect); + }}; +} + +/// Same as `test_query_bind_first_then_annotations_via_pool` but with `with_annotations` +/// applied before the binds. +#[macro_export] +macro_rules! test_query_annotations_first_then_bind_via_pool { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx::Row as _; + use sqlx_otel::QueryAnnotateExt as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let row = sqlx::query($dialect.bind_two_sum_sql) + .with_annotations($crate::common::test_annotations()) + .bind(10_i32) + .bind(20_i32) + .fetch_one(&pool) + .await + .unwrap(); + let sum: i64 = row.try_get("sum").unwrap(); + assert_eq!(sum, 30); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_annotated_span(&spans[0], &$dialect); + }}; +} + +/// Annotated `execute` against the wrapped pool. Uses `SELECT 1` so the test is +/// portable; the executor records `affected_rows` regardless of statement kind. +#[macro_export] +macro_rules! test_query_execute_with_annotations_via_pool { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx_otel::QueryAnnotateExt as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + sqlx::query("SELECT 1") + .with_annotations($crate::common::test_annotations()) + .execute(&pool) + .await + .unwrap(); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_annotated_span(&spans[0], &$dialect); + assert!($crate::common::attr(&spans[0], "db.response.affected_rows").is_some()); + }}; +} + +/// Annotated `execute` against `&mut PoolConnection`. +#[macro_export] +macro_rules! test_query_execute_with_annotations_via_connection { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx_otel::QueryAnnotateExt as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let mut conn = pool.acquire().await.unwrap(); + sqlx::query("SELECT 1") + .with_annotations($crate::common::test_annotations()) + .execute(&mut conn) + .await + .unwrap(); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_annotated_span(&spans[0], &$dialect); + }}; +} + +/// Annotated `execute` against `&mut Transaction<'_, DB>`. +#[macro_export] +macro_rules! test_query_execute_with_annotations_via_transaction { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx_otel::QueryAnnotateExt as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let mut tx = pool.begin().await.unwrap(); + sqlx::query("SELECT 1") + .with_annotations($crate::common::test_annotations()) + .execute(&mut tx) + .await + .unwrap(); + tx.commit().await.unwrap(); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_annotated_span(&spans[0], &$dialect); + }}; +} + +/// `with_operation` shorthand attaching the same annotations as the manual +/// `with_annotations(test_annotations())` call. +#[macro_export] +macro_rules! test_query_with_operation_shorthand_via_pool { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx_otel::QueryAnnotateExt as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + sqlx::query("SELECT 1") + .with_operation("SELECT", "users") + .execute(&pool) + .await + .unwrap(); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_annotated_span(&spans[0], &$dialect); + }}; +} + +/// Annotated `fetch_optional` returning `None`. Uses `SELECT 1 WHERE 1 = 0` to express +/// the empty-row case without dialect-specific table setup. +#[macro_export] +macro_rules! test_query_fetch_optional_with_annotations_via_pool { + ($pool_factory:expr, $dialect:expr) => {{ + use sqlx_otel::QueryAnnotateExt as _; + let tel = $crate::common::TestTelemetry::install(); + let pool = $pool_factory; + + let row = sqlx::query("SELECT 1 WHERE 1 = 0") + .with_annotations($crate::common::test_annotations()) + .fetch_optional(&pool) + .await + .unwrap(); + assert!(row.is_none()); + + let spans = tel.spans(); + assert_eq!(spans.len(), 1); + $crate::common::assert_annotated_span(&spans[0], &$dialect); + assert_eq!( + $crate::common::attr(&spans[0], "db.response.returned_rows"), + Some(opentelemetry::Value::I64(0)) + ); + }}; +} diff --git a/tests/mysql.rs b/tests/mysql.rs index 83cb6e6..e730219 100644 --- a/tests/mysql.rs +++ b/tests/mysql.rs @@ -5,22 +5,16 @@ mod common; use std::sync::OnceLock; use std::time::Duration; -use common::{ - assert_annotated_span, assert_common_span_attributes, assert_error_span, attr, test_annotations, -}; -use opentelemetry::trace::SpanKind; +use common::{assert_error_span, attr, test_annotations}; use serial_test::serial; use sqlx::Executor as _; use sqlx::MySql; -use sqlx::Row as _; -use sqlx_otel::{Pool, PoolBuilder, QueryAnnotateExt, Transaction}; +use sqlx_otel::{Pool, PoolBuilder, QueryAnnotateExt}; use testcontainers::core::IntoContainerPort; use testcontainers::runners::AsyncRunner; use testcontainers::{ContainerAsync, GenericImage, ImageExt}; use tokio::sync::OnceCell; -const SYSTEM: &str = "mysql"; - /// Backend row type used by parameterised map test macros (see `tests/sqlite.rs`). type Row = sqlx::mysql::MySqlRow; @@ -102,102 +96,7 @@ async fn execute_records_error() { #[tokio::test] #[serial] async fn execute_records_affected_rows() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - sqlx::query( - "CREATE TABLE IF NOT EXISTS affected_test (id INT PRIMARY KEY, name VARCHAR(255) NOT NULL)", - ) - .execute(&pool) - .await - .unwrap(); - sqlx::query("DELETE FROM affected_test") - .execute(&pool) - .await - .unwrap(); - - tel.reset(); - - // --- Bulk insert --- - sqlx::query( - "INSERT INTO affected_test (id, name) VALUES (1, 'alice'), (2, 'bob'), (3, 'carol')", - ) - .execute(&pool) - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_eq!( - attr(&spans[0], "db.response.affected_rows"), - Some(opentelemetry::Value::I64(3)), - "inserting 3 rows in one statement should affect 3 rows" - ); - tel.reset(); - - // --- Upsert (INSERT ON DUPLICATE KEY UPDATE) --- - sqlx::query( - "INSERT INTO affected_test (id, name) VALUES (1, 'alice_updated') \ - ON DUPLICATE KEY UPDATE name = VALUES(name)", - ) - .execute(&pool) - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - // MySQL reports 2 for ON DUPLICATE KEY UPDATE – it counts the matched row plus the - // updated row. - assert_eq!( - attr(&spans[0], "db.response.affected_rows"), - Some(opentelemetry::Value::I64(2)), - "MySQL upsert reports 2 affected rows (match + update)" - ); - tel.reset(); - - // --- Update multiple rows --- - sqlx::query("UPDATE affected_test SET name = CONCAT(name, '_updated') WHERE id IN (2, 3)") - .execute(&pool) - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_eq!( - attr(&spans[0], "db.response.affected_rows"), - Some(opentelemetry::Value::I64(2)), - "updating two rows should affect 2 rows" - ); - tel.reset(); - - // --- Delete multiple rows --- - sqlx::query("DELETE FROM affected_test WHERE id IN (1, 2, 3)") - .execute(&pool) - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_eq!( - attr(&spans[0], "db.response.affected_rows"), - Some(opentelemetry::Value::I64(3)), - "deleting three rows should affect 3 rows" - ); - tel.reset(); - - // --- Delete with no matching rows --- - sqlx::query("DELETE FROM affected_test WHERE id = 999") - .execute(&pool) - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_eq!( - attr(&spans[0], "db.response.affected_rows"), - Some(opentelemetry::Value::I64(0)), - "deleting non-existent rows should affect 0 rows" - ); + test_execute_records_affected_rows!(test_pool().await, common::MYSQL_DIALECT); } // =========================================================================== @@ -365,32 +264,7 @@ async fn fetch_optional_records_one_row() { #[tokio::test] #[serial] async fn fetch_optional_records_zero_rows() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - sqlx::query("CREATE TABLE IF NOT EXISTS empty_table (id INT AUTO_INCREMENT PRIMARY KEY)") - .execute(&pool) - .await - .unwrap(); - sqlx::query("DELETE FROM empty_table") - .execute(&pool) - .await - .unwrap(); - - tel.reset(); - let result = (&pool) - .fetch_optional("SELECT id FROM empty_table") - .await - .unwrap(); - assert!(result.is_none()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(0)) - ); + test_fetch_optional_records_zero_rows!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] @@ -573,42 +447,7 @@ async fn sqlstate_recorded_on_constraint_violation() { #[tokio::test] #[serial] async fn operation_duration_metric_is_recorded() { - use opentelemetry_sdk::metrics::data::{AggregatedMetrics, MetricData}; - - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let _row = (&pool).fetch_one("SELECT 1").await.unwrap(); - - let resource_metrics = tel.metrics(); - assert!(!resource_metrics.is_empty(), "should have metric data"); - - let mut found_duration = false; - for rm in &resource_metrics { - for sm in rm.scope_metrics() { - for metric in sm.metrics() { - if metric.name() == "db.client.operation.duration" { - found_duration = true; - assert_eq!(metric.unit(), "s"); - if let AggregatedMetrics::F64(MetricData::Histogram(hist)) = metric.data() { - let dp: Vec<_> = hist.data_points().collect(); - assert!(!dp.is_empty(), "histogram should have data points"); - assert!(dp[0].count() > 0, "data point count should be > 0"); - let has_system = dp[0] - .attributes() - .any(|kv| kv.key.as_str() == "db.system.name"); - assert!(has_system, "metric should have db.system.name attribute"); - } else { - panic!("db.client.operation.duration should be an f64 histogram"); - } - } - } - } - } - assert!( - found_duration, - "db.client.operation.duration metric not found" - ); + test_operation_duration_metric_is_recorded!(test_pool().await, common::MYSQL_DIALECT); } // =========================================================================== @@ -618,19 +457,7 @@ async fn operation_duration_metric_is_recorded() { #[tokio::test] #[serial] async fn transaction_rollback() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut tx: Transaction<'_, MySql> = pool.begin().await.unwrap(); - sqlx::query("CREATE TABLE IF NOT EXISTS rollback_test (id INT AUTO_INCREMENT PRIMARY KEY)") - .execute(&mut tx) - .await - .unwrap(); - tx.rollback().await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); + test_transaction_rollback!(test_pool().await, common::MYSQL_DIALECT); } // =========================================================================== @@ -674,12 +501,7 @@ async fn builder_with_network_peer_port() { #[tokio::test] #[serial] async fn pool_close_and_is_closed() { - let _tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - assert!(!pool.is_closed()); - pool.close().await; - assert!(pool.is_closed()); + test_pool_close_and_is_closed!(test_pool().await, common::MYSQL_DIALECT); } // =========================================================================== @@ -689,50 +511,13 @@ async fn pool_close_and_is_closed() { #[tokio::test] #[serial] async fn query_text_mode_off_suppresses_sql() { - let shared = shared_container().await; - let raw = sqlx::MySqlPool::connect(&shared.url).await.unwrap(); - let pool = PoolBuilder::from(raw) - .with_query_text_mode(sqlx_otel::QueryTextMode::Off) - .build(); - - let tel = common::TestTelemetry::install(); - let _row = (&pool).fetch_optional("SELECT 1").await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_eq!(spans[0].span_kind, SpanKind::Client); - assert_eq!( - attr(&spans[0], "db.system.name"), - Some(opentelemetry::Value::String(SYSTEM.to_owned().into())) - ); - assert!(attr(&spans[0], "db.namespace").is_some()); - assert!( - attr(&spans[0], "db.query.text").is_none(), - "db.query.text should not be present when QueryTextMode::Off" - ); + test_query_text_mode_off_suppresses_sql!(raw_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn query_text_mode_obfuscated_replaces_literals() { - let shared = shared_container().await; - let raw = sqlx::MySqlPool::connect(&shared.url).await.unwrap(); - let pool = PoolBuilder::from(raw) - .with_query_text_mode(sqlx_otel::QueryTextMode::Obfuscated) - .build(); - - let tel = common::TestTelemetry::install(); - let _row = (&pool) - .fetch_optional("SELECT 1, 'alice', 3.14") - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_eq!( - attr(&spans[0], "db.query.text"), - Some(opentelemetry::Value::String("SELECT ?, ?, ?".into())) - ); + test_query_text_mode_obfuscated_replaces_literals!(raw_pool().await, common::MYSQL_DIALECT); } // =========================================================================== @@ -758,19 +543,7 @@ async fn query_summary_drives_span_name() { #[tokio::test] #[serial] async fn query_execute_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - sqlx::query("CREATE TABLE IF NOT EXISTS qe_pool (id INT AUTO_INCREMENT PRIMARY KEY)") - .with_annotations(test_annotations()) - .execute(&pool) - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); - assert!(attr(&spans[0], "db.response.affected_rows").is_some()); + test_query_execute_with_annotations_via_pool!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] @@ -806,126 +579,37 @@ async fn query_fetch_one_with_annotations_via_pool() { #[tokio::test] #[serial] async fn query_fetch_optional_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - sqlx::query("CREATE TABLE IF NOT EXISTS qfo_pool (id INT PRIMARY KEY)") - .execute(&pool) - .await - .unwrap(); - - tel.reset(); - - let row = sqlx::query("SELECT id FROM qfo_pool WHERE id = 1") - .with_annotations(test_annotations()) - .fetch_optional(&pool) - .await - .unwrap(); - assert!(row.is_none()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(0)) - ); + test_query_fetch_optional_with_annotations_via_pool!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn query_bind_first_then_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let row = sqlx::query("SELECT CAST(? + ? AS SIGNED) AS sum") - .bind(2_i32) - .bind(3_i32) - .with_annotations(test_annotations()) - .fetch_one(&pool) - .await - .unwrap(); - let sum: i64 = row.try_get("sum").unwrap(); - assert_eq!(sum, 5); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); + test_query_bind_first_then_annotations_via_pool!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn query_annotations_first_then_bind_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let row = sqlx::query("SELECT CAST(? + ? AS SIGNED) AS sum") - .with_annotations(test_annotations()) - .bind(10_i32) - .bind(20_i32) - .fetch_one(&pool) - .await - .unwrap(); - let sum: i64 = row.try_get("sum").unwrap(); - assert_eq!(sum, 30); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); + test_query_annotations_first_then_bind_via_pool!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn query_with_operation_shorthand_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - sqlx::query("CREATE TABLE IF NOT EXISTS qop_pool (id INT AUTO_INCREMENT PRIMARY KEY)") - .with_operation("SELECT", "users") - .execute(&pool) - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); + test_query_with_operation_shorthand_via_pool!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn query_execute_with_annotations_via_connection() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - sqlx::query("CREATE TABLE IF NOT EXISTS qe_conn (id INT AUTO_INCREMENT PRIMARY KEY)") - .with_annotations(test_annotations()) - .execute(&mut conn) - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); + test_query_execute_with_annotations_via_connection!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] #[serial] async fn query_execute_with_annotations_via_transaction() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut tx: Transaction<'_, MySql> = pool.begin().await.unwrap(); - sqlx::query("CREATE TABLE IF NOT EXISTS qe_tx (id INT AUTO_INCREMENT PRIMARY KEY)") - .with_annotations(test_annotations()) - .execute(&mut tx) - .await - .unwrap(); - tx.commit().await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); + test_query_execute_with_annotations_via_transaction!(test_pool().await, common::MYSQL_DIALECT); } #[tokio::test] @@ -1166,9 +850,7 @@ async fn query_macro_execute_with_annotations_via_pool() { .unwrap(); assert_eq!(result.rows_affected(), 1); - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); + common::assert_one_annotated_span(&tel, &common::MYSQL_DIALECT); } #[tokio::test] @@ -1194,9 +876,7 @@ async fn query_macro_fetch_one_with_annotations_via_pool() { assert_eq!(row.id, 202); assert_eq!(row.name, "bob"); - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); + common::assert_one_annotated_span(&tel, &common::MYSQL_DIALECT); } #[tokio::test] @@ -1227,9 +907,7 @@ async fn query_macro_fetch_all_with_annotations_via_pool() { .unwrap(); assert_eq!(rows.len(), 3); - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); + common::assert_one_annotated_span(&tel, &common::MYSQL_DIALECT); } #[tokio::test] @@ -1250,9 +928,7 @@ async fn query_macro_fetch_optional_with_annotations_via_pool() { .unwrap(); assert!(row.is_none()); - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); + common::assert_one_annotated_span(&tel, &common::MYSQL_DIALECT); } type MacroUser = common::MacroUser; @@ -1284,9 +960,7 @@ async fn query_as_macro_fetch_one_with_annotations_via_pool() { assert_eq!(user.id, 206); assert_eq!(user.name, "frank"); - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); + common::assert_one_annotated_span(&tel, &common::MYSQL_DIALECT); } #[tokio::test] @@ -1316,9 +990,7 @@ async fn query_as_macro_fetch_all_with_annotations_via_pool() { .unwrap(); assert_eq!(users.len(), 2); - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); + common::assert_one_annotated_span(&tel, &common::MYSQL_DIALECT); } #[tokio::test] @@ -1343,9 +1015,7 @@ async fn query_as_macro_fetch_optional_with_annotations_via_pool() { .unwrap(); assert!(user.is_none()); - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); + common::assert_one_annotated_span(&tel, &common::MYSQL_DIALECT); } #[tokio::test] @@ -1370,9 +1040,7 @@ async fn query_scalar_macro_fetch_one_with_annotations_via_pool() { .unwrap(); assert_eq!(name, "irene"); - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); + common::assert_one_annotated_span(&tel, &common::MYSQL_DIALECT); } #[tokio::test] @@ -1401,7 +1069,5 @@ async fn query_scalar_macro_fetch_all_with_annotations_via_pool() { .unwrap(); assert_eq!(ids, vec![210, 211]); - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::MYSQL_DIALECT); + common::assert_one_annotated_span(&tel, &common::MYSQL_DIALECT); } diff --git a/tests/postgres.rs b/tests/postgres.rs index 437ba21..38634e5 100644 --- a/tests/postgres.rs +++ b/tests/postgres.rs @@ -5,22 +5,16 @@ mod common; use std::sync::OnceLock; use std::time::Duration; -use common::{ - assert_annotated_span, assert_common_span_attributes, assert_error_span, attr, test_annotations, -}; -use opentelemetry::trace::SpanKind; +use common::{assert_error_span, attr, test_annotations}; use serial_test::serial; use sqlx::Executor as _; use sqlx::Postgres; -use sqlx::Row as _; -use sqlx_otel::{Pool, PoolBuilder, QueryAnnotateExt, Transaction}; +use sqlx_otel::{Pool, PoolBuilder, QueryAnnotateExt}; use testcontainers::core::IntoContainerPort; use testcontainers::runners::AsyncRunner; use testcontainers::{ContainerAsync, GenericImage, ImageExt}; use tokio::sync::OnceCell; -const SYSTEM: &str = "postgresql"; - /// Backend row type used by parameterised map test macros (see `tests/sqlite.rs`). type Row = sqlx::postgres::PgRow; @@ -102,100 +96,7 @@ async fn execute_records_error() { #[tokio::test] #[serial] async fn execute_records_affected_rows() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - sqlx::query( - "CREATE TABLE IF NOT EXISTS affected_test (id INT PRIMARY KEY, name TEXT NOT NULL)", - ) - .execute(&pool) - .await - .unwrap(); - sqlx::query("DELETE FROM affected_test") - .execute(&pool) - .await - .unwrap(); - - tel.reset(); - - // --- Bulk insert --- - sqlx::query( - "INSERT INTO affected_test (id, name) VALUES (1, 'alice'), (2, 'bob'), (3, 'carol')", - ) - .execute(&pool) - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_eq!( - attr(&spans[0], "db.response.affected_rows"), - Some(opentelemetry::Value::I64(3)), - "inserting 3 rows in one statement should affect 3 rows" - ); - tel.reset(); - - // --- Upsert (INSERT ON CONFLICT) --- - sqlx::query( - "INSERT INTO affected_test (id, name) VALUES (1, 'alice_updated') \ - ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name", - ) - .execute(&pool) - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_eq!( - attr(&spans[0], "db.response.affected_rows"), - Some(opentelemetry::Value::I64(1)), - "upsert should affect 1 row" - ); - tel.reset(); - - // --- Update multiple rows --- - sqlx::query("UPDATE affected_test SET name = name || '_updated' WHERE id IN (2, 3)") - .execute(&pool) - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_eq!( - attr(&spans[0], "db.response.affected_rows"), - Some(opentelemetry::Value::I64(2)), - "updating two rows should affect 2 rows" - ); - tel.reset(); - - // --- Delete multiple rows --- - sqlx::query("DELETE FROM affected_test WHERE id IN (1, 2, 3)") - .execute(&pool) - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_eq!( - attr(&spans[0], "db.response.affected_rows"), - Some(opentelemetry::Value::I64(3)), - "deleting three rows should affect 3 rows" - ); - tel.reset(); - - // --- Delete with no matching rows --- - sqlx::query("DELETE FROM affected_test WHERE id = 999") - .execute(&pool) - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_eq!( - attr(&spans[0], "db.response.affected_rows"), - Some(opentelemetry::Value::I64(0)), - "deleting non-existent rows should affect 0 rows" - ); + test_execute_records_affected_rows!(test_pool().await, common::POSTGRES_DIALECT); } // =========================================================================== @@ -366,32 +267,7 @@ async fn fetch_optional_records_one_row() { #[tokio::test] #[serial] async fn fetch_optional_records_zero_rows() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - sqlx::query("CREATE TABLE IF NOT EXISTS empty_table (id SERIAL PRIMARY KEY)") - .execute(&pool) - .await - .unwrap(); - sqlx::query("DELETE FROM empty_table") - .execute(&pool) - .await - .unwrap(); - - tel.reset(); - let result = (&pool) - .fetch_optional("SELECT id FROM empty_table") - .await - .unwrap(); - assert!(result.is_none()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(0)) - ); + test_fetch_optional_records_zero_rows!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] @@ -574,42 +450,7 @@ async fn sqlstate_recorded_on_constraint_violation() { #[tokio::test] #[serial] async fn operation_duration_metric_is_recorded() { - use opentelemetry_sdk::metrics::data::{AggregatedMetrics, MetricData}; - - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let _row = (&pool).fetch_one("SELECT 1").await.unwrap(); - - let resource_metrics = tel.metrics(); - assert!(!resource_metrics.is_empty(), "should have metric data"); - - let mut found_duration = false; - for rm in &resource_metrics { - for sm in rm.scope_metrics() { - for metric in sm.metrics() { - if metric.name() == "db.client.operation.duration" { - found_duration = true; - assert_eq!(metric.unit(), "s"); - if let AggregatedMetrics::F64(MetricData::Histogram(hist)) = metric.data() { - let dp: Vec<_> = hist.data_points().collect(); - assert!(!dp.is_empty(), "histogram should have data points"); - assert!(dp[0].count() > 0, "data point count should be > 0"); - let has_system = dp[0] - .attributes() - .any(|kv| kv.key.as_str() == "db.system.name"); - assert!(has_system, "metric should have db.system.name attribute"); - } else { - panic!("db.client.operation.duration should be an f64 histogram"); - } - } - } - } - } - assert!( - found_duration, - "db.client.operation.duration metric not found" - ); + test_operation_duration_metric_is_recorded!(test_pool().await, common::POSTGRES_DIALECT); } // =========================================================================== @@ -619,19 +460,7 @@ async fn operation_duration_metric_is_recorded() { #[tokio::test] #[serial] async fn transaction_rollback() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut tx: Transaction<'_, Postgres> = pool.begin().await.unwrap(); - sqlx::query("CREATE TABLE IF NOT EXISTS rollback_test (id SERIAL PRIMARY KEY)") - .execute(&mut tx) - .await - .unwrap(); - tx.rollback().await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); + test_transaction_rollback!(test_pool().await, common::POSTGRES_DIALECT); } // =========================================================================== @@ -675,12 +504,7 @@ async fn builder_with_network_peer_port() { #[tokio::test] #[serial] async fn pool_close_and_is_closed() { - let _tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - assert!(!pool.is_closed()); - pool.close().await; - assert!(pool.is_closed()); + test_pool_close_and_is_closed!(test_pool().await, common::POSTGRES_DIALECT); } // =========================================================================== @@ -690,50 +514,13 @@ async fn pool_close_and_is_closed() { #[tokio::test] #[serial] async fn query_text_mode_off_suppresses_sql() { - let shared = shared_container().await; - let raw = sqlx::PgPool::connect(&shared.url).await.unwrap(); - let pool = PoolBuilder::from(raw) - .with_query_text_mode(sqlx_otel::QueryTextMode::Off) - .build(); - - let tel = common::TestTelemetry::install(); - let _row = (&pool).fetch_optional("SELECT 1").await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_eq!(spans[0].span_kind, SpanKind::Client); - assert_eq!( - attr(&spans[0], "db.system.name"), - Some(opentelemetry::Value::String(SYSTEM.to_owned().into())) - ); - assert!(attr(&spans[0], "db.namespace").is_some()); - assert!( - attr(&spans[0], "db.query.text").is_none(), - "db.query.text should not be present when QueryTextMode::Off" - ); + test_query_text_mode_off_suppresses_sql!(raw_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] async fn query_text_mode_obfuscated_replaces_literals() { - let shared = shared_container().await; - let raw = sqlx::PgPool::connect(&shared.url).await.unwrap(); - let pool = PoolBuilder::from(raw) - .with_query_text_mode(sqlx_otel::QueryTextMode::Obfuscated) - .build(); - - let tel = common::TestTelemetry::install(); - let _row = (&pool) - .fetch_optional("SELECT 1, 'alice', 3.14") - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_eq!( - attr(&spans[0], "db.query.text"), - Some(opentelemetry::Value::String("SELECT ?, ?, ?".into())) - ); + test_query_text_mode_obfuscated_replaces_literals!(raw_pool().await, common::POSTGRES_DIALECT); } // =========================================================================== @@ -759,19 +546,7 @@ async fn query_summary_drives_span_name() { #[tokio::test] #[serial] async fn query_execute_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - sqlx::query("CREATE TABLE IF NOT EXISTS qe_pool (id SERIAL PRIMARY KEY)") - .with_annotations(test_annotations()) - .execute(&pool) - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); - assert!(attr(&spans[0], "db.response.affected_rows").is_some()); + test_query_execute_with_annotations_via_pool!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] @@ -807,126 +582,46 @@ async fn query_fetch_one_with_annotations_via_pool() { #[tokio::test] #[serial] async fn query_fetch_optional_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - sqlx::query("CREATE TABLE IF NOT EXISTS qfo_pool (id INT PRIMARY KEY)") - .execute(&pool) - .await - .unwrap(); - - tel.reset(); - - let row = sqlx::query("SELECT id FROM qfo_pool WHERE id = 1") - .with_annotations(test_annotations()) - .fetch_optional(&pool) - .await - .unwrap(); - assert!(row.is_none()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(0)) + test_query_fetch_optional_with_annotations_via_pool!( + test_pool().await, + common::POSTGRES_DIALECT ); } #[tokio::test] #[serial] async fn query_bind_first_then_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let row = sqlx::query("SELECT $1::int + $2::int AS sum") - .bind(2_i32) - .bind(3_i32) - .with_annotations(test_annotations()) - .fetch_one(&pool) - .await - .unwrap(); - let sum: i32 = row.try_get("sum").unwrap(); - assert_eq!(sum, 5); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); + test_query_bind_first_then_annotations_via_pool!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] async fn query_annotations_first_then_bind_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let row = sqlx::query("SELECT $1::int + $2::int AS sum") - .with_annotations(test_annotations()) - .bind(10_i32) - .bind(20_i32) - .fetch_one(&pool) - .await - .unwrap(); - let sum: i32 = row.try_get("sum").unwrap(); - assert_eq!(sum, 30); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); + test_query_annotations_first_then_bind_via_pool!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] async fn query_with_operation_shorthand_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - sqlx::query("CREATE TABLE IF NOT EXISTS qop_pool (id SERIAL PRIMARY KEY)") - .with_operation("SELECT", "users") - .execute(&pool) - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); + test_query_with_operation_shorthand_via_pool!(test_pool().await, common::POSTGRES_DIALECT); } #[tokio::test] #[serial] async fn query_execute_with_annotations_via_connection() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - sqlx::query("CREATE TABLE IF NOT EXISTS qe_conn (id SERIAL PRIMARY KEY)") - .with_annotations(test_annotations()) - .execute(&mut conn) - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); + test_query_execute_with_annotations_via_connection!( + test_pool().await, + common::POSTGRES_DIALECT + ); } #[tokio::test] #[serial] async fn query_execute_with_annotations_via_transaction() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut tx: Transaction<'_, Postgres> = pool.begin().await.unwrap(); - sqlx::query("CREATE TABLE IF NOT EXISTS qe_tx (id SERIAL PRIMARY KEY)") - .with_annotations(test_annotations()) - .execute(&mut tx) - .await - .unwrap(); - tx.commit().await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); + test_query_execute_with_annotations_via_transaction!( + test_pool().await, + common::POSTGRES_DIALECT + ); } #[tokio::test] @@ -1169,9 +864,7 @@ async fn query_macro_execute_with_annotations_via_pool() { .unwrap(); assert_eq!(result.rows_affected(), 1); - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); + common::assert_one_annotated_span(&tel, &common::POSTGRES_DIALECT); } #[tokio::test] @@ -1199,9 +892,7 @@ async fn query_macro_fetch_one_with_annotations_via_pool() { assert_eq!(row.id, 102); assert_eq!(row.name, "bob"); - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); + common::assert_one_annotated_span(&tel, &common::POSTGRES_DIALECT); } #[tokio::test] @@ -1232,9 +923,7 @@ async fn query_macro_fetch_all_with_annotations_via_pool() { .unwrap(); assert_eq!(rows.len(), 3); - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); + common::assert_one_annotated_span(&tel, &common::POSTGRES_DIALECT); } #[tokio::test] @@ -1255,9 +944,7 @@ async fn query_macro_fetch_optional_with_annotations_via_pool() { .unwrap(); assert!(row.is_none()); - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); + common::assert_one_annotated_span(&tel, &common::POSTGRES_DIALECT); } type MacroUser = common::MacroUser; @@ -1291,9 +978,7 @@ async fn query_as_macro_fetch_one_with_annotations_via_pool() { assert_eq!(user.id, 106); assert_eq!(user.name, "frank"); - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); + common::assert_one_annotated_span(&tel, &common::POSTGRES_DIALECT); } #[tokio::test] @@ -1323,9 +1008,7 @@ async fn query_as_macro_fetch_all_with_annotations_via_pool() { .unwrap(); assert_eq!(users.len(), 2); - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); + common::assert_one_annotated_span(&tel, &common::POSTGRES_DIALECT); } #[tokio::test] @@ -1350,9 +1033,7 @@ async fn query_as_macro_fetch_optional_with_annotations_via_pool() { .unwrap(); assert!(user.is_none()); - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); + common::assert_one_annotated_span(&tel, &common::POSTGRES_DIALECT); } #[tokio::test] @@ -1379,9 +1060,7 @@ async fn query_scalar_macro_fetch_one_with_annotations_via_pool() { .unwrap(); assert_eq!(name, "irene"); - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); + common::assert_one_annotated_span(&tel, &common::POSTGRES_DIALECT); } #[tokio::test] @@ -1410,7 +1089,5 @@ async fn query_scalar_macro_fetch_all_with_annotations_via_pool() { .unwrap(); assert_eq!(ids, vec![110, 111]); - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::POSTGRES_DIALECT); + common::assert_one_annotated_span(&tel, &common::POSTGRES_DIALECT); } diff --git a/tests/sqlite.rs b/tests/sqlite.rs index 47de285..6af5cfa 100644 --- a/tests/sqlite.rs +++ b/tests/sqlite.rs @@ -2,13 +2,12 @@ mod common; -use common::{assert_annotated_span, assert_common_span_attributes, attr, test_annotations}; +use common::test_annotations; use futures::StreamExt as _; use serial_test::serial; use sqlx::Executor as _; -use sqlx::Row as _; use sqlx::Sqlite; -use sqlx_otel::{Pool, PoolBuilder, QueryAnnotateExt, Transaction}; +use sqlx_otel::{Pool, PoolBuilder, QueryAnnotateExt}; const SYSTEM: &str = "sqlite"; @@ -53,90 +52,7 @@ async fn execute_creates_span_via_transaction() { #[tokio::test] #[serial] async fn execute_records_affected_rows() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - sqlx::query("CREATE TABLE affected_test (id INTEGER PRIMARY KEY, name TEXT NOT NULL)") - .execute(&pool) - .await - .unwrap(); - tel.reset(); - - // --- Bulk insert via VALUES list --- - sqlx::query( - "INSERT INTO affected_test (id, name) VALUES (1, 'alice'), (2, 'bob'), (3, 'carol')", - ) - .execute(&pool) - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_eq!( - attr(&spans[0], "db.response.affected_rows"), - Some(opentelemetry::Value::I64(3)), - "inserting 3 rows in one statement should affect 3 rows" - ); - tel.reset(); - - // --- Upsert (INSERT OR REPLACE) --- - sqlx::query("INSERT OR REPLACE INTO affected_test (id, name) VALUES (1, 'alice_updated')") - .execute(&pool) - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_eq!( - attr(&spans[0], "db.response.affected_rows"), - Some(opentelemetry::Value::I64(1)), - "upsert should affect 1 row" - ); - tel.reset(); - - // --- Update multiple rows --- - sqlx::query("UPDATE affected_test SET name = name || '_updated' WHERE id IN (2, 3)") - .execute(&pool) - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_eq!( - attr(&spans[0], "db.response.affected_rows"), - Some(opentelemetry::Value::I64(2)), - "updating two rows should affect 2 rows" - ); - tel.reset(); - - // --- Delete multiple rows --- - sqlx::query("DELETE FROM affected_test WHERE id IN (1, 2, 3)") - .execute(&pool) - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_eq!( - attr(&spans[0], "db.response.affected_rows"), - Some(opentelemetry::Value::I64(3)), - "deleting three rows should affect 3 rows" - ); - tel.reset(); - - // --- Delete with no matching rows --- - sqlx::query("DELETE FROM affected_test WHERE id = 999") - .execute(&pool) - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_eq!( - attr(&spans[0], "db.response.affected_rows"), - Some(opentelemetry::Value::I64(0)), - "deleting non-existent rows should affect 0 rows" - ); + test_execute_records_affected_rows!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] @@ -310,31 +226,7 @@ async fn fetch_optional_records_one_row() { #[tokio::test] #[serial] async fn fetch_optional_records_zero_rows() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - sqlx::query("CREATE TABLE empty_table (id INTEGER PRIMARY KEY)") - .execute(&pool) - .await - .unwrap(); - - let result = (&pool) - .fetch_optional("SELECT id FROM empty_table") - .await - .unwrap(); - assert!(result.is_none()); - - let spans = tel.spans(); - let select_span = spans - .iter() - .find(|s| attr(s, "db.query.text").is_some_and(|v| v.to_string().contains("SELECT"))); - assert!(select_span.is_some()); - let select_span = select_span.unwrap(); - assert_common_span_attributes(select_span, SYSTEM); - assert_eq!( - attr(select_span, "db.response.returned_rows"), - Some(opentelemetry::Value::I64(0)) - ); + test_fetch_optional_records_zero_rows!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] @@ -446,42 +338,7 @@ async fn describe_records_error() { #[tokio::test] #[serial] async fn operation_duration_metric_is_recorded() { - use opentelemetry_sdk::metrics::data::{AggregatedMetrics, MetricData}; - - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let _: (i32,) = sqlx::query_as("SELECT 1").fetch_one(&pool).await.unwrap(); - - let resource_metrics = tel.metrics(); - assert!(!resource_metrics.is_empty(), "should have metric data"); - - let mut found_duration = false; - for rm in &resource_metrics { - for sm in rm.scope_metrics() { - for metric in sm.metrics() { - if metric.name() == "db.client.operation.duration" { - found_duration = true; - assert_eq!(metric.unit(), "s"); - if let AggregatedMetrics::F64(MetricData::Histogram(hist)) = metric.data() { - let dp: Vec<_> = hist.data_points().collect(); - assert!(!dp.is_empty(), "histogram should have data points"); - assert!(dp[0].count() > 0, "data point count should be > 0"); - let has_system = dp[0] - .attributes() - .any(|kv| kv.key.as_str() == "db.system.name"); - assert!(has_system, "metric should have db.system.name attribute"); - } else { - panic!("db.client.operation.duration should be an f64 histogram"); - } - } - } - } - } - assert!( - found_duration, - "db.client.operation.duration metric not found" - ); + test_operation_duration_metric_is_recorded!(test_pool().await, common::SQLITE_DIALECT); } // =========================================================================== @@ -497,29 +354,7 @@ async fn query_text_mode_off_suppresses_sql() { #[tokio::test] #[serial] async fn query_text_mode_obfuscated_replaces_literals() { - let tel = common::TestTelemetry::install(); - let raw = sqlx::SqlitePool::connect(":memory:").await.unwrap(); - let pool = PoolBuilder::from(raw) - .with_query_text_mode(sqlx_otel::QueryTextMode::Obfuscated) - .build(); - - sqlx::query("CREATE TABLE t (id INTEGER, name TEXT)") - .execute(&pool) - .await - .unwrap(); - sqlx::query("INSERT INTO t (id, name) VALUES (1, 'alice')") - .execute(&pool) - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 2); - assert_eq!( - attr(&spans[1], "db.query.text"), - Some(opentelemetry::Value::String( - "INSERT INTO t (id, name) VALUES (?, ?)".into() - )) - ); + test_query_text_mode_obfuscated_replaces_literals!(raw_pool().await, common::SQLITE_DIALECT); } // =========================================================================== @@ -529,19 +364,7 @@ async fn query_text_mode_obfuscated_replaces_literals() { #[tokio::test] #[serial] async fn transaction_rollback() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut tx: Transaction<'_, Sqlite> = pool.begin().await.unwrap(); - sqlx::query("CREATE TABLE rollback_test (id INTEGER PRIMARY KEY)") - .execute(&mut tx) - .await - .unwrap(); - tx.rollback().await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_common_span_attributes(&spans[0], SYSTEM); + test_transaction_rollback!(test_pool().await, common::SQLITE_DIALECT); } // =========================================================================== @@ -585,12 +408,7 @@ async fn builder_with_network_peer_port() { #[tokio::test] #[serial] async fn pool_close_and_is_closed() { - let _tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - assert!(!pool.is_closed()); - pool.close().await; - assert!(pool.is_closed()); + test_pool_close_and_is_closed!(test_pool().await, common::SQLITE_DIALECT); } // =========================================================================== @@ -616,19 +434,7 @@ async fn query_summary_drives_span_name() { #[tokio::test] #[serial] async fn query_execute_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - sqlx::query("CREATE TABLE qe_pool (id INTEGER PRIMARY KEY)") - .with_annotations(test_annotations()) - .execute(&pool) - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); - assert!(attr(&spans[0], "db.response.affected_rows").is_some()); + test_query_execute_with_annotations_via_pool!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] @@ -664,126 +470,37 @@ async fn query_fetch_one_with_annotations_via_pool() { #[tokio::test] #[serial] async fn query_fetch_optional_with_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - sqlx::query("CREATE TABLE qfo_pool (id INTEGER PRIMARY KEY)") - .execute(&pool) - .await - .unwrap(); - - tel.reset(); - - let row = sqlx::query("SELECT id FROM qfo_pool WHERE id = 1") - .with_annotations(test_annotations()) - .fetch_optional(&pool) - .await - .unwrap(); - assert!(row.is_none()); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); - assert_eq!( - attr(&spans[0], "db.response.returned_rows"), - Some(opentelemetry::Value::I64(0)) - ); + test_query_fetch_optional_with_annotations_via_pool!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn query_bind_first_then_annotations_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let row = sqlx::query("SELECT ?1 + ?2 AS sum") - .bind(2_i32) - .bind(3_i32) - .with_annotations(test_annotations()) - .fetch_one(&pool) - .await - .unwrap(); - let sum: i32 = row.try_get("sum").unwrap(); - assert_eq!(sum, 5); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); + test_query_bind_first_then_annotations_via_pool!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn query_annotations_first_then_bind_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let row = sqlx::query("SELECT ?1 + ?2 AS sum") - .with_annotations(test_annotations()) - .bind(10_i32) - .bind(20_i32) - .fetch_one(&pool) - .await - .unwrap(); - let sum: i32 = row.try_get("sum").unwrap(); - assert_eq!(sum, 30); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); + test_query_annotations_first_then_bind_via_pool!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn query_with_operation_shorthand_via_pool() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - sqlx::query("CREATE TABLE qop_pool (id INTEGER PRIMARY KEY)") - .with_operation("SELECT", "users") - .execute(&pool) - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); + test_query_with_operation_shorthand_via_pool!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn query_execute_with_annotations_via_connection() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut conn = pool.acquire().await.unwrap(); - sqlx::query("CREATE TABLE qe_conn (id INTEGER PRIMARY KEY)") - .with_annotations(test_annotations()) - .execute(&mut conn) - .await - .unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); + test_query_execute_with_annotations_via_connection!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] #[serial] async fn query_execute_with_annotations_via_transaction() { - let tel = common::TestTelemetry::install(); - let pool = test_pool().await; - - let mut tx: Transaction<'_, Sqlite> = pool.begin().await.unwrap(); - sqlx::query("CREATE TABLE qe_tx (id INTEGER PRIMARY KEY)") - .with_annotations(test_annotations()) - .execute(&mut tx) - .await - .unwrap(); - tx.commit().await.unwrap(); - - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); + test_query_execute_with_annotations_via_transaction!(test_pool().await, common::SQLITE_DIALECT); } #[tokio::test] @@ -1022,9 +739,7 @@ async fn query_macro_execute_with_annotations_via_pool() { .unwrap(); assert_eq!(result.rows_affected(), 1); - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); + common::assert_one_annotated_span(&tel, &common::SQLITE_DIALECT); } #[tokio::test] @@ -1050,9 +765,7 @@ async fn query_macro_fetch_one_with_annotations_via_pool() { assert_eq!(row.id, 2); assert_eq!(row.name, "bob"); - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); + common::assert_one_annotated_span(&tel, &common::SQLITE_DIALECT); } #[tokio::test] @@ -1081,9 +794,7 @@ async fn query_macro_fetch_all_with_annotations_via_pool() { .unwrap(); assert_eq!(rows.len(), 3); - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); + common::assert_one_annotated_span(&tel, &common::SQLITE_DIALECT); } #[tokio::test] @@ -1104,9 +815,7 @@ async fn query_macro_fetch_optional_with_annotations_via_pool() { .unwrap(); assert!(row.is_none()); - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); + common::assert_one_annotated_span(&tel, &common::SQLITE_DIALECT); } type MacroUser = common::MacroUser; @@ -1138,9 +847,7 @@ async fn query_as_macro_fetch_one_with_annotations_via_pool() { assert_eq!(user.id, 6); assert_eq!(user.name, "frank"); - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); + common::assert_one_annotated_span(&tel, &common::SQLITE_DIALECT); } #[tokio::test] @@ -1170,9 +877,7 @@ async fn query_as_macro_fetch_all_with_annotations_via_pool() { .unwrap(); assert_eq!(users.len(), 2); - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); + common::assert_one_annotated_span(&tel, &common::SQLITE_DIALECT); } #[tokio::test] @@ -1197,9 +902,7 @@ async fn query_as_macro_fetch_optional_with_annotations_via_pool() { .unwrap(); assert!(user.is_none()); - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); + common::assert_one_annotated_span(&tel, &common::SQLITE_DIALECT); } #[tokio::test] @@ -1224,9 +927,7 @@ async fn query_scalar_macro_fetch_one_with_annotations_via_pool() { .unwrap(); assert_eq!(name, "irene"); - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); + common::assert_one_annotated_span(&tel, &common::SQLITE_DIALECT); } #[tokio::test] @@ -1255,9 +956,7 @@ async fn query_scalar_macro_fetch_all_with_annotations_via_pool() { .unwrap(); assert_eq!(ids, vec![10, 11]); - let spans = tel.spans(); - assert_eq!(spans.len(), 1); - assert_annotated_span(&spans[0], &common::SQLITE_DIALECT); + common::assert_one_annotated_span(&tel, &common::SQLITE_DIALECT); } // =========================================================================== From 87170876918166b25a62fea46ecd0364ff76c832 Mon Sep 17 00:00:00 2001 From: Borislav Borisov Date: Tue, 28 Apr 2026 12:08:25 +0100 Subject: [PATCH 4/4] fix: Stop the shared test container at process exit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit testcontainers-rs 0.27 has no Ryuk reaper – it relies on `ContainerAsync::Drop`, which the language never runs for values held in a `'static` (the `OnceLock>` introduced in c29f995). The result was that postgres and mysql containers leaked after every test run. Capture the container ID into a sibling `static CONTAINER_ID: OnceLock` at start-time and add a `#[ctor::dtor]`-registered function that shells out `docker rm -f ` synchronously when the test process exits. Restores the cleanup invariant of the per-test container pattern that existed before c29f995, without giving up the shared-container speedup. --- Cargo.toml | 1 + tests/common.rs | 6 +++--- tests/mysql.rs | 24 ++++++++++++++++++++++++ tests/postgres.rs | 24 ++++++++++++++++++++++++ 4 files changed, 52 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 621cde9..fc2713b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,6 +42,7 @@ all-features = true rustdoc-args = ["--cfg", "docsrs"] [dev-dependencies] +ctor = "0.4" opentelemetry_sdk = { version = "0.31", features = ["testing", "rt-tokio"] } proptest = "1" serial_test = "3" diff --git a/tests/common.rs b/tests/common.rs index c66c480..28cc982 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -182,7 +182,7 @@ pub fn assert_error_span(span: &SpanData) { // PoolConnection`, `&mut Transaction<'_, DB>`, and the matching `Annotated` / // `AnnotatedMut` wrappers, each gated by `for<'a> &'a mut DB::Connection: Executor<'a, // Database = DB>`. Test bodies generic over `DB` (with that HRTB declared) trigger -// trait-resolution overflow on stable rustc — the compiler tries to satisfy the bound +// trait-resolution overflow on stable rustc – the compiler tries to satisfy the bound // against multiple wrapper impls and recurses. Bumping `recursion_limit` does not // help; the chain genuinely diverges. // @@ -1895,7 +1895,7 @@ macro_rules! test_describe_records_error { macro_rules! test_operation_duration_metric_is_recorded { ($pool_factory:expr, $dialect:expr) => {{ use opentelemetry_sdk::metrics::data::{AggregatedMetrics, MetricData}; - let _ = $dialect; // unused — backend doesn't influence the metric shape + let _ = $dialect; // unused – backend doesn't influence the metric shape let tel = $crate::common::TestTelemetry::install(); let pool = $pool_factory; @@ -2442,7 +2442,7 @@ macro_rules! test_query_map_position_2_via_pool { }}; } -/// `Query::with_annotations` after `bind` and `map` — last in the pipeline. +/// `Query::with_annotations` after `bind` and `map` – last in the pipeline. #[macro_export] macro_rules! test_query_map_position_3_via_pool { ($pool_factory:expr, $dialect:expr) => {{ diff --git a/tests/mysql.rs b/tests/mysql.rs index e730219..e4cb0ac 100644 --- a/tests/mysql.rs +++ b/tests/mysql.rs @@ -26,6 +26,12 @@ struct SharedContainer { static CONTAINER: OnceLock> = OnceLock::new(); +/// Container ID captured at start-time for use by the [`drop_container`] destructor. +/// Held in a sibling static (rather than reading from `SharedContainer` at exit) +/// because the `ContainerAsync` value is itself locked behind a `'static` future and +/// the destructor must run synchronously without async access. +static CONTAINER_ID: OnceLock = OnceLock::new(); + async fn shared_container() -> &'static SharedContainer { CONTAINER .get_or_init(OnceCell::new) @@ -43,6 +49,8 @@ async fn shared_container() -> &'static SharedContainer { .await .expect("starting mysql container"); + let _ = CONTAINER_ID.set(container.id().to_string()); + let port = container.get_host_port_ipv4(3306).await.unwrap(); let url = format!("mysql://root:test@localhost:{port}/testdb"); SharedContainer { @@ -53,6 +61,22 @@ async fn shared_container() -> &'static SharedContainer { .await } +/// Stop and remove the shared container at process exit. Required because the +/// `ContainerAsync` value lives in a `'static` (`CONTAINER`), so the language never +/// runs its `Drop`. Using `ctor::dtor` schedules a synchronous shell-out to +/// `docker rm -f` that fires after `main` returns – equivalent to the per-test +/// RAII cleanup that existed before the shared-container refactor (commit c29f995). +#[ctor::dtor] +fn drop_container() { + if let Some(id) = CONTAINER_ID.get() { + let _ = std::process::Command::new("docker") + .args(["rm", "-f", id.as_str()]) + .stdout(std::process::Stdio::null()) + .stderr(std::process::Stdio::null()) + .status(); + } +} + /// Return an instrumented pool connected to the shared container. async fn test_pool() -> Pool { PoolBuilder::from(raw_pool().await).build() diff --git a/tests/postgres.rs b/tests/postgres.rs index 38634e5..d382d9d 100644 --- a/tests/postgres.rs +++ b/tests/postgres.rs @@ -26,6 +26,12 @@ struct SharedContainer { static CONTAINER: OnceLock> = OnceLock::new(); +/// Container ID captured at start-time for use by the [`drop_container`] destructor. +/// Held in a sibling static (rather than reading from `SharedContainer` at exit) +/// because the `ContainerAsync` value is itself locked behind a `'static` future and +/// the destructor must run synchronously without async access. +static CONTAINER_ID: OnceLock = OnceLock::new(); + async fn shared_container() -> &'static SharedContainer { CONTAINER .get_or_init(OnceCell::new) @@ -43,6 +49,8 @@ async fn shared_container() -> &'static SharedContainer { .await .expect("starting postgres container"); + let _ = CONTAINER_ID.set(container.id().to_string()); + let port = container.get_host_port_ipv4(5432).await.unwrap(); let url = format!("postgres://postgres@localhost:{port}/testdb"); SharedContainer { @@ -53,6 +61,22 @@ async fn shared_container() -> &'static SharedContainer { .await } +/// Stop and remove the shared container at process exit. Required because the +/// `ContainerAsync` value lives in a `'static` (`CONTAINER`), so the language never +/// runs its `Drop`. Using `ctor::dtor` schedules a synchronous shell-out to +/// `docker rm -f` that fires after `main` returns – equivalent to the per-test +/// RAII cleanup that existed before the shared-container refactor (commit c29f995). +#[ctor::dtor] +fn drop_container() { + if let Some(id) = CONTAINER_ID.get() { + let _ = std::process::Command::new("docker") + .args(["rm", "-f", id.as_str()]) + .stdout(std::process::Stdio::null()) + .stderr(std::process::Stdio::null()) + .status(); + } +} + /// Return an instrumented pool connected to the shared container. async fn test_pool() -> Pool { PoolBuilder::from(raw_pool().await).build()