Unistore Chore: Add OTEL testing harness (#94835)
* add testing harness * fix mockery and linters * fix data race in tests * fix data race in tests * reduce cardinality of data
This commit is contained in:
@@ -14,6 +14,17 @@ import (
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql/sqltemplate"
|
||||
)
|
||||
|
||||
//nolint:unused
|
||||
const (
|
||||
otelAttrBaseKey = "dbutil_"
|
||||
otelAttrTemplateNameKey = otelAttrBaseKey + "template"
|
||||
otelAttrDialectKey = otelAttrBaseKey + "dialect"
|
||||
)
|
||||
|
||||
func withOtelAttrs(ctx context.Context, tmplName, dialectName string) context.Context {
|
||||
return ctx // TODO: in next PR
|
||||
}
|
||||
|
||||
// SQLError is an error returned by the database, which includes additionally
|
||||
// debugging information about what was sent to the database.
|
||||
type SQLError struct {
|
||||
@@ -79,7 +90,7 @@ func Debug(err error) error {
|
||||
|
||||
// Exec uses `req` as input for a non-data returning query generated with
|
||||
// `tmpl`, and executed in `x`.
|
||||
func Exec(ctx context.Context, x db.ContextExecer, tmpl *template.Template, req sqltemplate.SQLTemplate) (sql.Result, error) {
|
||||
func Exec(ctx context.Context, x db.ContextExecer, tmpl *template.Template, req sqltemplate.SQLTemplate) (db.Result, error) {
|
||||
if err := req.Validate(); err != nil {
|
||||
return nil, fmt.Errorf("Exec: invalid request for template %q: %w",
|
||||
tmpl.Name(), err)
|
||||
@@ -91,13 +102,15 @@ func Exec(ctx context.Context, x db.ContextExecer, tmpl *template.Template, req
|
||||
}
|
||||
query := sqltemplate.FormatSQL(rawQuery)
|
||||
|
||||
res, err := x.ExecContext(ctx, query, req.GetArgs()...)
|
||||
args := req.GetArgs()
|
||||
ctx = withOtelAttrs(ctx, tmpl.Name(), req.DialectName())
|
||||
res, err := x.ExecContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return nil, SQLError{
|
||||
Err: err,
|
||||
CallType: "Exec",
|
||||
TemplateName: tmpl.Name(),
|
||||
arguments: req.GetArgs(),
|
||||
arguments: args,
|
||||
Query: query,
|
||||
RawQuery: rawQuery,
|
||||
}
|
||||
@@ -108,7 +121,7 @@ func Exec(ctx context.Context, x db.ContextExecer, tmpl *template.Template, req
|
||||
|
||||
// Query uses `req` as input for a single-statement, set-returning query
|
||||
// generated with `tmpl`, and executed in `x`.
|
||||
func QueryRows(ctx context.Context, x db.ContextExecer, tmpl *template.Template, req sqltemplate.SQLTemplate) (*sql.Rows, error) {
|
||||
func QueryRows(ctx context.Context, x db.ContextExecer, tmpl *template.Template, req sqltemplate.SQLTemplate) (db.Rows, error) {
|
||||
if err := req.Validate(); err != nil {
|
||||
return nil, fmt.Errorf("Query: invalid request for template %q: %w",
|
||||
tmpl.Name(), err)
|
||||
@@ -120,13 +133,15 @@ func QueryRows(ctx context.Context, x db.ContextExecer, tmpl *template.Template,
|
||||
}
|
||||
query := sqltemplate.FormatSQL(rawQuery)
|
||||
|
||||
rows, err := x.QueryContext(ctx, query, req.GetArgs()...)
|
||||
args := req.GetArgs()
|
||||
ctx = withOtelAttrs(ctx, tmpl.Name(), req.DialectName())
|
||||
rows, err := x.QueryContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return nil, SQLError{
|
||||
Err: err,
|
||||
CallType: "Query",
|
||||
TemplateName: tmpl.Name(),
|
||||
arguments: req.GetArgs(),
|
||||
arguments: args,
|
||||
ScanDest: req.GetScanDest(),
|
||||
Query: query,
|
||||
RawQuery: rawQuery,
|
||||
@@ -186,13 +201,13 @@ func QueryRow[T any](ctx context.Context, x db.ContextExecer, tmpl *template.Tem
|
||||
}
|
||||
}
|
||||
|
||||
// DiscardRows discards all the ResultSets in the given *sql.Rows and returns
|
||||
// DiscardRows discards all the ResultSets in the given db.Rows and returns
|
||||
// the final rows error and the number of times NextResultSet was called. This
|
||||
// is useful to check for errors in queries with multiple SQL statements where
|
||||
// there is no interesting output, since some drivers may omit an error returned
|
||||
// by a SQL statement found in a statement that is not the first one. Note that
|
||||
// not all drivers support multi-statement calls, though.
|
||||
func DiscardRows(rows *sql.Rows) (int, error) {
|
||||
func DiscardRows(rows db.Rows) (int, error) {
|
||||
discardedResultSets := 1
|
||||
for ; rows.NextResultSet(); discardedResultSets++ {
|
||||
}
|
||||
@@ -203,7 +218,7 @@ type scanner interface {
|
||||
Scan(dest ...any) error
|
||||
}
|
||||
|
||||
// scanRow is used on *sql.Row and *sql.Rows, and is factored out here not to
|
||||
// scanRow is used on db.Row and db.Rows, and is factored out here not to
|
||||
// improving code reuse, but rather for ease of testing.
|
||||
func scanRow[T any](sc scanner, req sqltemplate.WithResults[T]) (zero T, err error) {
|
||||
if err = sc.Scan(req.GetScanDest()...); err != nil {
|
||||
|
||||
@@ -87,6 +87,8 @@ func TestQuery(t *testing.T) {
|
||||
rdb := test.NewDBProviderNopSQL(t)
|
||||
|
||||
// setup expectations
|
||||
req.EXPECT().DialectName().Return("test").Maybe()
|
||||
req.EXPECT().GetColNames().Return(nil).Maybe()
|
||||
req.EXPECT().Validate().Return(nil).Once()
|
||||
req.EXPECT().GetArgs().Return(nil)
|
||||
req.EXPECT().GetScanDest().Return(nil).Maybe()
|
||||
@@ -108,6 +110,8 @@ func TestQuery(t *testing.T) {
|
||||
rows := newReturnsRow(rdb.SQLMock, req)
|
||||
|
||||
// setup expectations
|
||||
req.EXPECT().DialectName().Return("test").Maybe()
|
||||
req.EXPECT().GetColNames().Return(nil).Maybe()
|
||||
req.EXPECT().Validate().Return(nil).Once()
|
||||
req.EXPECT().GetArgs().Return(nil).Once()
|
||||
rows.Add(1, nil)
|
||||
@@ -131,6 +135,8 @@ func TestQuery(t *testing.T) {
|
||||
rdb := test.NewDBProviderNopSQL(t)
|
||||
|
||||
// setup expectations
|
||||
req.EXPECT().DialectName().Return("test").Maybe()
|
||||
req.EXPECT().GetColNames().Return(nil).Maybe()
|
||||
req.EXPECT().Validate().Return(errTest).Once()
|
||||
|
||||
// execute and assert
|
||||
@@ -152,6 +158,8 @@ func TestQuery(t *testing.T) {
|
||||
req.EXPECT().Validate().Return(nil).Once()
|
||||
|
||||
// execute and assert
|
||||
req.EXPECT().DialectName().Return("test").Maybe()
|
||||
req.EXPECT().GetColNames().Return(nil).Maybe()
|
||||
res, err := Query(ctx, rdb.DB, invalidTestTmpl, req)
|
||||
require.Zero(t, res)
|
||||
require.Error(t, err)
|
||||
@@ -167,6 +175,8 @@ func TestQuery(t *testing.T) {
|
||||
rdb := test.NewDBProviderNopSQL(t)
|
||||
|
||||
// setup expectations
|
||||
req.EXPECT().DialectName().Return("test").Maybe()
|
||||
req.EXPECT().GetColNames().Return(nil).Maybe()
|
||||
req.EXPECT().Validate().Return(nil).Once()
|
||||
req.EXPECT().GetArgs().Return(nil)
|
||||
req.EXPECT().GetScanDest().Return(nil).Maybe()
|
||||
@@ -189,6 +199,8 @@ func TestQuery(t *testing.T) {
|
||||
rows := newReturnsRow(rdb.SQLMock, req)
|
||||
|
||||
// setup expectations
|
||||
req.EXPECT().DialectName().Return("test").Maybe()
|
||||
req.EXPECT().GetColNames().Return(nil).Maybe()
|
||||
req.EXPECT().Validate().Return(nil).Once()
|
||||
req.EXPECT().GetArgs().Return(nil).Once()
|
||||
rows.Add(0, errTest)
|
||||
@@ -211,6 +223,8 @@ func TestQuery(t *testing.T) {
|
||||
rows := newReturnsRow(rdb.SQLMock, req)
|
||||
|
||||
// setup expectations
|
||||
req.EXPECT().DialectName().Return("test").Maybe()
|
||||
req.EXPECT().GetColNames().Return(nil).Maybe()
|
||||
req.EXPECT().Validate().Return(nil).Once()
|
||||
req.EXPECT().GetArgs().Return(nil).Once()
|
||||
rows.Rows.AddRow() // we don't expect GetScanDest or Results here
|
||||
@@ -235,6 +249,8 @@ func TestQuery(t *testing.T) {
|
||||
rows2 := newReturnsRow(rdb.SQLMock, req)
|
||||
|
||||
// setup expectations
|
||||
req.EXPECT().DialectName().Return("test").Maybe()
|
||||
req.EXPECT().GetColNames().Return(nil).Maybe()
|
||||
req.EXPECT().Validate().Return(nil).Once()
|
||||
req.EXPECT().GetArgs().Return(nil).Once()
|
||||
rows1.Add(1, nil)
|
||||
@@ -262,6 +278,8 @@ func TestQueryRow(t *testing.T) {
|
||||
rows := newReturnsRow(rdb.SQLMock, req)
|
||||
|
||||
// setup expectations
|
||||
req.EXPECT().DialectName().Return("test").Maybe()
|
||||
req.EXPECT().GetColNames().Return(nil).Maybe()
|
||||
req.EXPECT().Validate().Return(nil).Once()
|
||||
req.EXPECT().GetArgs().Return(nil).Once()
|
||||
rows.Add(1, nil)
|
||||
@@ -282,6 +300,8 @@ func TestQueryRow(t *testing.T) {
|
||||
rdb := test.NewDBProviderNopSQL(t)
|
||||
|
||||
// setup expectations
|
||||
req.EXPECT().DialectName().Return("test").Maybe()
|
||||
req.EXPECT().GetColNames().Return(nil).Maybe()
|
||||
req.EXPECT().Validate().Return(nil).Once()
|
||||
req.EXPECT().GetArgs().Return(nil).Once()
|
||||
rdb.SQLMock.ExpectQuery("").WillReturnRows(rdb.SQLMock.NewRows(nil))
|
||||
@@ -302,6 +322,8 @@ func TestQueryRow(t *testing.T) {
|
||||
rdb := test.NewDBProviderNopSQL(t)
|
||||
|
||||
// setup expectations
|
||||
req.EXPECT().DialectName().Return("test").Maybe()
|
||||
req.EXPECT().GetColNames().Return(nil).Maybe()
|
||||
req.EXPECT().Validate().Return(nil).Once()
|
||||
req.EXPECT().GetArgs().Return(nil)
|
||||
req.EXPECT().GetScanDest().Return(nil).Maybe()
|
||||
@@ -324,6 +346,8 @@ func TestQueryRow(t *testing.T) {
|
||||
rows := newReturnsRow(rdb.SQLMock, req)
|
||||
|
||||
// setup expectations
|
||||
req.EXPECT().DialectName().Return("test").Maybe()
|
||||
req.EXPECT().GetColNames().Return(nil).Maybe()
|
||||
req.EXPECT().Validate().Return(nil).Once()
|
||||
req.EXPECT().GetArgs().Return(nil).Once()
|
||||
rows.Add(1, nil)
|
||||
@@ -348,6 +372,8 @@ func TestQueryRow(t *testing.T) {
|
||||
rows2 := newReturnsRow(rdb.SQLMock, req)
|
||||
|
||||
// setup expectations
|
||||
req.EXPECT().DialectName().Return("test").Maybe()
|
||||
req.EXPECT().GetColNames().Return(nil).Maybe()
|
||||
req.EXPECT().Validate().Return(nil).Once()
|
||||
req.EXPECT().GetArgs().Return(nil).Once()
|
||||
rows1.Add(1, nil)
|
||||
@@ -445,6 +471,8 @@ func TestExec(t *testing.T) {
|
||||
rdb := test.NewDBProviderNopSQL(t)
|
||||
|
||||
// setup expectations
|
||||
req.EXPECT().DialectName().Return("test").Maybe()
|
||||
req.EXPECT().GetColNames().Return(nil).Maybe()
|
||||
req.EXPECT().Validate().Return(nil).Once()
|
||||
req.EXPECT().GetArgs().Return(nil).Once()
|
||||
rdb.SQLMock.ExpectExec("").WillReturnResult(sqlmock.NewResult(0, 0))
|
||||
@@ -464,6 +492,8 @@ func TestExec(t *testing.T) {
|
||||
rdb := test.NewDBProviderNopSQL(t)
|
||||
|
||||
// setup expectations
|
||||
req.EXPECT().DialectName().Return("test").Maybe()
|
||||
req.EXPECT().GetColNames().Return(nil).Maybe()
|
||||
req.EXPECT().Validate().Return(errTest).Once()
|
||||
|
||||
// execute and assert
|
||||
@@ -482,6 +512,8 @@ func TestExec(t *testing.T) {
|
||||
rdb := test.NewDBProviderNopSQL(t)
|
||||
|
||||
// setup expectations
|
||||
req.EXPECT().DialectName().Return("test").Maybe()
|
||||
req.EXPECT().GetColNames().Return(nil).Maybe()
|
||||
req.EXPECT().Validate().Return(nil).Once()
|
||||
|
||||
// execute and assert
|
||||
@@ -500,6 +532,8 @@ func TestExec(t *testing.T) {
|
||||
rdb := test.NewDBProviderNopSQL(t)
|
||||
|
||||
// setup expectations
|
||||
req.EXPECT().DialectName().Return("test").Maybe()
|
||||
req.EXPECT().GetColNames().Return(nil).Maybe()
|
||||
req.EXPECT().Validate().Return(nil).Once()
|
||||
req.EXPECT().GetArgs().Return(nil)
|
||||
rdb.SQLMock.ExpectExec("").WillReturnError(errTest)
|
||||
|
||||
Reference in New Issue
Block a user