github.com/mier85/go-sensor@v1.30.1-0.20220920111756-9bf41b3bc7e0/instrumentation_sql_test.go (about) 1 // (c) Copyright IBM Corp. 2021 2 // (c) Copyright Instana Inc. 2020 3 4 package instana_test 5 6 import ( 7 "context" 8 "database/sql" 9 "database/sql/driver" 10 "io" 11 "testing" 12 13 "github.com/instana/testify/assert" 14 "github.com/instana/testify/require" 15 instana "github.com/mier85/go-sensor" 16 ot "github.com/opentracing/opentracing-go" 17 "github.com/opentracing/opentracing-go/ext" 18 ) 19 20 func TestInstrumentSQLDriver(t *testing.T) { 21 recorder := instana.NewTestRecorder() 22 s := instana.NewSensorWithTracer(instana.NewTracerWithEverything(&instana.Options{ 23 Service: "go-sensor-test", 24 }, recorder)) 25 26 instana.InstrumentSQLDriver(s, "test_register_driver", sqlDriver{}) 27 assert.NotPanics(t, func() { 28 instana.InstrumentSQLDriver(s, "test_register_driver", sqlDriver{}) 29 }) 30 } 31 32 func TestOpenSQLDB(t *testing.T) { 33 recorder := instana.NewTestRecorder() 34 s := instana.NewSensorWithTracer(instana.NewTracerWithEverything(&instana.Options{ 35 Service: "go-sensor-test", 36 }, recorder)) 37 38 instana.InstrumentSQLDriver(s, "test_driver", sqlDriver{}) 39 require.Contains(t, sql.Drivers(), "test_driver_with_instana") 40 41 db, err := instana.SQLOpen("test_driver", "connection string") 42 require.NoError(t, err) 43 44 t.Run("Exec", func(t *testing.T) { 45 res, err := db.Exec("TEST QUERY") 46 require.NoError(t, err) 47 48 lastID, err := res.LastInsertId() 49 require.NoError(t, err) 50 assert.Equal(t, int64(42), lastID) 51 52 spans := recorder.GetQueuedSpans() 53 require.Len(t, spans, 1) 54 55 span := spans[0] 56 assert.Equal(t, 0, span.Ec) 57 assert.EqualValues(t, instana.ExitSpanKind, span.Kind) 58 59 require.IsType(t, instana.SDKSpanData{}, span.Data) 60 data := span.Data.(instana.SDKSpanData) 61 62 assert.Equal(t, instana.SDKSpanTags{ 63 Name: "sdk.database", 64 Type: "exit", 65 Custom: map[string]interface{}{ 66 "tags": ot.Tags{ 67 "span.kind": ext.SpanKindRPCClientEnum, 68 "db.instance": "connection string", 69 "db.statement": "TEST QUERY", 70 "db.type": "sql", 71 "peer.address": "connection string", 72 }, 73 }, 74 }, data.Tags) 75 }) 76 77 t.Run("Query", func(t *testing.T) { 78 res, err := db.Query("TEST QUERY") 79 require.NoError(t, err) 80 81 cols, err := res.Columns() 82 require.NoError(t, err) 83 assert.Equal(t, []string{"col1", "col2"}, cols) 84 85 spans := recorder.GetQueuedSpans() 86 require.Len(t, spans, 1) 87 88 span := spans[0] 89 assert.Equal(t, 0, span.Ec) 90 assert.EqualValues(t, instana.ExitSpanKind, span.Kind) 91 92 require.IsType(t, instana.SDKSpanData{}, span.Data) 93 data := span.Data.(instana.SDKSpanData) 94 95 assert.Equal(t, instana.SDKSpanTags{ 96 Name: "sdk.database", 97 Type: "exit", 98 Custom: map[string]interface{}{ 99 "tags": ot.Tags{ 100 "span.kind": ext.SpanKindRPCClientEnum, 101 "db.instance": "connection string", 102 "db.statement": "TEST QUERY", 103 "db.type": "sql", 104 "peer.address": "connection string", 105 }, 106 }, 107 }, data.Tags) 108 }) 109 } 110 111 func TestOpenSQLDB_URIConnString(t *testing.T) { 112 recorder := instana.NewTestRecorder() 113 s := instana.NewSensorWithTracer(instana.NewTracerWithEverything(&instana.Options{ 114 Service: "go-sensor-test", 115 }, recorder)) 116 117 instana.InstrumentSQLDriver(s, "fake_db_driver", sqlDriver{}) 118 require.Contains(t, sql.Drivers(), "test_driver_with_instana") 119 120 db, err := instana.SQLOpen("fake_db_driver", "db://user1:p@55w0rd@db-host:1234/test-schema?param=value") 121 require.NoError(t, err) 122 123 _, err = db.Exec("TEST QUERY") 124 require.NoError(t, err) 125 126 spans := recorder.GetQueuedSpans() 127 require.Len(t, spans, 1) 128 129 require.IsType(t, instana.SDKSpanData{}, spans[0].Data) 130 data := spans[0].Data.(instana.SDKSpanData) 131 132 assert.Equal(t, instana.SDKSpanTags{ 133 Name: "sdk.database", 134 Type: "exit", 135 Custom: map[string]interface{}{ 136 "tags": ot.Tags{ 137 "span.kind": ext.SpanKindRPCClientEnum, 138 "db.instance": "test-schema", 139 "db.statement": "TEST QUERY", 140 "db.type": "sql", 141 "peer.address": "db://user1@db-host:1234/test-schema?param=value", 142 "peer.hostname": "db-host", 143 "peer.port": "1234", 144 }, 145 }, 146 }, data.Tags) 147 } 148 149 func TestOpenSQLDB_PostgresKVConnString(t *testing.T) { 150 recorder := instana.NewTestRecorder() 151 s := instana.NewSensorWithTracer(instana.NewTracerWithEverything(&instana.Options{ 152 Service: "go-sensor-test", 153 }, recorder)) 154 155 instana.InstrumentSQLDriver(s, "fake_postgres_driver", sqlDriver{}) 156 require.Contains(t, sql.Drivers(), "fake_postgres_driver_with_instana") 157 158 db, err := instana.SQLOpen("fake_postgres_driver", "host=db-host1,db-host-2 hostaddr=1.2.3.4,2.3.4.5 connect_timeout=10 port=1234 user=user1 password=p@55w0rd dbname=test-schema") 159 require.NoError(t, err) 160 161 _, err = db.Exec("TEST QUERY") 162 require.NoError(t, err) 163 164 spans := recorder.GetQueuedSpans() 165 require.Len(t, spans, 1) 166 167 require.IsType(t, instana.SDKSpanData{}, spans[0].Data) 168 data := spans[0].Data.(instana.SDKSpanData) 169 170 assert.Equal(t, instana.SDKSpanTags{ 171 Name: "sdk.database", 172 Type: "exit", 173 Custom: map[string]interface{}{ 174 "tags": ot.Tags{ 175 "span.kind": ext.SpanKindRPCClientEnum, 176 "db.instance": "test-schema", 177 "db.statement": "TEST QUERY", 178 "db.type": "sql", 179 "peer.address": "host=db-host1,db-host-2 hostaddr=1.2.3.4,2.3.4.5 connect_timeout=10 port=1234 user=user1 dbname=test-schema", 180 "peer.hostname": "1.2.3.4,2.3.4.5", 181 "peer.port": "1234", 182 }, 183 }, 184 }, data.Tags) 185 } 186 187 func TestOpenSQLDB_MySQLKVConnString(t *testing.T) { 188 recorder := instana.NewTestRecorder() 189 s := instana.NewSensorWithTracer(instana.NewTracerWithEverything(&instana.Options{ 190 Service: "go-sensor-test", 191 }, recorder)) 192 193 instana.InstrumentSQLDriver(s, "fake_mysql_driver", sqlDriver{}) 194 require.Contains(t, sql.Drivers(), "fake_mysql_driver_with_instana") 195 196 db, err := instana.SQLOpen("fake_mysql_driver", "Server=db-host1, db-host2;Database=test-schema;Port=1234;Uid=user1;Pwd=p@55w0rd;") 197 require.NoError(t, err) 198 199 _, err = db.Exec("TEST QUERY") 200 require.NoError(t, err) 201 202 spans := recorder.GetQueuedSpans() 203 require.Len(t, spans, 1) 204 205 require.IsType(t, instana.SDKSpanData{}, spans[0].Data) 206 data := spans[0].Data.(instana.SDKSpanData) 207 208 assert.Equal(t, instana.SDKSpanTags{ 209 Name: "sdk.database", 210 Type: "exit", 211 Custom: map[string]interface{}{ 212 "tags": ot.Tags{ 213 "span.kind": ext.SpanKindRPCClientEnum, 214 "db.instance": "test-schema", 215 "db.statement": "TEST QUERY", 216 "db.type": "sql", 217 "peer.address": "Server=db-host1, db-host2;Database=test-schema;Port=1234;Uid=user1;", 218 "peer.hostname": "db-host1, db-host2", 219 "peer.port": "1234", 220 }, 221 }, 222 }, data.Tags) 223 } 224 225 func TestNoPanicWithNotParsableConnectionString(t *testing.T) { 226 s := instana.NewSensorWithTracer(instana.NewTracerWithEverything(&instana.Options{ 227 Service: "go-sensor-test", 228 }, instana.NewTestRecorder())) 229 230 instana.InstrumentSQLDriver(s, "test_driver", sqlDriver{}) 231 require.Contains(t, sql.Drivers(), "test_driver_with_instana") 232 233 assert.NotPanics(t, func() { 234 _, _ = instana.SQLOpen("test_driver", 235 "postgres:mysecretpassword@localhost/postgres") 236 }) 237 } 238 239 type sqlDriver struct{ Error error } 240 241 func (drv sqlDriver) Open(name string) (driver.Conn, error) { return sqlConn{drv.Error}, nil } //nolint:gosimple 242 243 type sqlConn struct{ Error error } 244 245 func (conn sqlConn) Prepare(query string) (driver.Stmt, error) { return sqlStmt{conn.Error}, nil } //nolint:gosimple 246 func (sqlConn) Close() error { return driver.ErrSkip } 247 func (sqlConn) Begin() (driver.Tx, error) { return nil, driver.ErrSkip } 248 249 func (conn sqlConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) { 250 return sqlResult{}, conn.Error 251 } 252 253 func (conn sqlConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) { 254 return sqlRows{}, conn.Error 255 } 256 257 type sqlStmt struct{ Error error } 258 259 func (sqlStmt) Close() error { return nil } 260 func (sqlStmt) NumInput() int { return -1 } 261 func (stmt sqlStmt) Exec(args []driver.Value) (driver.Result, error) { return sqlResult{}, stmt.Error } 262 func (stmt sqlStmt) Query(args []driver.Value) (driver.Rows, error) { return sqlRows{}, stmt.Error } 263 264 type sqlResult struct{} 265 266 func (sqlResult) LastInsertId() (int64, error) { return 42, nil } 267 func (sqlResult) RowsAffected() (int64, error) { return 100, nil } 268 269 type sqlRows struct{} 270 271 func (sqlRows) Columns() []string { return []string{"col1", "col2"} } 272 func (sqlRows) Close() error { return nil } 273 func (sqlRows) Next(dest []driver.Value) error { return io.EOF }