Skip to content

Adding more SQL test coverage #488

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions instrumentation_sql_go1.10_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,5 +186,7 @@ func TestWrapSQLConnector_Query_Error(t *testing.T) {

type sqlConnector struct{ Error error }

func (c sqlConnector) Connect(context.Context) (driver.Conn, error) { return sqlConn{c.Error}, nil } //nolint:gosimple
func (sqlConnector) Driver() driver.Driver { return sqlDriver{} }
func (c sqlConnector) Connect(context.Context) (driver.Conn, error) {
return sqlConn{Error: c.Error}, nil
} //nolint:gosimple
func (sqlConnector) Driver() driver.Driver { return sqlDriver{} }
251 changes: 243 additions & 8 deletions instrumentation_sql_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,206 @@ func TestOpenSQLDB_RedisConnString(t *testing.T) {
}, data.Tags)
}

func TestConnPrepareContext(t *testing.T) {
recorder := instana.NewTestRecorder()
s := instana.NewSensorWithTracer(instana.NewTracerWithEverything(&instana.Options{
Service: "go-sensor-test",
AgentClient: alwaysReadyClient{},
}, recorder))
defer instana.ShutdownSensor()

instana.InstrumentSQLDriver(s, "fake_pc", sqlDriver{})
require.Contains(t, sql.Drivers(), "fake_pc_with_instana")

db, err := instana.SQLOpen("fake_pc", "conn string")
require.NoError(t, err)

ctx := context.Background()

stmt, err := db.PrepareContext(ctx, "select 1 from table")
require.NoError(t, err)

_, err = stmt.QueryContext(ctx)
require.NoError(t, err)

spans := recorder.GetQueuedSpans()

require.Len(t, spans, 1)

require.IsType(t, instana.SDKSpanData{}, spans[0].Data)
data := spans[0].Data.(instana.SDKSpanData)

assert.Equal(t, instana.SDKSpanTags{
Name: "sdk.database",
Type: "exit",
Custom: map[string]interface{}{
"tags": ot.Tags{
"span.kind": ext.SpanKindRPCClientEnum,
"db.instance": "conn string",
"db.statement": "select 1 from table",
"db.type": "sql",
"peer.address": "conn string",
},
},
}, data.Tags)
}

func TestConnPrepareContextWithError(t *testing.T) {
recorder := instana.NewTestRecorder()
s := instana.NewSensorWithTracer(instana.NewTracerWithEverything(&instana.Options{
Service: "go-sensor-test",
AgentClient: alwaysReadyClient{},
}, recorder))
defer instana.ShutdownSensor()

instana.InstrumentSQLDriver(s, "fake_conn_pc_error", sqlDriver{Error: errors.New("some error")})
require.Contains(t, sql.Drivers(), "fake_conn_pc_error_with_instana")

db, err := instana.SQLOpen("fake_conn_pc_error", "conn string")
require.NoError(t, err)

ctx := context.Background()

stmt, err := db.PrepareContext(ctx, "select 1 from table")
require.NoError(t, err)

_, err = stmt.QueryContext(ctx)
require.Error(t, err)

spans := recorder.GetQueuedSpans()

require.Len(t, spans, 2)

assert.Equal(t, spans[0].Ec, 1)

require.IsType(t, instana.SDKSpanData{}, spans[0].Data)
data := spans[0].Data.(instana.SDKSpanData)

assert.Equal(t, instana.SDKSpanTags{
Name: "sdk.database",
Type: "exit",
Custom: map[string]interface{}{
"tags": ot.Tags{
"span.kind": ext.SpanKindRPCClientEnum,
"db.error": "some error",
"db.instance": "conn string",
"db.statement": "select 1 from table",
"db.type": "sql",
"peer.address": "conn string",
},
},
}, data.Tags)
}

func TestStmtExecContext(t *testing.T) {
recorder := instana.NewTestRecorder()
s := instana.NewSensorWithTracer(instana.NewTracerWithEverything(&instana.Options{
Service: "go-sensor-test",
AgentClient: alwaysReadyClient{},
}, recorder))
defer instana.ShutdownSensor()

instana.InstrumentSQLDriver(s, "fake_stmt_ec", sqlDriver{})
require.Contains(t, sql.Drivers(), "fake_stmt_ec_with_instana")

db, err := instana.SQLOpen("fake_stmt_ec", "conn string")
require.NoError(t, err)

ctx := context.Background()

stmt, err := db.PrepareContext(ctx, "select 1 from table")
require.NoError(t, err)

_, err = stmt.ExecContext(ctx)
require.NoError(t, err)

spans := recorder.GetQueuedSpans()

require.Len(t, spans, 1)

require.IsType(t, instana.SDKSpanData{}, spans[0].Data)
data := spans[0].Data.(instana.SDKSpanData)

assert.Equal(t, instana.SDKSpanTags{
Name: "sdk.database",
Type: "exit",
Custom: map[string]interface{}{
"tags": ot.Tags{
"span.kind": ext.SpanKindRPCClientEnum,
"db.instance": "conn string",
"db.statement": "select 1 from table",
"db.type": "sql",
"peer.address": "conn string",
},
},
}, data.Tags)
}

func TestStmtExecContextWithError(t *testing.T) {
recorder := instana.NewTestRecorder()
s := instana.NewSensorWithTracer(instana.NewTracerWithEverything(&instana.Options{
Service: "go-sensor-test",
AgentClient: alwaysReadyClient{},
}, recorder))
defer instana.ShutdownSensor()

instana.InstrumentSQLDriver(s, "fake_stmt_ec_with_error", sqlDriver{Error: errors.New("oh no")})
require.Contains(t, sql.Drivers(), "fake_stmt_ec_with_error_with_instana")

db, err := instana.SQLOpen("fake_stmt_ec_with_error", "conn string")
require.NoError(t, err)

ctx := context.Background()

stmt, err := db.PrepareContext(ctx, "select 1 from table")
require.NoError(t, err)

_, err = stmt.ExecContext(ctx)
require.Error(t, err)

spans := recorder.GetQueuedSpans()

require.Len(t, spans, 2)

require.IsType(t, instana.SDKSpanData{}, spans[0].Data)
data := spans[0].Data.(instana.SDKSpanData)

assert.Equal(t, instana.SDKSpanTags{
Name: "sdk.database",
Type: "exit",
Custom: map[string]interface{}{
"tags": ot.Tags{
"span.kind": ext.SpanKindRPCClientEnum,
"db.error": "oh no",
"db.instance": "conn string",
"db.statement": "select 1 from table",
"db.type": "sql",
"peer.address": "conn string",
},
},
}, data.Tags)
}

func TestConnPrepareContextWithErrorOnReturn(t *testing.T) {
recorder := instana.NewTestRecorder()
s := instana.NewSensorWithTracer(instana.NewTracerWithEverything(&instana.Options{
Service: "go-sensor-test",
AgentClient: alwaysReadyClient{},
}, recorder))
defer instana.ShutdownSensor()

instana.InstrumentSQLDriver(s, "fake_conn_pc_error_on_ret", sqlDriver{PrepareError: errors.New("oh no")})
require.Contains(t, sql.Drivers(), "fake_conn_pc_error_on_ret_with_instana")

db, err := instana.SQLOpen("fake_conn_pc_error_on_ret", "conn string")
require.NoError(t, err)

ctx := context.Background()

_, err = db.PrepareContext(ctx, "select 1 from table")
require.Error(t, err)
}

func TestOpenSQLDB_RedisConnString_WithError(t *testing.T) {
recorder := instana.NewTestRecorder()
s := instana.NewSensorWithTracer(instana.NewTracerWithEverything(&instana.Options{
Expand All @@ -358,7 +558,7 @@ func TestOpenSQLDB_RedisConnString_WithError(t *testing.T) {
}, recorder))
defer instana.ShutdownSensor()

instana.InstrumentSQLDriver(s, "fake_redis_driver_with_error", sqlDriver{errors.New("oops")})
instana.InstrumentSQLDriver(s, "fake_redis_driver_with_error", sqlDriver{Error: errors.New("oops")})
require.Contains(t, sql.Drivers(), "fake_redis_driver_with_error_with_instana")

db, err := instana.SQLOpen("fake_redis_driver_with_error", ":[email protected]:6790")
Expand Down Expand Up @@ -482,15 +682,41 @@ func TestProcedureWithNoDefaultChecker(t *testing.T) {
assert.NoError(t, err)
}

type sqlDriver struct{ Error error }
type sqlDriver struct {
// Error is a generic error in the SQL execution. It generates spans with errors
Error error
// StmtError will give an error when a method from Stmt returns. It does not generate spans at all
StmtError error
// PrepareError will give an error when a method from Prepare* returns. It does not generate spans at all
PrepareError error
}

func (drv sqlDriver) Open(name string) (driver.Conn, error) { return sqlConn{drv.Error}, nil } //nolint:gosimple
func (drv sqlDriver) Open(name string) (driver.Conn, error) {
return sqlConn{
Error: drv.Error,
StmtError: drv.StmtError,
PrepareError: drv.PrepareError,
}, nil
} //nolint:gosimple

type sqlConn struct{ Error error }
type sqlConn struct {
Error error
StmtError error
PrepareError error
}

func (conn sqlConn) Prepare(query string) (driver.Stmt, error) { return sqlStmt{conn.Error}, nil } //nolint:gosimple
func (s sqlConn) Close() error { return driver.ErrSkip }
func (s sqlConn) Begin() (driver.Tx, error) { return nil, driver.ErrSkip }
var _ driver.Conn = (*sqlConn)(nil)
var _ driver.ConnPrepareContext = (*sqlConn)(nil)

func (conn sqlConn) Prepare(query string) (driver.Stmt, error) {
return sqlStmt{Error: conn.Error}, nil
} //nolint:gosimple
func (conn sqlConn) Close() error { return driver.ErrSkip }
func (conn sqlConn) Begin() (driver.Tx, error) { return nil, driver.ErrSkip }

func (conn sqlConn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) {
return sqlStmt{StmtError: conn.StmtError, Error: conn.Error}, conn.PrepareError //nolint:gosimple
}

func (conn sqlConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) {
return sqlResult{}, conn.Error
Expand All @@ -500,12 +726,21 @@ func (conn sqlConn) QueryContext(ctx context.Context, query string, args []drive
return sqlRows{}, conn.Error
}

type sqlStmt struct{ Error error }
type sqlStmt struct {
Error error
StmtError error
}

func (sqlStmt) Close() error { return nil }
func (sqlStmt) NumInput() int { return -1 }
func (stmt sqlStmt) Exec(args []driver.Value) (driver.Result, error) { return sqlResult{}, stmt.Error }
func (stmt sqlStmt) Query(args []driver.Value) (driver.Rows, error) { return sqlRows{}, stmt.Error }
func (stmt sqlStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) {
return sqlRows{}, stmt.Error
}
func (stmt sqlStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) {
return sqlResult{}, stmt.Error
}

type sqlResult struct{}

Expand Down
2 changes: 1 addition & 1 deletion sonar-project.properties
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ sonar.links.ci=https://app.circleci.com/pipelines/github/instana/go-sensor
sonar.sourceEncoding=utf-8
sonar.sources=.
sonar.tests=.
sonar.exclusions=example/*.go,example/**/*.go,**/example/*.go,**/**/example/*.go
sonar.exclusions=eum.js,internal/bin/sql/sqlgen.go,example/*.go,example/**/*.go,**/example/*.go,**/**/example/*.go
sonar.test.inclusions=**/*_test.go,**/**/*_test.go,instrumentation/**/*_test.go
sonar.go.coverage.reportPaths=coverage.out,instrumentation/**/coverage.out
sonar.go.tests.reportPaths=coverage.json,instrumentation/**/coverage.json
2 changes: 2 additions & 0 deletions sql_execer.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
otlog "github.com/opentracing/opentracing-go/log"
)

// Execer interface is deprecated

type wExecer struct {
driver.Execer
connDetails DbConnDetails
Expand Down
2 changes: 2 additions & 0 deletions sql_queryer.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
otlog "github.com/opentracing/opentracing-go/log"
)

// Queryer is deprecated since Go v1.8

type wQueryer struct {
driver.Queryer
connDetails DbConnDetails
Expand Down