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 }