From 19c97dfa611c4d70c69b74dac10c262507f3fe98 Mon Sep 17 00:00:00 2001 From: Reinaldy Rafli Date: Sat, 19 Oct 2024 10:31:58 +0700 Subject: [PATCH] chore: avoid cyclo errors --- sentrysql/sentrysql_test.go | 1283 +++++++++++++++++++---------------- 1 file changed, 684 insertions(+), 599 deletions(-) diff --git a/sentrysql/sentrysql_test.go b/sentrysql/sentrysql_test.go index 4ff51735..ac939a9c 100644 --- a/sentrysql/sentrysql_test.go +++ b/sentrysql/sentrysql_test.go @@ -4,6 +4,7 @@ import ( "context" "database/sql" "fmt" + "os" "strings" "testing" "time" @@ -65,10 +66,22 @@ func ExampleNewSentrySQLConnector() { // Continue executing PostgreSQL queries } -//nolint:dupl -func TestNewSentrySQL_Integration(t *testing.T) { +var optstrans = cmp.Options{ + cmpopts.IgnoreFields( + sentry.Span{}, + "TraceID", "SpanID", "ParentSpanID", "StartTime", "EndTime", + "mu", "parent", "sampleRate", "ctx", "dynamicSamplingContext", "recorder", "finishOnce", "collectProfile", "contexts", + ), +} + +func TestMain(m *testing.M) { sql.Register("sentrysql-sqlite", sentrysql.NewSentrySQL(&sqlite.Driver{}, sentrysql.WithDatabaseName("memory"), sentrysql.WithDatabaseSystem(sentrysql.DatabaseSystem("sqlite")), sentrysql.WithServerAddress("localhost", "5432"))) + os.Exit(m.Run()) +} + +//nolint:dupl +func TestNewSentrySQL_Integration(t *testing.T) { db, err := sql.Open("sentrysql-sqlite", ":memory:") if err != nil { t.Fatalf("opening sqlite: %v", err) @@ -94,14 +107,6 @@ func TestNewSentrySQL_Integration(t *testing.T) { } } - optstrans := cmp.Options{ - cmpopts.IgnoreFields( - sentry.Span{}, - "TraceID", "SpanID", "ParentSpanID", "StartTime", "EndTime", - "mu", "parent", "sampleRate", "ctx", "dynamicSamplingContext", "recorder", "finishOnce", "collectProfile", "contexts", - ), - } - t.Run("QueryContext", func(t *testing.T) { tests := []struct { Query string @@ -361,703 +366,713 @@ func TestNewSentrySQL_Integration(t *testing.T) { } }) - t.Run("PrepareContext", func(t *testing.T) { - t.Run("Exec", func(t *testing.T) { - tests := []struct { - Query string - Parameters []interface{} - WantSpan *sentry.Span - WantError bool - }{ - { - Query: "INSERT INTO exec_test (id, name) VALUES (?, ?)", - Parameters: []interface{}{3, "Sarah"}, - WantSpan: &sentry.Span{ - Data: map[string]interface{}{ - "db.system": sentrysql.DatabaseSystem("sqlite"), - "db.name": "memory", - "server.address": "localhost", - "server.port": "5432", - "db.operation": "INSERT", - }, - Description: "INSERT INTO exec_test (id, name) VALUES (?, ?)", - Op: "db.sql.exec", - Tags: nil, - Origin: "manual", - Sampled: sentry.SampledTrue, - Status: sentry.SpanStatusOK, - }, - }, - { - Query: "INSERT INTO exec_test (id, name) VALUES (?, ?, ?, ?)", - Parameters: []interface{}{4, "John", "Doe", "John Doe"}, - WantError: true, - WantSpan: &sentry.Span{ - Data: map[string]interface{}{ - "db.system": sentrysql.DatabaseSystem("sqlite"), - "db.name": "memory", - "server.address": "localhost", - "server.port": "5432", - "db.operation": "INSERT", - }, - Description: "INSERT INTO exec_test (id, name) VALUES (?, ?, ?, ?)", - Op: "db.sql.exec", - Tags: nil, - Origin: "manual", - Sampled: sentry.SampledTrue, - Status: sentry.SpanStatusInternalError, - }, - }, - } - - spansCh := make(chan []*sentry.Span, len(tests)) - - sentryClient, err := sentry.NewClient(sentry.ClientOptions{ - EnableTracing: true, - TracesSampleRate: 1.0, - BeforeSendTransaction: func(event *sentry.Event, hint *sentry.EventHint) *sentry.Event { - spansCh <- event.Spans - return event - }, - }) - if err != nil { - t.Fatal(err) - } - - for _, tt := range tests { - hub := sentry.NewHub(sentryClient, sentry.NewScope()) - ctx, cancel := context.WithTimeout(sentry.SetHubOnContext(context.Background(), hub), 10*time.Second) - span := sentry.StartSpan(ctx, "fake_parent", sentry.WithTransactionName("Fake Parent")) - ctx = span.Context() - - stmt, err := db.PrepareContext(ctx, tt.Query) - if err != nil { - cancel() - t.Fatal(err) - } - - _, err = stmt.Exec(tt.Parameters...) - if err != nil && !tt.WantError { - cancel() - t.Fatal(err) - } + t.Run("Ping", func(t *testing.T) { + // Just checking if this works and doesn't panic + err := db.Ping() + if err != nil { + t.Fatal(err) + } + }) - span.Finish() - cancel() - } + t.Run("PingContext", func(t *testing.T) { + // Just checking if this works and doesn't panic + err := db.PingContext(context.Background()) + if err != nil { + t.Fatal(err) + } + }) +} - if ok := sentryClient.Flush(testutils.FlushTimeout()); !ok { - t.Fatal("sentry.Flush timed out") - } - close(spansCh) +//nolint:dupl +func TestNewSentrySQL_Conn(t *testing.T) { + db, err := sql.Open("sentrysql-sqlite", ":memory:") + if err != nil { + t.Fatalf("opening sqlite: %v", err) + } + db.SetMaxOpenConns(1) + defer db.Close() - var got [][]*sentry.Span - for e := range spansCh { - got = append(got, e) - } + setupQueries := []string{ + "CREATE TABLE exec_test (id INT, name TEXT)", + "CREATE TABLE query_test (id INT, name TEXT, age INT, created_at TEXT)", + "INSERT INTO query_test (id, name, age, created_at) VALUES (1, 'John', 30, '2023-01-01')", + "INSERT INTO query_test (id, name, age, created_at) VALUES (2, 'Jane', 25, '2023-01-02')", + "INSERT INTO query_test (id, name, age, created_at) VALUES (3, 'Bob', 35, '2023-01-03')", + } - for i, tt := range tests { - var foundMatch = false - gotSpans := got[i] - - var diffs []string - for _, gotSpan := range gotSpans { - if diff := cmp.Diff(tt.WantSpan, gotSpan, optstrans); diff != "" { - diffs = append(diffs, diff) - } else { - foundMatch = true - break - } - } + setupCtx, cancelCtx := context.WithTimeout(context.Background(), 30*time.Second) + defer cancelCtx() - if !foundMatch { - t.Errorf("Span mismatch (-want +got):\n%s", strings.Join(diffs, "\n")) - } - } - }) + for _, query := range setupQueries { + _, err = db.ExecContext(setupCtx, query) + if err != nil { + t.Fatalf("initializing table on sqlite: %v", err) + } + } - t.Run("Query", func(t *testing.T) { - tests := []struct { - Query string - Parameters []interface{} - WantSpan *sentry.Span - WantError bool - }{ - { - Query: "SELECT * FROM query_test WHERE id = ?", - Parameters: []interface{}{2}, - WantSpan: &sentry.Span{ - Data: map[string]interface{}{ - "db.system": sentrysql.DatabaseSystem("sqlite"), - "db.name": "memory", - "server.address": "localhost", - "server.port": "5432", - "db.operation": "SELECT", - }, - Description: "SELECT * FROM query_test WHERE id = ?", - Op: "db.sql.query", - Tags: nil, - Origin: "manual", - Sampled: sentry.SampledTrue, - Status: sentry.SpanStatusOK, + t.Run("QueryContext", func(t *testing.T) { + tests := []struct { + Query string + Parameters []interface{} + WantSpan *sentry.Span + WantError bool + }{ + { + Query: "SELECT * FROM query_test WHERE id = ?", + Parameters: []interface{}{1}, + WantSpan: &sentry.Span{ + Data: map[string]interface{}{ + "db.system": sentrysql.DatabaseSystem("sqlite"), + "db.name": "memory", + "server.address": "localhost", + "server.port": "5432", + "db.operation": "SELECT", }, + Description: "SELECT * FROM query_test WHERE id = ?", + Op: "db.sql.query", + Tags: nil, + Origin: "manual", + Sampled: sentry.SampledTrue, + Status: sentry.SpanStatusOK, }, - { - Query: "SELECT * FROM query_test WHERE id =", - Parameters: []interface{}{1}, - WantError: true, - WantSpan: &sentry.Span{ - Data: map[string]interface{}{ - "db.system": sentrysql.DatabaseSystem("sqlite"), - "db.name": "memory", - "server.address": "localhost", - "server.port": "5432", - "db.operation": "SELECT", - }, - Description: "SELECT * FROM query_test WHERE id =", - Op: "db.sql.query", - Tags: nil, - Origin: "manual", - Sampled: sentry.SampledTrue, - Status: sentry.SpanStatusInternalError, + }, + { + Query: "SELECT FROM query_test", + Parameters: []interface{}{1}, + WantError: true, + WantSpan: &sentry.Span{ + Data: map[string]interface{}{ + "db.system": sentrysql.DatabaseSystem("sqlite"), + "db.name": "memory", + "server.address": "localhost", + "server.port": "5432", + "db.operation": "SELECT", }, + Description: "SELECT FROM query_test", + Op: "db.sql.query", + Tags: nil, + Origin: "manual", + Sampled: sentry.SampledTrue, + Status: sentry.SpanStatusInternalError, }, - } + }, + } - spansCh := make(chan []*sentry.Span, len(tests)) + spansCh := make(chan []*sentry.Span, len(tests)) - sentryClient, err := sentry.NewClient(sentry.ClientOptions{ - EnableTracing: true, - TracesSampleRate: 1.0, - BeforeSendTransaction: func(event *sentry.Event, hint *sentry.EventHint) *sentry.Event { - spansCh <- event.Spans - return event - }, - }) + sentryClient, err := sentry.NewClient(sentry.ClientOptions{ + EnableTracing: true, + TracesSampleRate: 1.0, + BeforeSendTransaction: func(event *sentry.Event, hint *sentry.EventHint) *sentry.Event { + spansCh <- event.Spans + return event + }, + }) + if err != nil { + t.Fatal(err) + } + + for _, tt := range tests { + hub := sentry.NewHub(sentryClient, sentry.NewScope()) + ctx, cancel := context.WithTimeout(sentry.SetHubOnContext(context.Background(), hub), 10*time.Second) + span := sentry.StartSpan(ctx, "fake_parent", sentry.WithTransactionName("Fake Parent")) + ctx = span.Context() + + conn, err := db.Conn(ctx) if err != nil { + cancel() t.Fatal(err) } - for _, tt := range tests { - hub := sentry.NewHub(sentryClient, sentry.NewScope()) - ctx, cancel := context.WithTimeout(sentry.SetHubOnContext(context.Background(), hub), 10*time.Second) - span := sentry.StartSpan(ctx, "fake_parent", sentry.WithTransactionName("Fake Parent")) - ctx = span.Context() + rows, err := conn.QueryContext(ctx, tt.Query, tt.Parameters...) + if err != nil && !tt.WantError { + _ = conn.Close() + cancel() + t.Fatal(err) + } - stmt, err := db.PrepareContext(ctx, tt.Query) - if err != nil { - cancel() - t.Fatal(err) - } + if rows != nil { + _ = rows.Close() + } - rows, err := stmt.Query(tt.Parameters...) - if err != nil && !tt.WantError { - cancel() - t.Fatal(err) - } + _ = conn.Close() - if rows != nil { - _ = rows.Close() - } + span.Finish() + cancel() + } - span.Finish() - cancel() - } + if ok := sentryClient.Flush(testutils.FlushTimeout()); !ok { + t.Fatal("sentry.Flush timed out") + } + close(spansCh) - if ok := sentryClient.Flush(testutils.FlushTimeout()); !ok { - t.Fatal("sentry.Flush timed out") - } - close(spansCh) + var got [][]*sentry.Span + for e := range spansCh { + got = append(got, e) + } - var got [][]*sentry.Span - for e := range spansCh { - got = append(got, e) - } + for i, tt := range tests { + var foundMatch = false + gotSpans := got[i] - for i, tt := range tests { - var foundMatch = false - gotSpans := got[i] - - var diffs []string - for _, gotSpan := range gotSpans { - if diff := cmp.Diff(tt.WantSpan, gotSpan, optstrans); diff != "" { - diffs = append(diffs, diff) - } else { - foundMatch = true - break - } + var diffs []string + for _, gotSpan := range gotSpans { + if diff := cmp.Diff(tt.WantSpan, gotSpan, optstrans); diff != "" { + diffs = append(diffs, diff) + } else { + foundMatch = true + break } + } - if !foundMatch { - t.Errorf("Span mismatch (-want +got):\n%s", strings.Join(diffs, "\n")) - } + if !foundMatch { + t.Errorf("Span mismatch (-want +got):\n%s", strings.Join(diffs, "\n")) } - }) + } }) - t.Run("Conn", func(t *testing.T) { - t.Run("QueryContext", func(t *testing.T) { - tests := []struct { - Query string - Parameters []interface{} - WantSpan *sentry.Span - WantError bool - }{ - { - Query: "SELECT * FROM query_test WHERE id = ?", - Parameters: []interface{}{1}, - WantSpan: &sentry.Span{ - Data: map[string]interface{}{ - "db.system": sentrysql.DatabaseSystem("sqlite"), - "db.name": "memory", - "server.address": "localhost", - "server.port": "5432", - "db.operation": "SELECT", - }, - Description: "SELECT * FROM query_test WHERE id = ?", - Op: "db.sql.query", - Tags: nil, - Origin: "manual", - Sampled: sentry.SampledTrue, - Status: sentry.SpanStatusOK, + t.Run("ExecContext", func(t *testing.T) { + tests := []struct { + Query string + Parameters []interface{} + WantSpan *sentry.Span + WantError bool + }{ + { + Query: "INSERT INTO exec_test (id, name) VALUES (?, ?)", + Parameters: []interface{}{2, "Peter"}, + WantSpan: &sentry.Span{ + Data: map[string]interface{}{ + "db.system": sentrysql.DatabaseSystem("sqlite"), + "db.name": "memory", + "server.address": "localhost", + "server.port": "5432", + "db.operation": "INSERT", }, + Description: "INSERT INTO exec_test (id, name) VALUES (?, ?)", + Op: "db.sql.exec", + Tags: nil, + Origin: "manual", + Sampled: sentry.SampledTrue, + Status: sentry.SpanStatusOK, }, - { - Query: "SELECT FROM query_test", - Parameters: []interface{}{1}, - WantError: true, - WantSpan: &sentry.Span{ - Data: map[string]interface{}{ - "db.system": sentrysql.DatabaseSystem("sqlite"), - "db.name": "memory", - "server.address": "localhost", - "server.port": "5432", - "db.operation": "SELECT", - }, - Description: "SELECT FROM query_test", - Op: "db.sql.query", - Tags: nil, - Origin: "manual", - Sampled: sentry.SampledTrue, - Status: sentry.SpanStatusInternalError, + }, + { + Query: "INSERT INTO exec_test (id, name) VALUES (?, ?, ?, ?)", + Parameters: []interface{}{4, "John", "Doe", "John Doe"}, + WantError: true, + WantSpan: &sentry.Span{ + Data: map[string]interface{}{ + "db.system": sentrysql.DatabaseSystem("sqlite"), + "db.name": "memory", + "server.address": "localhost", + "server.port": "5432", + "db.operation": "INSERT", }, + Description: "INSERT INTO exec_test (id, name) VALUES (?, ?, ?, ?)", + Op: "db.sql.exec", + Tags: nil, + Origin: "manual", + Sampled: sentry.SampledTrue, + Status: sentry.SpanStatusInternalError, }, - } + }, + } - spansCh := make(chan []*sentry.Span, len(tests)) + spansCh := make(chan []*sentry.Span, len(tests)) - sentryClient, err := sentry.NewClient(sentry.ClientOptions{ - EnableTracing: true, - TracesSampleRate: 1.0, - BeforeSendTransaction: func(event *sentry.Event, hint *sentry.EventHint) *sentry.Event { - spansCh <- event.Spans - return event - }, - }) + sentryClient, err := sentry.NewClient(sentry.ClientOptions{ + EnableTracing: true, + TracesSampleRate: 1.0, + BeforeSendTransaction: func(event *sentry.Event, hint *sentry.EventHint) *sentry.Event { + spansCh <- event.Spans + return event + }, + }) + if err != nil { + t.Fatal(err) + } + + for _, tt := range tests { + hub := sentry.NewHub(sentryClient, sentry.NewScope()) + ctx, cancel := context.WithTimeout(sentry.SetHubOnContext(context.Background(), hub), 10*time.Second) + span := sentry.StartSpan(ctx, "fake_parent", sentry.WithTransactionName("Fake Parent")) + ctx = span.Context() + + conn, err := db.Conn(ctx) if err != nil { + cancel() t.Fatal(err) } - for _, tt := range tests { - hub := sentry.NewHub(sentryClient, sentry.NewScope()) - ctx, cancel := context.WithTimeout(sentry.SetHubOnContext(context.Background(), hub), 10*time.Second) - span := sentry.StartSpan(ctx, "fake_parent", sentry.WithTransactionName("Fake Parent")) - ctx = span.Context() - - conn, err := db.Conn(ctx) - if err != nil { - cancel() - t.Fatal(err) - } - - rows, err := conn.QueryContext(ctx, tt.Query, tt.Parameters...) - if err != nil && !tt.WantError { - _ = conn.Close() - cancel() - t.Fatal(err) - } - - if rows != nil { - _ = rows.Close() - } - + _, err = conn.ExecContext(ctx, tt.Query, tt.Parameters...) + if err != nil && !tt.WantError { _ = conn.Close() - - span.Finish() cancel() + t.Fatal(err) } - if ok := sentryClient.Flush(testutils.FlushTimeout()); !ok { - t.Fatal("sentry.Flush timed out") - } - close(spansCh) - - var got [][]*sentry.Span - for e := range spansCh { - got = append(got, e) - } + _ = conn.Close() - for i, tt := range tests { - var foundMatch = false - gotSpans := got[i] - - var diffs []string - for _, gotSpan := range gotSpans { - if diff := cmp.Diff(tt.WantSpan, gotSpan, optstrans); diff != "" { - diffs = append(diffs, diff) - } else { - foundMatch = true - break - } - } + span.Finish() + cancel() + } - if !foundMatch { - t.Errorf("Span mismatch (-want +got):\n%s", strings.Join(diffs, "\n")) + if ok := sentryClient.Flush(testutils.FlushTimeout()); !ok { + t.Fatal("sentry.Flush timed out") + } + close(spansCh) + + var got [][]*sentry.Span + for e := range spansCh { + got = append(got, e) + } + + for i, tt := range tests { + var foundMatch = false + gotSpans := got[i] + + var diffs []string + for _, gotSpan := range gotSpans { + if diff := cmp.Diff(tt.WantSpan, gotSpan, optstrans); diff != "" { + diffs = append(diffs, diff) + } else { + foundMatch = true + break } } - }) - t.Run("ExecContext", func(t *testing.T) { - tests := []struct { - Query string - Parameters []interface{} - WantSpan *sentry.Span - WantError bool - }{ - { - Query: "INSERT INTO exec_test (id, name) VALUES (?, ?)", - Parameters: []interface{}{2, "Peter"}, - WantSpan: &sentry.Span{ - Data: map[string]interface{}{ - "db.system": sentrysql.DatabaseSystem("sqlite"), - "db.name": "memory", - "server.address": "localhost", - "server.port": "5432", - "db.operation": "INSERT", - }, - Description: "INSERT INTO exec_test (id, name) VALUES (?, ?)", - Op: "db.sql.exec", - Tags: nil, - Origin: "manual", - Sampled: sentry.SampledTrue, - Status: sentry.SpanStatusOK, + if !foundMatch { + t.Errorf("Span mismatch (-want +got):\n%s", strings.Join(diffs, "\n")) + } + } + }) +} + +//nolint:dupl +func TestNewSentrySQL_BeginTx(t *testing.T) { + db, err := sql.Open("sentrysql-sqlite", ":memory:") + if err != nil { + t.Fatalf("opening sqlite: %v", err) + } + db.SetMaxOpenConns(1) + defer db.Close() + + setupQueries := []string{ + "CREATE TABLE exec_test (id INT, name TEXT)", + "CREATE TABLE query_test (id INT, name TEXT, age INT, created_at TEXT)", + "INSERT INTO query_test (id, name, age, created_at) VALUES (1, 'John', 30, '2023-01-01')", + "INSERT INTO query_test (id, name, age, created_at) VALUES (2, 'Jane', 25, '2023-01-02')", + "INSERT INTO query_test (id, name, age, created_at) VALUES (3, 'Bob', 35, '2023-01-03')", + } + + setupCtx, cancelCtx := context.WithTimeout(context.Background(), 30*time.Second) + defer cancelCtx() + + for _, query := range setupQueries { + _, err = db.ExecContext(setupCtx, query) + if err != nil { + t.Fatalf("initializing table on sqlite: %v", err) + } + } + + t.Run("Singles", func(t *testing.T) { + tests := []struct { + Query string + Parameters []interface{} + WantSpan *sentry.Span + WantError bool + }{ + { + Query: "INSERT INTO exec_test (id, name) VALUES (?, ?)", + Parameters: []interface{}{2, "Peter"}, + WantSpan: &sentry.Span{ + Data: map[string]interface{}{ + "db.system": sentrysql.DatabaseSystem("sqlite"), + "db.name": "memory", + "server.address": "localhost", + "server.port": "5432", + "db.operation": "INSERT", }, + Description: "INSERT INTO exec_test (id, name) VALUES (?, ?)", + Op: "db.sql.exec", + Tags: nil, + Origin: "manual", + Sampled: sentry.SampledTrue, + Status: sentry.SpanStatusOK, }, - { - Query: "INSERT INTO exec_test (id, name) VALUES (?, ?, ?, ?)", - Parameters: []interface{}{4, "John", "Doe", "John Doe"}, - WantError: true, - WantSpan: &sentry.Span{ - Data: map[string]interface{}{ - "db.system": sentrysql.DatabaseSystem("sqlite"), - "db.name": "memory", - "server.address": "localhost", - "server.port": "5432", - "db.operation": "INSERT", - }, - Description: "INSERT INTO exec_test (id, name) VALUES (?, ?, ?, ?)", - Op: "db.sql.exec", - Tags: nil, - Origin: "manual", - Sampled: sentry.SampledTrue, - Status: sentry.SpanStatusInternalError, + }, + { + Query: "INSERT INTO exec_test (id, name) VALUES (?, ?, ?, ?)", + Parameters: []interface{}{4, "John", "Doe", "John Doe"}, + WantError: true, + WantSpan: &sentry.Span{ + Data: map[string]interface{}{ + "db.system": sentrysql.DatabaseSystem("sqlite"), + "db.name": "memory", + "server.address": "localhost", + "server.port": "5432", + "db.operation": "INSERT", }, + Description: "INSERT INTO exec_test (id, name) VALUES (?, ?, ?, ?)", + Op: "db.sql.exec", + Tags: nil, + Origin: "manual", + Sampled: sentry.SampledTrue, + Status: sentry.SpanStatusInternalError, }, - } + }, + } - spansCh := make(chan []*sentry.Span, len(tests)) + spansCh := make(chan []*sentry.Span, len(tests)) - sentryClient, err := sentry.NewClient(sentry.ClientOptions{ - EnableTracing: true, - TracesSampleRate: 1.0, - BeforeSendTransaction: func(event *sentry.Event, hint *sentry.EventHint) *sentry.Event { - spansCh <- event.Spans - return event - }, - }) + sentryClient, err := sentry.NewClient(sentry.ClientOptions{ + EnableTracing: true, + TracesSampleRate: 1.0, + BeforeSendTransaction: func(event *sentry.Event, hint *sentry.EventHint) *sentry.Event { + spansCh <- event.Spans + return event + }, + }) + if err != nil { + t.Fatal(err) + } + + for _, tt := range tests { + hub := sentry.NewHub(sentryClient, sentry.NewScope()) + ctx, cancel := context.WithTimeout(sentry.SetHubOnContext(context.Background(), hub), 10*time.Second) + span := sentry.StartSpan(ctx, "fake_parent", sentry.WithTransactionName("Fake Parent")) + ctx = span.Context() + + conn, err := db.Conn(ctx) if err != nil { + cancel() t.Fatal(err) } - for _, tt := range tests { - hub := sentry.NewHub(sentryClient, sentry.NewScope()) - ctx, cancel := context.WithTimeout(sentry.SetHubOnContext(context.Background(), hub), 10*time.Second) - span := sentry.StartSpan(ctx, "fake_parent", sentry.WithTransactionName("Fake Parent")) - ctx = span.Context() - - conn, err := db.Conn(ctx) - if err != nil { - cancel() - t.Fatal(err) - } - - _, err = conn.ExecContext(ctx, tt.Query, tt.Parameters...) - if err != nil && !tt.WantError { - _ = conn.Close() - cancel() - t.Fatal(err) - } + tx, err := conn.BeginTx(ctx, nil) + if err != nil { + cancel() + t.Fatal(err) + } + _, err = tx.ExecContext(ctx, tt.Query, tt.Parameters...) + if err != nil && !tt.WantError { _ = conn.Close() - - span.Finish() cancel() + t.Fatal(err) } - if ok := sentryClient.Flush(testutils.FlushTimeout()); !ok { - t.Fatal("sentry.Flush timed out") + err = tx.Commit() + if err != nil && !tt.WantError { + _ = conn.Close() + cancel() + t.Fatal(err) } - close(spansCh) - var got [][]*sentry.Span - for e := range spansCh { - got = append(got, e) - } + _ = tx.Rollback() - for i, tt := range tests { - var foundMatch = false - gotSpans := got[i] - - var diffs []string - for _, gotSpan := range gotSpans { - if diff := cmp.Diff(tt.WantSpan, gotSpan, optstrans); diff != "" { - diffs = append(diffs, diff) - } else { - foundMatch = true - break - } - } + _ = conn.Close() - if !foundMatch { - t.Errorf("Span mismatch (-want +got):\n%s", strings.Join(diffs, "\n")) + span.Finish() + cancel() + } + + if ok := sentryClient.Flush(testutils.FlushTimeout()); !ok { + t.Fatal("sentry.Flush timed out") + } + close(spansCh) + + var got [][]*sentry.Span + for e := range spansCh { + got = append(got, e) + } + + for i, tt := range tests { + var foundMatch = false + gotSpans := got[i] + + var diffs []string + for _, gotSpan := range gotSpans { + if diff := cmp.Diff(tt.WantSpan, gotSpan, optstrans); diff != "" { + diffs = append(diffs, diff) + } else { + foundMatch = true + break } } - }) + + if !foundMatch { + t.Errorf("Span mismatch (-want +got):\n%s", strings.Join(diffs, "\n")) + } + } }) - t.Run("Ping", func(t *testing.T) { - // Just checking if this works and doesn't panic - err := db.Ping() + t.Run("Multiple Queries", func(t *testing.T) { + spansCh := make(chan []*sentry.Span, 2) + + sentryClient, err := sentry.NewClient(sentry.ClientOptions{ + EnableTracing: true, + TracesSampleRate: 1.0, + BeforeSendTransaction: func(event *sentry.Event, hint *sentry.EventHint) *sentry.Event { + spansCh <- event.Spans + return event + }, + }) if err != nil { t.Fatal(err) } - }) - t.Run("PingContext", func(t *testing.T) { - // Just checking if this works and doesn't panic - err := db.PingContext(context.Background()) + hub := sentry.NewHub(sentryClient, sentry.NewScope()) + ctx, cancel := context.WithTimeout(sentry.SetHubOnContext(context.Background(), hub), 10*time.Second) + defer cancel() + span := sentry.StartSpan(ctx, "fake_parent", sentry.WithTransactionName("Fake Parent")) + ctx = span.Context() + + conn, err := db.Conn(ctx) if err != nil { t.Fatal(err) } - }) - t.Run("BeginTx", func(t *testing.T) { - t.Run("Singles", func(t *testing.T) { - tests := []struct { - Query string - Parameters []interface{} - WantSpan *sentry.Span - WantError bool - }{ - { - Query: "INSERT INTO exec_test (id, name) VALUES (?, ?)", - Parameters: []interface{}{2, "Peter"}, - WantSpan: &sentry.Span{ - Data: map[string]interface{}{ - "db.system": sentrysql.DatabaseSystem("sqlite"), - "db.name": "memory", - "server.address": "localhost", - "server.port": "5432", - "db.operation": "INSERT", - }, - Description: "INSERT INTO exec_test (id, name) VALUES (?, ?)", - Op: "db.sql.exec", - Tags: nil, - Origin: "manual", - Sampled: sentry.SampledTrue, - Status: sentry.SpanStatusOK, - }, - }, - { - Query: "INSERT INTO exec_test (id, name) VALUES (?, ?, ?, ?)", - Parameters: []interface{}{4, "John", "Doe", "John Doe"}, - WantError: true, - WantSpan: &sentry.Span{ - Data: map[string]interface{}{ - "db.system": sentrysql.DatabaseSystem("sqlite"), - "db.name": "memory", - "server.address": "localhost", - "server.port": "5432", - "db.operation": "INSERT", - }, - Description: "INSERT INTO exec_test (id, name) VALUES (?, ?, ?, ?)", - Op: "db.sql.exec", - Tags: nil, - Origin: "manual", - Sampled: sentry.SampledTrue, - Status: sentry.SpanStatusInternalError, - }, - }, - } - - spansCh := make(chan []*sentry.Span, len(tests)) + tx, err := conn.BeginTx(ctx, nil) + if err != nil { + t.Fatal(err) + } + defer func() { + _ = tx.Rollback() + }() - sentryClient, err := sentry.NewClient(sentry.ClientOptions{ - EnableTracing: true, - TracesSampleRate: 1.0, - BeforeSendTransaction: func(event *sentry.Event, hint *sentry.EventHint) *sentry.Event { - spansCh <- event.Spans - return event - }, - }) - if err != nil { - t.Fatal(err) - } + var name string + err = tx.QueryRowContext(ctx, "SELECT name FROM query_test WHERE id = ?", 1).Scan(&name) + if err != nil { + _ = tx.Rollback() + _ = conn.Close() + cancel() + t.Fatal(err) + } - for _, tt := range tests { - hub := sentry.NewHub(sentryClient, sentry.NewScope()) - ctx, cancel := context.WithTimeout(sentry.SetHubOnContext(context.Background(), hub), 10*time.Second) - span := sentry.StartSpan(ctx, "fake_parent", sentry.WithTransactionName("Fake Parent")) - ctx = span.Context() + _, err = tx.ExecContext(ctx, "INSERT INTO exec_test (id, name) VALUES (?, ?)", 5, "Catherine") + if err != nil { + _ = tx.Rollback() + _ = conn.Close() + cancel() + t.Fatal(err) + } - conn, err := db.Conn(ctx) - if err != nil { - cancel() - t.Fatal(err) - } + err = tx.Commit() + if err != nil { + _ = conn.Close() + cancel() + t.Fatal(err) + } - tx, err := conn.BeginTx(ctx, nil) - if err != nil { - cancel() - t.Fatal(err) - } + _ = conn.Close() - _, err = tx.ExecContext(ctx, tt.Query, tt.Parameters...) - if err != nil && !tt.WantError { - _ = conn.Close() - cancel() - t.Fatal(err) - } + span.Finish() - err = tx.Commit() - if err != nil && !tt.WantError { - _ = conn.Close() - cancel() - t.Fatal(err) - } + cancel() - _ = tx.Rollback() + if ok := sentryClient.Flush(testutils.FlushTimeout()); !ok { + t.Fatal("sentry.Flush timed out") + } + close(spansCh) - _ = conn.Close() + var got []*sentry.Span + for e := range spansCh { + got = append(got, e...) + } - span.Finish() - cancel() - } + want := []*sentry.Span{ + { + Data: map[string]interface{}{ + "db.system": sentrysql.DatabaseSystem("sqlite"), + "db.name": "memory", + "server.address": "localhost", + "server.port": "5432", + "db.operation": "SELECT", + }, + Description: "SELECT name FROM query_test WHERE id = ?", + Op: "db.sql.query", + Tags: nil, + Origin: "manual", + Sampled: sentry.SampledTrue, + Status: sentry.SpanStatusOK, + }, + { + Data: map[string]interface{}{ + "db.system": sentrysql.DatabaseSystem("sqlite"), + "db.name": "memory", + "server.address": "localhost", + "server.port": "5432", + "db.operation": "INSERT", + }, + Description: "INSERT INTO exec_test (id, name) VALUES (?, ?)", + Op: "db.sql.exec", + Tags: nil, + Origin: "manual", + Sampled: sentry.SampledTrue, + Status: sentry.SpanStatusOK, + }, + } - if ok := sentryClient.Flush(testutils.FlushTimeout()); !ok { - t.Fatal("sentry.Flush timed out") - } - close(spansCh) + if diff := cmp.Diff(want, got, optstrans); diff != "" { + t.Errorf("Span mismatch (-want +got):\n%s", diff) + } + }) +} - var got [][]*sentry.Span - for e := range spansCh { - got = append(got, e) - } +//nolint:dupl +func TestNewSentrySQL_PrepareContext(t *testing.T) { + db, err := sql.Open("sentrysql-sqlite", ":memory:") + if err != nil { + t.Fatalf("opening sqlite: %v", err) + } + db.SetMaxOpenConns(1) + defer db.Close() - for i, tt := range tests { - var foundMatch = false - gotSpans := got[i] - - var diffs []string - for _, gotSpan := range gotSpans { - if diff := cmp.Diff(tt.WantSpan, gotSpan, optstrans); diff != "" { - diffs = append(diffs, diff) - } else { - foundMatch = true - break - } - } + setupQueries := []string{ + "CREATE TABLE exec_test (id INT, name TEXT)", + "CREATE TABLE query_test (id INT, name TEXT, age INT, created_at TEXT)", + "INSERT INTO query_test (id, name, age, created_at) VALUES (1, 'John', 30, '2023-01-01')", + "INSERT INTO query_test (id, name, age, created_at) VALUES (2, 'Jane', 25, '2023-01-02')", + "INSERT INTO query_test (id, name, age, created_at) VALUES (3, 'Bob', 35, '2023-01-03')", + } - if !foundMatch { - t.Errorf("Span mismatch (-want +got):\n%s", strings.Join(diffs, "\n")) - } - } - }) + setupCtx, cancelCtx := context.WithTimeout(context.Background(), 30*time.Second) + defer cancelCtx() - t.Run("Multiple Queries", func(t *testing.T) { - spansCh := make(chan []*sentry.Span, 2) + for _, query := range setupQueries { + _, err = db.ExecContext(setupCtx, query) + if err != nil { + t.Fatalf("initializing table on sqlite: %v", err) + } + } - sentryClient, err := sentry.NewClient(sentry.ClientOptions{ - EnableTracing: true, - TracesSampleRate: 1.0, - BeforeSendTransaction: func(event *sentry.Event, hint *sentry.EventHint) *sentry.Event { - spansCh <- event.Spans - return event + t.Run("Exec", func(t *testing.T) { + tests := []struct { + Query string + Parameters []interface{} + WantSpan *sentry.Span + WantError bool + }{ + { + Query: "INSERT INTO exec_test (id, name) VALUES (?, ?)", + Parameters: []interface{}{3, "Sarah"}, + WantSpan: &sentry.Span{ + Data: map[string]interface{}{ + "db.system": sentrysql.DatabaseSystem("sqlite"), + "db.name": "memory", + "server.address": "localhost", + "server.port": "5432", + "db.operation": "INSERT", + }, + Description: "INSERT INTO exec_test (id, name) VALUES (?, ?)", + Op: "db.sql.exec", + Tags: nil, + Origin: "manual", + Sampled: sentry.SampledTrue, + Status: sentry.SpanStatusOK, }, - }) - if err != nil { - t.Fatal(err) - } + }, + { + Query: "INSERT INTO exec_test (id, name) VALUES (?, ?, ?, ?)", + Parameters: []interface{}{4, "John", "Doe", "John Doe"}, + WantError: true, + WantSpan: &sentry.Span{ + Data: map[string]interface{}{ + "db.system": sentrysql.DatabaseSystem("sqlite"), + "db.name": "memory", + "server.address": "localhost", + "server.port": "5432", + "db.operation": "INSERT", + }, + Description: "INSERT INTO exec_test (id, name) VALUES (?, ?, ?, ?)", + Op: "db.sql.exec", + Tags: nil, + Origin: "manual", + Sampled: sentry.SampledTrue, + Status: sentry.SpanStatusInternalError, + }, + }, + } + + spansCh := make(chan []*sentry.Span, len(tests)) + + sentryClient, err := sentry.NewClient(sentry.ClientOptions{ + EnableTracing: true, + TracesSampleRate: 1.0, + BeforeSendTransaction: func(event *sentry.Event, hint *sentry.EventHint) *sentry.Event { + spansCh <- event.Spans + return event + }, + }) + if err != nil { + t.Fatal(err) + } + for _, tt := range tests { hub := sentry.NewHub(sentryClient, sentry.NewScope()) ctx, cancel := context.WithTimeout(sentry.SetHubOnContext(context.Background(), hub), 10*time.Second) - defer cancel() span := sentry.StartSpan(ctx, "fake_parent", sentry.WithTransactionName("Fake Parent")) ctx = span.Context() - conn, err := db.Conn(ctx) - if err != nil { - t.Fatal(err) - } - - tx, err := conn.BeginTx(ctx, nil) - if err != nil { - t.Fatal(err) - } - defer tx.Rollback() - - var name string - err = tx.QueryRowContext(ctx, "SELECT name FROM query_test WHERE id = ?", 1).Scan(&name) + stmt, err := db.PrepareContext(ctx, tt.Query) if err != nil { - tx.Rollback() - conn.Close() cancel() t.Fatal(err) } - _, err = tx.ExecContext(ctx, "INSERT INTO exec_test (id, name) VALUES (?, ?)", 5, "Catherine") - if err != nil { - tx.Rollback() - conn.Close() + _, err = stmt.Exec(tt.Parameters...) + if err != nil && !tt.WantError { cancel() t.Fatal(err) } - err = tx.Commit() - if err != nil { - conn.Close() - cancel() - t.Fatal(err) - } + span.Finish() + cancel() + } - conn.Close() + if ok := sentryClient.Flush(testutils.FlushTimeout()); !ok { + t.Fatal("sentry.Flush timed out") + } + close(spansCh) - span.Finish() + var got [][]*sentry.Span + for e := range spansCh { + got = append(got, e) + } - cancel() + for i, tt := range tests { + var foundMatch = false + gotSpans := got[i] - if ok := sentryClient.Flush(testutils.FlushTimeout()); !ok { - t.Fatal("sentry.Flush timed out") + var diffs []string + for _, gotSpan := range gotSpans { + if diff := cmp.Diff(tt.WantSpan, gotSpan, optstrans); diff != "" { + diffs = append(diffs, diff) + } else { + foundMatch = true + break + } } - close(spansCh) - var got []*sentry.Span - for e := range spansCh { - got = append(got, e...) + if !foundMatch { + t.Errorf("Span mismatch (-want +got):\n%s", strings.Join(diffs, "\n")) } + } + }) - want := []*sentry.Span{ - { + t.Run("Query", func(t *testing.T) { + tests := []struct { + Query string + Parameters []interface{} + WantSpan *sentry.Span + WantError bool + }{ + { + Query: "SELECT * FROM query_test WHERE id = ?", + Parameters: []interface{}{2}, + WantSpan: &sentry.Span{ Data: map[string]interface{}{ "db.system": sentrysql.DatabaseSystem("sqlite"), "db.name": "memory", @@ -1065,33 +1080,103 @@ func TestNewSentrySQL_Integration(t *testing.T) { "server.port": "5432", "db.operation": "SELECT", }, - Description: "SELECT name FROM query_test WHERE id = ?", + Description: "SELECT * FROM query_test WHERE id = ?", Op: "db.sql.query", Tags: nil, Origin: "manual", Sampled: sentry.SampledTrue, Status: sentry.SpanStatusOK, }, - { + }, + { + Query: "SELECT * FROM query_test WHERE id =", + Parameters: []interface{}{1}, + WantError: true, + WantSpan: &sentry.Span{ Data: map[string]interface{}{ "db.system": sentrysql.DatabaseSystem("sqlite"), "db.name": "memory", "server.address": "localhost", "server.port": "5432", - "db.operation": "INSERT", + "db.operation": "SELECT", }, - Description: "INSERT INTO exec_test (id, name) VALUES (?, ?)", - Op: "db.sql.exec", + Description: "SELECT * FROM query_test WHERE id =", + Op: "db.sql.query", Tags: nil, Origin: "manual", Sampled: sentry.SampledTrue, - Status: sentry.SpanStatusOK, + Status: sentry.SpanStatusInternalError, }, + }, + } + + spansCh := make(chan []*sentry.Span, len(tests)) + + sentryClient, err := sentry.NewClient(sentry.ClientOptions{ + EnableTracing: true, + TracesSampleRate: 1.0, + BeforeSendTransaction: func(event *sentry.Event, hint *sentry.EventHint) *sentry.Event { + spansCh <- event.Spans + return event + }, + }) + if err != nil { + t.Fatal(err) + } + + for _, tt := range tests { + hub := sentry.NewHub(sentryClient, sentry.NewScope()) + ctx, cancel := context.WithTimeout(sentry.SetHubOnContext(context.Background(), hub), 10*time.Second) + span := sentry.StartSpan(ctx, "fake_parent", sentry.WithTransactionName("Fake Parent")) + ctx = span.Context() + + stmt, err := db.PrepareContext(ctx, tt.Query) + if err != nil { + cancel() + t.Fatal(err) } - if diff := cmp.Diff(want, got, optstrans); diff != "" { - t.Errorf("Span mismatch (-want +got):\n%s", diff) + rows, err := stmt.Query(tt.Parameters...) + if err != nil && !tt.WantError { + cancel() + t.Fatal(err) } - }) + + if rows != nil { + _ = rows.Close() + } + + span.Finish() + cancel() + } + + if ok := sentryClient.Flush(testutils.FlushTimeout()); !ok { + t.Fatal("sentry.Flush timed out") + } + close(spansCh) + + var got [][]*sentry.Span + for e := range spansCh { + got = append(got, e) + } + + for i, tt := range tests { + var foundMatch = false + gotSpans := got[i] + + var diffs []string + for _, gotSpan := range gotSpans { + if diff := cmp.Diff(tt.WantSpan, gotSpan, optstrans); diff != "" { + diffs = append(diffs, diff) + } else { + foundMatch = true + break + } + } + + if !foundMatch { + t.Errorf("Span mismatch (-want +got):\n%s", strings.Join(diffs, "\n")) + } + } }) }