...

Source file src/github.com/cybertec-postgresql/pgwatch/v5/internal/metrics/postgres_test.go

Documentation: github.com/cybertec-postgresql/pgwatch/v5/internal/metrics

     1  package metrics_test
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  
     7  	"github.com/cybertec-postgresql/pgwatch/v5/internal/log"
     8  	"github.com/cybertec-postgresql/pgwatch/v5/internal/metrics"
     9  	"github.com/pashagolub/pgxmock/v4"
    10  	"github.com/stretchr/testify/assert"
    11  )
    12  
    13  var ctx = log.WithLogger(context.Background(), log.NewNoopLogger())
    14  
    15  func AnyArgs(n int) []any {
    16  	args := make([]any, n)
    17  	for i := range args {
    18  		args[i] = pgxmock.AnyArg()
    19  	}
    20  	return args
    21  }
    22  
    23  func TestNewPostgresMetricReaderWriter(t *testing.T) {
    24  	a := assert.New(t)
    25  
    26  	t.Run("ConnectionError", func(*testing.T) {
    27  		pgrw, err := metrics.NewPostgresMetricReaderWriter(ctx, "postgres://user:pass@foohost:5432/db1")
    28  		a.Error(err)
    29  		a.Nil(pgrw)
    30  	})
    31  	t.Run("InvalidConnStr", func(*testing.T) {
    32  		pgrw, err := metrics.NewPostgresMetricReaderWriter(ctx, "invalid_connstr")
    33  		a.Error(err)
    34  		a.Nil(pgrw)
    35  	})
    36  }
    37  
    38  func TestNewPostgresMetricReaderWriterConn(t *testing.T) {
    39  	df := metrics.GetDefaultMetrics()
    40  	metricsCount := len(df.MetricDefs)
    41  	presetsCount := len(df.PresetDefs)
    42  
    43  	a := assert.New(t)
    44  	conn, err := pgxmock.NewPool()
    45  	a.NoError(err)
    46  
    47  	doesntExist := func() *pgxmock.Rows { return pgxmock.NewRows([]string{"exists"}).AddRow(false) }
    48  
    49  	t.Run("FullBoostrap", func(*testing.T) {
    50  		conn.ExpectQuery(`SELECT EXISTS`).WithArgs("pgwatch").WillReturnRows(doesntExist())
    51  		conn.ExpectBegin()
    52  		conn.ExpectExec("CREATE SCHEMA IF NOT EXISTS pgwatch").WillReturnResult(pgxmock.NewResult("CREATE", 1))
    53  		conn.ExpectBegin()
    54  		conn.ExpectExec(`INSERT.+metric`).WithArgs(AnyArgs(8)...).WillReturnResult(pgxmock.NewResult("INSERT", 1)).Times(uint(metricsCount))
    55  		conn.ExpectExec(`INSERT.+preset`).WithArgs(AnyArgs(3)...).WillReturnResult(pgxmock.NewResult("INSERT", 1)).Times(uint(presetsCount))
    56  		conn.ExpectCommit()
    57  		conn.ExpectCommit()
    58  		conn.ExpectPing()
    59  
    60  		readerWriter, err := metrics.NewPostgresMetricReaderWriterConn(ctx, conn)
    61  		a.NoError(err)
    62  		a.NotNil(readerWriter)
    63  		a.NoError(conn.ExpectationsWereMet())
    64  	})
    65  
    66  	t.Run("SchemaQueryFail", func(*testing.T) {
    67  		conn.ExpectQuery(`SELECT EXISTS`).WithArgs("pgwatch").WillReturnError(assert.AnError)
    68  		rw, err := metrics.NewPostgresMetricReaderWriterConn(ctx, conn)
    69  		a.Error(err)
    70  		a.Nil(rw)
    71  		a.NoError(conn.ExpectationsWereMet())
    72  	})
    73  
    74  	t.Run("BeginFail", func(*testing.T) {
    75  		conn.ExpectQuery(`SELECT EXISTS`).WithArgs("pgwatch").WillReturnRows(doesntExist())
    76  		conn.ExpectBegin().WillReturnError(assert.AnError)
    77  		rw, err := metrics.NewPostgresMetricReaderWriterConn(ctx, conn)
    78  		a.Error(err)
    79  		a.Nil(rw)
    80  		a.NoError(conn.ExpectationsWereMet())
    81  	})
    82  
    83  	t.Run("CreateSchemaFail", func(*testing.T) {
    84  		conn.ExpectQuery(`SELECT EXISTS`).WithArgs("pgwatch").WillReturnRows(doesntExist())
    85  		conn.ExpectBegin()
    86  		conn.ExpectExec("CREATE SCHEMA IF NOT EXISTS pgwatch").WillReturnError(assert.AnError)
    87  		conn.ExpectRollback()
    88  		rw, err := metrics.NewPostgresMetricReaderWriterConn(ctx, conn)
    89  		a.Error(err)
    90  		a.Nil(rw)
    91  		a.NoError(conn.ExpectationsWereMet())
    92  	})
    93  
    94  	t.Run("WriteDefaultMetricsBeginFail", func(*testing.T) {
    95  		conn.ExpectQuery(`SELECT EXISTS`).WithArgs("pgwatch").WillReturnRows(doesntExist())
    96  		conn.ExpectBegin()
    97  		conn.ExpectExec("CREATE SCHEMA IF NOT EXISTS pgwatch").WillReturnResult(pgxmock.NewResult("CREATE", 1))
    98  		conn.ExpectBegin().WillReturnError(assert.AnError)
    99  		conn.ExpectRollback()
   100  		rw, err := metrics.NewPostgresMetricReaderWriterConn(ctx, conn)
   101  		a.Error(err)
   102  		a.Nil(rw)
   103  		a.NoError(conn.ExpectationsWereMet())
   104  	})
   105  
   106  	t.Run("WriteInsertMetricsFail", func(*testing.T) {
   107  		conn.ExpectQuery(`SELECT EXISTS`).WithArgs("pgwatch").WillReturnRows(doesntExist())
   108  		conn.ExpectBegin()
   109  		conn.ExpectExec("CREATE SCHEMA IF NOT EXISTS pgwatch").WillReturnResult(pgxmock.NewResult("CREATE", 1))
   110  		conn.ExpectBegin()
   111  		conn.ExpectExec(`INSERT.+metric`).WithArgs(AnyArgs(8)...).WillReturnError(assert.AnError)
   112  		conn.ExpectRollback()
   113  		rw, err := metrics.NewPostgresMetricReaderWriterConn(ctx, conn)
   114  		a.Error(err)
   115  		a.Nil(rw)
   116  		a.NoError(conn.ExpectationsWereMet())
   117  	})
   118  
   119  	t.Run("WriteInsertPresetsFail", func(*testing.T) {
   120  		conn.ExpectQuery(`SELECT EXISTS`).WithArgs("pgwatch").WillReturnRows(doesntExist())
   121  		conn.ExpectBegin()
   122  		conn.ExpectExec("CREATE SCHEMA IF NOT EXISTS pgwatch").WillReturnResult(pgxmock.NewResult("CREATE", 1))
   123  		conn.ExpectBegin()
   124  		conn.ExpectExec(`INSERT.+metric`).WithArgs(AnyArgs(8)...).WillReturnResult(pgxmock.NewResult("INSERT", 1)).Times(uint(metricsCount))
   125  		conn.ExpectExec(`INSERT.+preset`).WithArgs(AnyArgs(3)...).WillReturnError(assert.AnError)
   126  		conn.ExpectRollback()
   127  		rw, err := metrics.NewPostgresMetricReaderWriterConn(ctx, conn)
   128  		a.Error(err)
   129  		a.Nil(rw)
   130  		a.NoError(conn.ExpectationsWereMet())
   131  	})
   132  
   133  	t.Run("DefaultMetricsCommitFail", func(*testing.T) {
   134  		conn.ExpectQuery(`SELECT EXISTS`).WithArgs("pgwatch").WillReturnRows(doesntExist())
   135  		conn.ExpectBegin()
   136  		conn.ExpectExec("CREATE SCHEMA IF NOT EXISTS pgwatch").WillReturnResult(pgxmock.NewResult("CREATE", 1))
   137  		conn.ExpectBegin()
   138  		conn.ExpectExec(`INSERT.+metric`).WithArgs(AnyArgs(8)...).WillReturnResult(pgxmock.NewResult("INSERT", 1)).Times(uint(metricsCount))
   139  		conn.ExpectExec(`INSERT.+preset`).WithArgs(AnyArgs(3)...).WillReturnResult(pgxmock.NewResult("INSERT", 1)).Times(uint(presetsCount))
   140  		conn.ExpectCommit().WillReturnError(assert.AnError)
   141  		conn.ExpectRollback()
   142  		rw, err := metrics.NewPostgresMetricReaderWriterConn(ctx, conn)
   143  		a.Error(err)
   144  		a.Nil(rw)
   145  		a.NoError(conn.ExpectationsWereMet())
   146  	})
   147  
   148  	t.Run("CommitFail", func(*testing.T) {
   149  		conn.ExpectQuery(`SELECT EXISTS`).WithArgs("pgwatch").WillReturnRows(doesntExist())
   150  		conn.ExpectBegin()
   151  		conn.ExpectExec("CREATE SCHEMA IF NOT EXISTS pgwatch").WillReturnResult(pgxmock.NewResult("CREATE", 1))
   152  		conn.ExpectBegin()
   153  		conn.ExpectExec(`INSERT.+metric`).WithArgs(AnyArgs(8)...).WillReturnResult(pgxmock.NewResult("INSERT", 1)).Times(uint(metricsCount))
   154  		conn.ExpectExec(`INSERT.+preset`).WithArgs(AnyArgs(3)...).WillReturnResult(pgxmock.NewResult("INSERT", 1)).Times(uint(presetsCount))
   155  		conn.ExpectCommit()
   156  		conn.ExpectCommit().WillReturnError(assert.AnError)
   157  		rw, err := metrics.NewPostgresMetricReaderWriterConn(ctx, conn)
   158  		a.Error(err)
   159  		a.Nil(rw)
   160  		a.NoError(conn.ExpectationsWereMet())
   161  	})
   162  
   163  	t.Run("SchemaExists", func(*testing.T) {
   164  		conn.ExpectQuery(`SELECT EXISTS`).WithArgs("pgwatch").WillReturnRows(pgxmock.NewRows([]string{"exists"}).AddRow(true))
   165  		conn.ExpectPing()
   166  		rw, err := metrics.NewPostgresMetricReaderWriterConn(ctx, conn)
   167  		a.NoError(err)
   168  		a.NotNil(rw)
   169  		a.NoError(conn.ExpectationsWereMet())
   170  	})
   171  }
   172  
   173  func TestMetricsToPostgres(t *testing.T) {
   174  	a := assert.New(t)
   175  	conn, err := pgxmock.NewPool()
   176  	a.NoError(err)
   177  
   178  	conn.ExpectQuery(`SELECT EXISTS`).WithArgs("pgwatch").WillReturnRows(pgxmock.NewRows([]string{"exists"}).AddRow(true))
   179  	conn.ExpectPing()
   180  
   181  	readerWriter, err := metrics.NewPostgresMetricReaderWriterConn(ctx, conn)
   182  	a.NoError(err)
   183  	a.NotNil(readerWriter)
   184  
   185  	metricsRows := func() *pgxmock.Rows {
   186  		return pgxmock.NewRows([]string{"name", "sqls", "init_sql", "description", "node_status", "gauges", "is_instance_level", "storage_name"}).
   187  			AddRow("test", metrics.SQLs{11: "select"}, "init", "desc", "primary", []string{"*"}, true, "storage")
   188  	}
   189  	presetRows := func() *pgxmock.Rows {
   190  		return pgxmock.NewRows([]string{"name", "description", "metrics"}).
   191  			AddRow("test", "desc", map[string]float64{"metric": 30})
   192  	}
   193  
   194  	t.Run("GetMetrics", func(*testing.T) {
   195  		conn.ExpectQuery(`SELECT.+FROM.+metric`).WillReturnRows(metricsRows())
   196  		conn.ExpectQuery(`SELECT.+FROM.+preset`).WillReturnRows(presetRows())
   197  
   198  		m, err := readerWriter.GetMetrics()
   199  		a.NoError(err)
   200  		a.Len(m.MetricDefs, 1)
   201  	})
   202  
   203  	t.Run("GetMetricsFail", func(*testing.T) {
   204  		conn.ExpectQuery(`SELECT.+FROM.+metric`).WillReturnError(assert.AnError)
   205  		_, err = readerWriter.GetMetrics()
   206  		a.Error(err)
   207  	})
   208  
   209  	t.Run("GetPresetsFail", func(*testing.T) {
   210  		conn.ExpectQuery(`SELECT.+FROM.+metric`).WillReturnRows(metricsRows())
   211  		conn.ExpectQuery(`SELECT.+FROM.+preset`).WillReturnError(assert.AnError)
   212  		_, err = readerWriter.GetMetrics()
   213  		a.Error(err)
   214  	})
   215  
   216  	t.Run("GetMetricsScanFail", func(*testing.T) {
   217  		conn.ExpectQuery(`SELECT.+FROM.+metric`).WillReturnRows(metricsRows().RowError(0, assert.AnError))
   218  		_, err = readerWriter.GetMetrics()
   219  		a.Error(err)
   220  	})
   221  
   222  	t.Run("GetPresetsScanFail", func(*testing.T) {
   223  		conn.ExpectQuery(`SELECT.+FROM.+metric`).WillReturnRows(metricsRows())
   224  		conn.ExpectQuery(`SELECT.+FROM.+preset`).WillReturnRows(presetRows().RowError(0, assert.AnError))
   225  		_, err = readerWriter.GetMetrics()
   226  		a.Error(err)
   227  	})
   228  
   229  	t.Run("WriteMetrics", func(*testing.T) {
   230  		conn.ExpectBegin().WillReturnError(assert.AnError)
   231  		err = readerWriter.WriteMetrics(&metrics.Metrics{})
   232  		a.Error(err)
   233  	})
   234  
   235  	t.Run("DeleteMetric", func(*testing.T) {
   236  		conn.ExpectExec(`DELETE.+metric`).WithArgs("test").WillReturnResult(pgxmock.NewResult("DELETE", 1))
   237  		err = readerWriter.DeleteMetric("test")
   238  		a.NoError(err)
   239  	})
   240  
   241  	t.Run("UpdateMetric", func(*testing.T) {
   242  		conn.ExpectExec(`INSERT.+metric`).WithArgs(AnyArgs(8)...).WillReturnResult(pgxmock.NewResult("UPDATE", 1))
   243  		err = readerWriter.UpdateMetric("test", metrics.Metric{})
   244  		a.NoError(err)
   245  	})
   246  
   247  	t.Run("FailUpdateMetric", func(*testing.T) {
   248  		conn.ExpectExec(`INSERT.+metric`).WithArgs(AnyArgs(8)...).WillReturnResult(pgxmock.NewResult("UPDATE", 0))
   249  		err = readerWriter.UpdateMetric("test", metrics.Metric{})
   250  		a.ErrorIs(err, metrics.ErrMetricNotFound)
   251  	})
   252  
   253  	t.Run("DeletePreset", func(*testing.T) {
   254  		conn.ExpectExec(`DELETE.+preset`).WithArgs("test").WillReturnResult(pgxmock.NewResult("DELETE", 1))
   255  		err = readerWriter.DeletePreset("test")
   256  		a.NoError(err)
   257  	})
   258  
   259  	t.Run("UpdatePreset", func(*testing.T) {
   260  		conn.ExpectExec(`INSERT.+preset`).WithArgs(AnyArgs(3)...).WillReturnResult(pgxmock.NewResult("INSERT", 1))
   261  		err = readerWriter.UpdatePreset("test", metrics.Preset{})
   262  		a.NoError(err)
   263  	})
   264  
   265  	t.Run("FailUpdatePreset", func(*testing.T) {
   266  		conn.ExpectExec(`INSERT.+preset`).WithArgs(AnyArgs(3)...).WillReturnResult(pgxmock.NewResult("INSERT", 0))
   267  		err = readerWriter.UpdatePreset("test", metrics.Preset{})
   268  		a.ErrorIs(err, metrics.ErrPresetNotFound)
   269  	})
   270  
   271  	// check all expectations were met
   272  	a.NoError(conn.ExpectationsWereMet())
   273  }
   274  
   275  // Additional tests for GetMetrics, WriteMetrics, DeleteMetric, UpdateMetric, DeletePreset, and UpdatePreset follow a similar pattern.
   276