github.com/netdata/go.d.plugin@v0.58.1/modules/postgres/postgres_test.go (about)

     1  // SPDX-License-Identifier: GPL-3.0-or-later
     2  
     3  package postgres
     4  
     5  import (
     6  	"bufio"
     7  	"bytes"
     8  	"database/sql/driver"
     9  	"errors"
    10  	"fmt"
    11  	"os"
    12  	"strings"
    13  	"testing"
    14  
    15  	"github.com/netdata/go.d.plugin/pkg/matcher"
    16  
    17  	"github.com/DATA-DOG/go-sqlmock"
    18  	"github.com/stretchr/testify/assert"
    19  	"github.com/stretchr/testify/require"
    20  )
    21  
    22  var (
    23  	dataV140004ServerVersionNum, _ = os.ReadFile("testdata/v14.4/server_version_num.txt")
    24  
    25  	dataV140004IsSuperUserFalse, _       = os.ReadFile("testdata/v14.4/is_super_user-false.txt")
    26  	dataV140004IsSuperUserTrue, _        = os.ReadFile("testdata/v14.4/is_super_user-true.txt")
    27  	dataV140004PGIsInRecoveryTrue, _     = os.ReadFile("testdata/v14.4/pg_is_in_recovery-true.txt")
    28  	dataV140004SettingsMaxConnections, _ = os.ReadFile("testdata/v14.4/settings_max_connections.txt")
    29  	dataV140004SettingsMaxLocksHeld, _   = os.ReadFile("testdata/v14.4/settings_max_locks_held.txt")
    30  
    31  	dataV140004ServerCurrentConnections, _ = os.ReadFile("testdata/v14.4/server_current_connections.txt")
    32  	dataV140004ServerConnectionsState, _   = os.ReadFile("testdata/v14.4/server_connections_state.txt")
    33  	dataV140004Checkpoints, _              = os.ReadFile("testdata/v14.4/checkpoints.txt")
    34  	dataV140004ServerUptime, _             = os.ReadFile("testdata/v14.4/uptime.txt")
    35  	dataV140004TXIDWraparound, _           = os.ReadFile("testdata/v14.4/txid_wraparound.txt")
    36  	dataV140004WALWrites, _                = os.ReadFile("testdata/v14.4/wal_writes.txt")
    37  	dataV140004WALFiles, _                 = os.ReadFile("testdata/v14.4/wal_files.txt")
    38  	dataV140004WALArchiveFiles, _          = os.ReadFile("testdata/v14.4/wal_archive_files.txt")
    39  	dataV140004CatalogRelations, _         = os.ReadFile("testdata/v14.4/catalog_relations.txt")
    40  	dataV140004AutovacuumWorkers, _        = os.ReadFile("testdata/v14.4/autovacuum_workers.txt")
    41  	dataV140004XactQueryRunningTime, _     = os.ReadFile("testdata/v14.4/xact_query_running_time.txt")
    42  
    43  	dataV140004ReplStandbyAppDelta, _ = os.ReadFile("testdata/v14.4/replication_standby_app_wal_delta.txt")
    44  	dataV140004ReplStandbyAppLag, _   = os.ReadFile("testdata/v14.4/replication_standby_app_wal_lag.txt")
    45  
    46  	dataV140004ReplSlotFiles, _ = os.ReadFile("testdata/v14.4/replication_slot_files.txt")
    47  
    48  	dataV140004DatabaseStats, _     = os.ReadFile("testdata/v14.4/database_stats.txt")
    49  	dataV140004DatabaseSize, _      = os.ReadFile("testdata/v14.4/database_size.txt")
    50  	dataV140004DatabaseConflicts, _ = os.ReadFile("testdata/v14.4/database_conflicts.txt")
    51  	dataV140004DatabaseLocks, _     = os.ReadFile("testdata/v14.4/database_locks.txt")
    52  
    53  	dataV140004QueryableDatabaseList, _ = os.ReadFile("testdata/v14.4/queryable_database_list.txt")
    54  
    55  	dataV140004StatUserTablesDBPostgres, _   = os.ReadFile("testdata/v14.4/stat_user_tables_db_postgres.txt")
    56  	dataV140004StatIOUserTablesDBPostgres, _ = os.ReadFile("testdata/v14.4/statio_user_tables_db_postgres.txt")
    57  
    58  	dataV140004StatUserIndexesDBPostgres, _ = os.ReadFile("testdata/v14.4/stat_user_indexes_db_postgres.txt")
    59  
    60  	dataV140004Bloat, _        = os.ReadFile("testdata/v14.4/bloat_tables.txt")
    61  	dataV140004ColumnsStats, _ = os.ReadFile("testdata/v14.4/table_columns_stats.txt")
    62  )
    63  
    64  func Test_testDataIsValid(t *testing.T) {
    65  	for name, data := range map[string][]byte{
    66  		"dataV140004ServerVersionNum": dataV140004ServerVersionNum,
    67  
    68  		"dataV140004IsSuperUserFalse":       dataV140004IsSuperUserFalse,
    69  		"dataV140004IsSuperUserTrue":        dataV140004IsSuperUserTrue,
    70  		"dataV140004PGIsInRecoveryTrue":     dataV140004PGIsInRecoveryTrue,
    71  		"dataV140004SettingsMaxConnections": dataV140004SettingsMaxConnections,
    72  		"dataV140004SettingsMaxLocksHeld":   dataV140004SettingsMaxLocksHeld,
    73  
    74  		"dataV140004ServerCurrentConnections": dataV140004ServerCurrentConnections,
    75  		"dataV140004ServerConnectionsState":   dataV140004ServerConnectionsState,
    76  		"dataV140004Checkpoints":              dataV140004Checkpoints,
    77  		"dataV140004ServerUptime":             dataV140004ServerUptime,
    78  		"dataV140004TXIDWraparound":           dataV140004TXIDWraparound,
    79  		"dataV140004WALWrites":                dataV140004WALWrites,
    80  		"dataV140004WALFiles":                 dataV140004WALFiles,
    81  		"dataV140004WALArchiveFiles":          dataV140004WALArchiveFiles,
    82  		"dataV140004CatalogRelations":         dataV140004CatalogRelations,
    83  		"dataV140004AutovacuumWorkers":        dataV140004AutovacuumWorkers,
    84  		"dataV140004XactQueryRunningTime":     dataV140004XactQueryRunningTime,
    85  
    86  		"dataV14004ReplStandbyAppDelta": dataV140004ReplStandbyAppDelta,
    87  		"dataV14004ReplStandbyAppLag":   dataV140004ReplStandbyAppLag,
    88  
    89  		"dataV140004ReplSlotFiles": dataV140004ReplSlotFiles,
    90  
    91  		"dataV140004DatabaseStats":     dataV140004DatabaseStats,
    92  		"dataV140004DatabaseSize":      dataV140004DatabaseSize,
    93  		"dataV140004DatabaseConflicts": dataV140004DatabaseConflicts,
    94  		"dataV140004DatabaseLocks":     dataV140004DatabaseLocks,
    95  
    96  		"dataV140004QueryableDatabaseList": dataV140004QueryableDatabaseList,
    97  
    98  		"dataV140004StatUserTablesDBPostgres":   dataV140004StatUserTablesDBPostgres,
    99  		"dataV140004StatIOUserTablesDBPostgres": dataV140004StatIOUserTablesDBPostgres,
   100  
   101  		"dataV140004StatUserIndexesDBPostgres": dataV140004StatUserIndexesDBPostgres,
   102  
   103  		"dataV140004Bloat":        dataV140004Bloat,
   104  		"dataV140004ColumnsStats": dataV140004ColumnsStats,
   105  	} {
   106  		require.NotNilf(t, data, name)
   107  	}
   108  }
   109  
   110  func TestPostgres_Init(t *testing.T) {
   111  	tests := map[string]struct {
   112  		wantFail bool
   113  		config   Config
   114  	}{
   115  		"Success with default": {
   116  			wantFail: false,
   117  			config:   New().Config,
   118  		},
   119  		"Fail when DSN not set": {
   120  			wantFail: true,
   121  			config:   Config{DSN: ""},
   122  		},
   123  	}
   124  
   125  	for name, test := range tests {
   126  		t.Run(name, func(t *testing.T) {
   127  			pg := New()
   128  			pg.Config = test.config
   129  
   130  			if test.wantFail {
   131  				assert.False(t, pg.Init())
   132  			} else {
   133  				assert.True(t, pg.Init())
   134  			}
   135  		})
   136  	}
   137  }
   138  
   139  func TestPostgres_Cleanup(t *testing.T) {
   140  
   141  }
   142  
   143  func TestPostgres_Charts(t *testing.T) {
   144  	assert.NotNil(t, New().Charts())
   145  }
   146  
   147  func TestPostgres_Check(t *testing.T) {
   148  	tests := map[string]struct {
   149  		prepareMock func(t *testing.T, pg *Postgres, mock sqlmock.Sqlmock)
   150  		wantFail    bool
   151  	}{
   152  		"Success when all queries are successful (v14.4)": {
   153  			wantFail: false,
   154  			prepareMock: func(t *testing.T, pg *Postgres, m sqlmock.Sqlmock) {
   155  				pg.dbSr = matcher.TRUE()
   156  
   157  				mockExpect(t, m, queryServerVersion(), dataV140004ServerVersionNum)
   158  				mockExpect(t, m, queryIsSuperUser(), dataV140004IsSuperUserTrue)
   159  				mockExpect(t, m, queryPGIsInRecovery(), dataV140004PGIsInRecoveryTrue)
   160  
   161  				mockExpect(t, m, querySettingsMaxConnections(), dataV140004SettingsMaxConnections)
   162  				mockExpect(t, m, querySettingsMaxLocksHeld(), dataV140004SettingsMaxLocksHeld)
   163  
   164  				mockExpect(t, m, queryServerCurrentConnectionsUsed(), dataV140004ServerCurrentConnections)
   165  				mockExpect(t, m, queryServerConnectionsState(), dataV140004ServerConnectionsState)
   166  				mockExpect(t, m, queryCheckpoints(), dataV140004Checkpoints)
   167  				mockExpect(t, m, queryServerUptime(), dataV140004ServerUptime)
   168  				mockExpect(t, m, queryTXIDWraparound(), dataV140004TXIDWraparound)
   169  				mockExpect(t, m, queryWALWrites(140004), dataV140004WALWrites)
   170  				mockExpect(t, m, queryCatalogRelations(), dataV140004CatalogRelations)
   171  				mockExpect(t, m, queryAutovacuumWorkers(), dataV140004AutovacuumWorkers)
   172  				mockExpect(t, m, queryXactQueryRunningTime(), dataV140004XactQueryRunningTime)
   173  
   174  				mockExpect(t, m, queryWALFiles(140004), dataV140004WALFiles)
   175  				mockExpect(t, m, queryWALArchiveFiles(140004), dataV140004WALArchiveFiles)
   176  
   177  				mockExpect(t, m, queryReplicationStandbyAppDelta(140004), dataV140004ReplStandbyAppDelta)
   178  				mockExpect(t, m, queryReplicationStandbyAppLag(), dataV140004ReplStandbyAppLag)
   179  				mockExpect(t, m, queryReplicationSlotFiles(140004), dataV140004ReplSlotFiles)
   180  
   181  				mockExpect(t, m, queryDatabaseStats(), dataV140004DatabaseStats)
   182  				mockExpect(t, m, queryDatabaseSize(140004), dataV140004DatabaseSize)
   183  				mockExpect(t, m, queryDatabaseConflicts(), dataV140004DatabaseConflicts)
   184  				mockExpect(t, m, queryDatabaseLocks(), dataV140004DatabaseLocks)
   185  
   186  				mockExpect(t, m, queryQueryableDatabaseList(), dataV140004QueryableDatabaseList)
   187  				mockExpect(t, m, queryStatUserTables(), dataV140004StatUserTablesDBPostgres)
   188  				mockExpect(t, m, queryStatIOUserTables(), dataV140004StatIOUserTablesDBPostgres)
   189  				mockExpect(t, m, queryStatUserIndexes(), dataV140004StatUserIndexesDBPostgres)
   190  				mockExpect(t, m, queryBloat(), dataV140004Bloat)
   191  				mockExpect(t, m, queryColumnsStats(), dataV140004ColumnsStats)
   192  			},
   193  		},
   194  		"Fail when the second query unsuccessful (v14.4)": {
   195  			wantFail: true,
   196  			prepareMock: func(t *testing.T, pg *Postgres, m sqlmock.Sqlmock) {
   197  				mockExpect(t, m, queryServerVersion(), dataV140004ServerVersionNum)
   198  				mockExpect(t, m, queryIsSuperUser(), dataV140004IsSuperUserTrue)
   199  				mockExpect(t, m, queryPGIsInRecovery(), dataV140004PGIsInRecoveryTrue)
   200  
   201  				mockExpect(t, m, querySettingsMaxConnections(), dataV140004ServerVersionNum)
   202  				mockExpect(t, m, querySettingsMaxLocksHeld(), dataV140004SettingsMaxLocksHeld)
   203  
   204  				mockExpect(t, m, queryServerCurrentConnectionsUsed(), dataV140004ServerCurrentConnections)
   205  				mockExpectErr(m, queryServerConnectionsState())
   206  			},
   207  		},
   208  		"Fail when querying the database version returns an error": {
   209  			wantFail: true,
   210  			prepareMock: func(t *testing.T, pg *Postgres, m sqlmock.Sqlmock) {
   211  				mockExpectErr(m, queryServerVersion())
   212  			},
   213  		},
   214  		"Fail when querying settings max connection returns an error": {
   215  			wantFail: true,
   216  			prepareMock: func(t *testing.T, pg *Postgres, m sqlmock.Sqlmock) {
   217  				mockExpect(t, m, queryServerVersion(), dataV140004ServerVersionNum)
   218  				mockExpect(t, m, queryIsSuperUser(), dataV140004IsSuperUserTrue)
   219  				mockExpect(t, m, queryPGIsInRecovery(), dataV140004PGIsInRecoveryTrue)
   220  
   221  				mockExpectErr(m, querySettingsMaxConnections())
   222  			},
   223  		},
   224  	}
   225  
   226  	for name, test := range tests {
   227  		t.Run(name, func(t *testing.T) {
   228  			db, mock, err := sqlmock.New(
   229  				sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual),
   230  			)
   231  			require.NoError(t, err)
   232  			pg := New()
   233  			pg.db = db
   234  			defer func() { _ = db.Close() }()
   235  
   236  			require.True(t, pg.Init())
   237  
   238  			test.prepareMock(t, pg, mock)
   239  
   240  			if test.wantFail {
   241  				assert.False(t, pg.Check())
   242  			} else {
   243  				assert.True(t, pg.Check())
   244  			}
   245  			assert.NoError(t, mock.ExpectationsWereMet())
   246  		})
   247  	}
   248  }
   249  
   250  func TestPostgres_Collect(t *testing.T) {
   251  	type testCaseStep struct {
   252  		prepareMock func(t *testing.T, pg *Postgres, mock sqlmock.Sqlmock)
   253  		check       func(t *testing.T, pg *Postgres)
   254  	}
   255  	tests := map[string][]testCaseStep{
   256  		"Success on all queries, collect all dbs (v14.4)": {
   257  			{
   258  				prepareMock: func(t *testing.T, pg *Postgres, m sqlmock.Sqlmock) {
   259  					pg.dbSr = matcher.TRUE()
   260  					mockExpect(t, m, queryServerVersion(), dataV140004ServerVersionNum)
   261  					mockExpect(t, m, queryIsSuperUser(), dataV140004IsSuperUserTrue)
   262  					mockExpect(t, m, queryPGIsInRecovery(), dataV140004PGIsInRecoveryTrue)
   263  
   264  					mockExpect(t, m, querySettingsMaxConnections(), dataV140004SettingsMaxConnections)
   265  					mockExpect(t, m, querySettingsMaxLocksHeld(), dataV140004SettingsMaxLocksHeld)
   266  
   267  					mockExpect(t, m, queryServerCurrentConnectionsUsed(), dataV140004ServerCurrentConnections)
   268  					mockExpect(t, m, queryServerConnectionsState(), dataV140004ServerConnectionsState)
   269  					mockExpect(t, m, queryCheckpoints(), dataV140004Checkpoints)
   270  					mockExpect(t, m, queryServerUptime(), dataV140004ServerUptime)
   271  					mockExpect(t, m, queryTXIDWraparound(), dataV140004TXIDWraparound)
   272  					mockExpect(t, m, queryWALWrites(140004), dataV140004WALWrites)
   273  					mockExpect(t, m, queryCatalogRelations(), dataV140004CatalogRelations)
   274  					mockExpect(t, m, queryAutovacuumWorkers(), dataV140004AutovacuumWorkers)
   275  					mockExpect(t, m, queryXactQueryRunningTime(), dataV140004XactQueryRunningTime)
   276  
   277  					mockExpect(t, m, queryWALFiles(140004), dataV140004WALFiles)
   278  					mockExpect(t, m, queryWALArchiveFiles(140004), dataV140004WALArchiveFiles)
   279  
   280  					mockExpect(t, m, queryReplicationStandbyAppDelta(140004), dataV140004ReplStandbyAppDelta)
   281  					mockExpect(t, m, queryReplicationStandbyAppLag(), dataV140004ReplStandbyAppLag)
   282  					mockExpect(t, m, queryReplicationSlotFiles(140004), dataV140004ReplSlotFiles)
   283  
   284  					mockExpect(t, m, queryDatabaseStats(), dataV140004DatabaseStats)
   285  					mockExpect(t, m, queryDatabaseSize(140004), dataV140004DatabaseSize)
   286  					mockExpect(t, m, queryDatabaseConflicts(), dataV140004DatabaseConflicts)
   287  					mockExpect(t, m, queryDatabaseLocks(), dataV140004DatabaseLocks)
   288  
   289  					mockExpect(t, m, queryQueryableDatabaseList(), dataV140004QueryableDatabaseList)
   290  					mockExpect(t, m, queryStatUserTables(), dataV140004StatUserTablesDBPostgres)
   291  					mockExpect(t, m, queryStatIOUserTables(), dataV140004StatIOUserTablesDBPostgres)
   292  					mockExpect(t, m, queryStatUserIndexes(), dataV140004StatUserIndexesDBPostgres)
   293  					mockExpect(t, m, queryBloat(), dataV140004Bloat)
   294  					mockExpect(t, m, queryColumnsStats(), dataV140004ColumnsStats)
   295  				},
   296  				check: func(t *testing.T, pg *Postgres) {
   297  					mx := pg.Collect()
   298  
   299  					expected := map[string]int64{
   300  						"autovacuum_analyze":                                       0,
   301  						"autovacuum_brin_summarize":                                0,
   302  						"autovacuum_vacuum":                                        0,
   303  						"autovacuum_vacuum_analyze":                                0,
   304  						"autovacuum_vacuum_freeze":                                 0,
   305  						"buffers_alloc":                                            27295744,
   306  						"buffers_backend":                                          0,
   307  						"buffers_backend_fsync":                                    0,
   308  						"buffers_checkpoint":                                       32768,
   309  						"buffers_clean":                                            0,
   310  						"catalog_relkind_I_count":                                  0,
   311  						"catalog_relkind_I_size":                                   0,
   312  						"catalog_relkind_S_count":                                  0,
   313  						"catalog_relkind_S_size":                                   0,
   314  						"catalog_relkind_c_count":                                  0,
   315  						"catalog_relkind_c_size":                                   0,
   316  						"catalog_relkind_f_count":                                  0,
   317  						"catalog_relkind_f_size":                                   0,
   318  						"catalog_relkind_i_count":                                  155,
   319  						"catalog_relkind_i_size":                                   3678208,
   320  						"catalog_relkind_m_count":                                  0,
   321  						"catalog_relkind_m_size":                                   0,
   322  						"catalog_relkind_p_count":                                  0,
   323  						"catalog_relkind_p_size":                                   0,
   324  						"catalog_relkind_r_count":                                  66,
   325  						"catalog_relkind_r_size":                                   3424256,
   326  						"catalog_relkind_t_count":                                  38,
   327  						"catalog_relkind_t_size":                                   548864,
   328  						"catalog_relkind_v_count":                                  137,
   329  						"catalog_relkind_v_size":                                   0,
   330  						"checkpoint_sync_time":                                     47,
   331  						"checkpoint_write_time":                                    167,
   332  						"checkpoints_req":                                          16,
   333  						"checkpoints_timed":                                        1814,
   334  						"databases_count":                                          2,
   335  						"db_postgres_blks_hit":                                     1221125,
   336  						"db_postgres_blks_read":                                    3252,
   337  						"db_postgres_blks_read_perc":                               0,
   338  						"db_postgres_confl_bufferpin":                              0,
   339  						"db_postgres_confl_deadlock":                               0,
   340  						"db_postgres_confl_lock":                                   0,
   341  						"db_postgres_confl_snapshot":                               0,
   342  						"db_postgres_confl_tablespace":                             0,
   343  						"db_postgres_conflicts":                                    0,
   344  						"db_postgres_deadlocks":                                    0,
   345  						"db_postgres_lock_mode_AccessExclusiveLock_awaited":        0,
   346  						"db_postgres_lock_mode_AccessExclusiveLock_held":           0,
   347  						"db_postgres_lock_mode_AccessShareLock_awaited":            0,
   348  						"db_postgres_lock_mode_AccessShareLock_held":               99,
   349  						"db_postgres_lock_mode_ExclusiveLock_awaited":              0,
   350  						"db_postgres_lock_mode_ExclusiveLock_held":                 0,
   351  						"db_postgres_lock_mode_RowExclusiveLock_awaited":           0,
   352  						"db_postgres_lock_mode_RowExclusiveLock_held":              99,
   353  						"db_postgres_lock_mode_RowShareLock_awaited":               0,
   354  						"db_postgres_lock_mode_RowShareLock_held":                  99,
   355  						"db_postgres_lock_mode_ShareLock_awaited":                  0,
   356  						"db_postgres_lock_mode_ShareLock_held":                     0,
   357  						"db_postgres_lock_mode_ShareRowExclusiveLock_awaited":      0,
   358  						"db_postgres_lock_mode_ShareRowExclusiveLock_held":         0,
   359  						"db_postgres_lock_mode_ShareUpdateExclusiveLock_awaited":   0,
   360  						"db_postgres_lock_mode_ShareUpdateExclusiveLock_held":      0,
   361  						"db_postgres_numbackends":                                  3,
   362  						"db_postgres_numbackends_utilization":                      10,
   363  						"db_postgres_size":                                         8758051,
   364  						"db_postgres_temp_bytes":                                   0,
   365  						"db_postgres_temp_files":                                   0,
   366  						"db_postgres_tup_deleted":                                  0,
   367  						"db_postgres_tup_fetched":                                  359833,
   368  						"db_postgres_tup_fetched_perc":                             2,
   369  						"db_postgres_tup_inserted":                                 0,
   370  						"db_postgres_tup_returned":                                 13207245,
   371  						"db_postgres_tup_updated":                                  0,
   372  						"db_postgres_xact_commit":                                  1438660,
   373  						"db_postgres_xact_rollback":                                70,
   374  						"db_production_blks_hit":                                   0,
   375  						"db_production_blks_read":                                  0,
   376  						"db_production_blks_read_perc":                             0,
   377  						"db_production_confl_bufferpin":                            0,
   378  						"db_production_confl_deadlock":                             0,
   379  						"db_production_confl_lock":                                 0,
   380  						"db_production_confl_snapshot":                             0,
   381  						"db_production_confl_tablespace":                           0,
   382  						"db_production_conflicts":                                  0,
   383  						"db_production_deadlocks":                                  0,
   384  						"db_production_lock_mode_AccessExclusiveLock_awaited":      0,
   385  						"db_production_lock_mode_AccessExclusiveLock_held":         0,
   386  						"db_production_lock_mode_AccessShareLock_awaited":          0,
   387  						"db_production_lock_mode_AccessShareLock_held":             0,
   388  						"db_production_lock_mode_ExclusiveLock_awaited":            0,
   389  						"db_production_lock_mode_ExclusiveLock_held":               0,
   390  						"db_production_lock_mode_RowExclusiveLock_awaited":         0,
   391  						"db_production_lock_mode_RowExclusiveLock_held":            0,
   392  						"db_production_lock_mode_RowShareLock_awaited":             0,
   393  						"db_production_lock_mode_RowShareLock_held":                0,
   394  						"db_production_lock_mode_ShareLock_awaited":                99,
   395  						"db_production_lock_mode_ShareLock_held":                   0,
   396  						"db_production_lock_mode_ShareRowExclusiveLock_awaited":    0,
   397  						"db_production_lock_mode_ShareRowExclusiveLock_held":       0,
   398  						"db_production_lock_mode_ShareUpdateExclusiveLock_awaited": 0,
   399  						"db_production_lock_mode_ShareUpdateExclusiveLock_held":    99,
   400  						"db_production_numbackends":                                1,
   401  						"db_production_numbackends_utilization":                    1,
   402  						"db_production_size":                                       8602115,
   403  						"db_production_temp_bytes":                                 0,
   404  						"db_production_temp_files":                                 0,
   405  						"db_production_tup_deleted":                                0,
   406  						"db_production_tup_fetched":                                0,
   407  						"db_production_tup_fetched_perc":                           0,
   408  						"db_production_tup_inserted":                               0,
   409  						"db_production_tup_returned":                               0,
   410  						"db_production_tup_updated":                                0,
   411  						"db_production_xact_commit":                                0,
   412  						"db_production_xact_rollback":                              0,
   413  						"index_myaccounts_email_key_table_myaccounts_db_postgres_schema_myschema_size":                     8192,
   414  						"index_myaccounts_email_key_table_myaccounts_db_postgres_schema_myschema_usage_status_unused":      1,
   415  						"index_myaccounts_email_key_table_myaccounts_db_postgres_schema_myschema_usage_status_used":        0,
   416  						"index_myaccounts_email_key_table_myaccounts_db_postgres_schema_public_size":                       8192,
   417  						"index_myaccounts_email_key_table_myaccounts_db_postgres_schema_public_usage_status_unused":        1,
   418  						"index_myaccounts_email_key_table_myaccounts_db_postgres_schema_public_usage_status_used":          0,
   419  						"index_myaccounts_pkey_table_myaccounts_db_postgres_schema_myschema_size":                          8192,
   420  						"index_myaccounts_pkey_table_myaccounts_db_postgres_schema_myschema_usage_status_unused":           1,
   421  						"index_myaccounts_pkey_table_myaccounts_db_postgres_schema_myschema_usage_status_used":             0,
   422  						"index_myaccounts_pkey_table_myaccounts_db_postgres_schema_public_size":                            8192,
   423  						"index_myaccounts_pkey_table_myaccounts_db_postgres_schema_public_usage_status_unused":             1,
   424  						"index_myaccounts_pkey_table_myaccounts_db_postgres_schema_public_usage_status_used":               0,
   425  						"index_myaccounts_username_key_table_myaccounts_db_postgres_schema_myschema_size":                  8192,
   426  						"index_myaccounts_username_key_table_myaccounts_db_postgres_schema_myschema_usage_status_unused":   1,
   427  						"index_myaccounts_username_key_table_myaccounts_db_postgres_schema_myschema_usage_status_used":     0,
   428  						"index_myaccounts_username_key_table_myaccounts_db_postgres_schema_public_size":                    8192,
   429  						"index_myaccounts_username_key_table_myaccounts_db_postgres_schema_public_usage_status_unused":     1,
   430  						"index_myaccounts_username_key_table_myaccounts_db_postgres_schema_public_usage_status_used":       0,
   431  						"index_pgbench_accounts_pkey_table_pgbench_accounts_db_postgres_schema_public_bloat_size":          0,
   432  						"index_pgbench_accounts_pkey_table_pgbench_accounts_db_postgres_schema_public_bloat_size_perc":     0,
   433  						"index_pgbench_accounts_pkey_table_pgbench_accounts_db_postgres_schema_public_size":                112336896,
   434  						"index_pgbench_accounts_pkey_table_pgbench_accounts_db_postgres_schema_public_usage_status_unused": 0,
   435  						"index_pgbench_accounts_pkey_table_pgbench_accounts_db_postgres_schema_public_usage_status_used":   1,
   436  						"index_pgbench_branches_pkey_table_pgbench_branches_db_postgres_schema_public_size":                16384,
   437  						"index_pgbench_branches_pkey_table_pgbench_branches_db_postgres_schema_public_usage_status_unused": 1,
   438  						"index_pgbench_branches_pkey_table_pgbench_branches_db_postgres_schema_public_usage_status_used":   0,
   439  						"index_pgbench_tellers_pkey_table_pgbench_tellers_db_postgres_schema_public_size":                  32768,
   440  						"index_pgbench_tellers_pkey_table_pgbench_tellers_db_postgres_schema_public_usage_status_unused":   1,
   441  						"index_pgbench_tellers_pkey_table_pgbench_tellers_db_postgres_schema_public_usage_status_used":     0,
   442  						"locks_utilization":                                                     6,
   443  						"maxwritten_clean":                                                      0,
   444  						"oldest_current_xid":                                                    9,
   445  						"percent_towards_emergency_autovacuum":                                  0,
   446  						"percent_towards_wraparound":                                            0,
   447  						"query_running_time_hist_bucket_1":                                      1,
   448  						"query_running_time_hist_bucket_2":                                      0,
   449  						"query_running_time_hist_bucket_3":                                      0,
   450  						"query_running_time_hist_bucket_4":                                      0,
   451  						"query_running_time_hist_bucket_5":                                      0,
   452  						"query_running_time_hist_bucket_6":                                      0,
   453  						"query_running_time_hist_bucket_inf":                                    0,
   454  						"query_running_time_hist_count":                                         1,
   455  						"query_running_time_hist_sum":                                           0,
   456  						"repl_slot_ocean_replslot_files":                                        0,
   457  						"repl_slot_ocean_replslot_wal_keep":                                     0,
   458  						"repl_standby_app_phys-standby2_wal_flush_lag_size":                     0,
   459  						"repl_standby_app_phys-standby2_wal_flush_lag_time":                     0,
   460  						"repl_standby_app_phys-standby2_wal_replay_lag_size":                    0,
   461  						"repl_standby_app_phys-standby2_wal_replay_lag_time":                    0,
   462  						"repl_standby_app_phys-standby2_wal_sent_lag_size":                      0,
   463  						"repl_standby_app_phys-standby2_wal_write_lag_size":                     0,
   464  						"repl_standby_app_phys-standby2_wal_write_time":                         0,
   465  						"repl_standby_app_walreceiver_wal_flush_lag_size":                       2,
   466  						"repl_standby_app_walreceiver_wal_flush_lag_time":                       2,
   467  						"repl_standby_app_walreceiver_wal_replay_lag_size":                      2,
   468  						"repl_standby_app_walreceiver_wal_replay_lag_time":                      2,
   469  						"repl_standby_app_walreceiver_wal_sent_lag_size":                        2,
   470  						"repl_standby_app_walreceiver_wal_write_lag_size":                       2,
   471  						"repl_standby_app_walreceiver_wal_write_time":                           2,
   472  						"server_connections_available":                                          97,
   473  						"server_connections_state_active":                                       1,
   474  						"server_connections_state_disabled":                                     1,
   475  						"server_connections_state_fastpath_function_call":                       1,
   476  						"server_connections_state_idle":                                         14,
   477  						"server_connections_state_idle_in_transaction":                          7,
   478  						"server_connections_state_idle_in_transaction_aborted":                  1,
   479  						"server_connections_used":                                               3,
   480  						"server_connections_utilization":                                        3,
   481  						"server_uptime":                                                         499906,
   482  						"table_pgbench_accounts_db_postgres_schema_public_bloat_size":           9863168,
   483  						"table_pgbench_accounts_db_postgres_schema_public_bloat_size_perc":      1,
   484  						"table_pgbench_accounts_db_postgres_schema_public_heap_blks_hit":        224484753408,
   485  						"table_pgbench_accounts_db_postgres_schema_public_heap_blks_read":       1803882668032,
   486  						"table_pgbench_accounts_db_postgres_schema_public_heap_blks_read_perc":  88,
   487  						"table_pgbench_accounts_db_postgres_schema_public_idx_blks_hit":         7138635948032,
   488  						"table_pgbench_accounts_db_postgres_schema_public_idx_blks_read":        973310976000,
   489  						"table_pgbench_accounts_db_postgres_schema_public_idx_blks_read_perc":   11,
   490  						"table_pgbench_accounts_db_postgres_schema_public_idx_scan":             99955,
   491  						"table_pgbench_accounts_db_postgres_schema_public_idx_tup_fetch":        99955,
   492  						"table_pgbench_accounts_db_postgres_schema_public_last_analyze_ago":     377149,
   493  						"table_pgbench_accounts_db_postgres_schema_public_last_vacuum_ago":      377149,
   494  						"table_pgbench_accounts_db_postgres_schema_public_n_dead_tup":           1000048,
   495  						"table_pgbench_accounts_db_postgres_schema_public_n_dead_tup_perc":      16,
   496  						"table_pgbench_accounts_db_postgres_schema_public_n_live_tup":           5000048,
   497  						"table_pgbench_accounts_db_postgres_schema_public_n_tup_del":            0,
   498  						"table_pgbench_accounts_db_postgres_schema_public_n_tup_hot_upd":        0,
   499  						"table_pgbench_accounts_db_postgres_schema_public_n_tup_hot_upd_perc":   0,
   500  						"table_pgbench_accounts_db_postgres_schema_public_n_tup_ins":            5000000,
   501  						"table_pgbench_accounts_db_postgres_schema_public_n_tup_upd":            0,
   502  						"table_pgbench_accounts_db_postgres_schema_public_seq_scan":             2,
   503  						"table_pgbench_accounts_db_postgres_schema_public_seq_tup_read":         5000000,
   504  						"table_pgbench_accounts_db_postgres_schema_public_tidx_blks_hit":        -1,
   505  						"table_pgbench_accounts_db_postgres_schema_public_tidx_blks_read":       -1,
   506  						"table_pgbench_accounts_db_postgres_schema_public_tidx_blks_read_perc":  50,
   507  						"table_pgbench_accounts_db_postgres_schema_public_toast_blks_hit":       -1,
   508  						"table_pgbench_accounts_db_postgres_schema_public_toast_blks_read":      -1,
   509  						"table_pgbench_accounts_db_postgres_schema_public_toast_blks_read_perc": 50,
   510  						"table_pgbench_accounts_db_postgres_schema_public_total_size":           784031744,
   511  						"table_pgbench_branches_db_postgres_schema_public_heap_blks_hit":        304316416,
   512  						"table_pgbench_branches_db_postgres_schema_public_heap_blks_read":       507150336,
   513  						"table_pgbench_branches_db_postgres_schema_public_heap_blks_read_perc":  62,
   514  						"table_pgbench_branches_db_postgres_schema_public_idx_blks_hit":         101441536,
   515  						"table_pgbench_branches_db_postgres_schema_public_idx_blks_read":        101425152,
   516  						"table_pgbench_branches_db_postgres_schema_public_idx_blks_read_perc":   49,
   517  						"table_pgbench_branches_db_postgres_schema_public_idx_scan":             0,
   518  						"table_pgbench_branches_db_postgres_schema_public_idx_tup_fetch":        0,
   519  						"table_pgbench_branches_db_postgres_schema_public_last_analyze_ago":     377149,
   520  						"table_pgbench_branches_db_postgres_schema_public_last_vacuum_ago":      371719,
   521  						"table_pgbench_branches_db_postgres_schema_public_n_dead_tup":           0,
   522  						"table_pgbench_branches_db_postgres_schema_public_n_dead_tup_perc":      0,
   523  						"table_pgbench_branches_db_postgres_schema_public_n_live_tup":           50,
   524  						"table_pgbench_branches_db_postgres_schema_public_n_tup_del":            0,
   525  						"table_pgbench_branches_db_postgres_schema_public_n_tup_hot_upd":        0,
   526  						"table_pgbench_branches_db_postgres_schema_public_n_tup_hot_upd_perc":   0,
   527  						"table_pgbench_branches_db_postgres_schema_public_n_tup_ins":            50,
   528  						"table_pgbench_branches_db_postgres_schema_public_n_tup_upd":            0,
   529  						"table_pgbench_branches_db_postgres_schema_public_seq_scan":             6,
   530  						"table_pgbench_branches_db_postgres_schema_public_seq_tup_read":         300,
   531  						"table_pgbench_branches_db_postgres_schema_public_tidx_blks_hit":        -1,
   532  						"table_pgbench_branches_db_postgres_schema_public_tidx_blks_read":       -1,
   533  						"table_pgbench_branches_db_postgres_schema_public_tidx_blks_read_perc":  50,
   534  						"table_pgbench_branches_db_postgres_schema_public_toast_blks_hit":       -1,
   535  						"table_pgbench_branches_db_postgres_schema_public_toast_blks_read":      -1,
   536  						"table_pgbench_branches_db_postgres_schema_public_toast_blks_read_perc": 50,
   537  						"table_pgbench_branches_db_postgres_schema_public_total_size":           57344,
   538  						"table_pgbench_history_db_postgres_schema_public_heap_blks_hit":         0,
   539  						"table_pgbench_history_db_postgres_schema_public_heap_blks_read":        0,
   540  						"table_pgbench_history_db_postgres_schema_public_heap_blks_read_perc":   0,
   541  						"table_pgbench_history_db_postgres_schema_public_idx_blks_hit":          -1,
   542  						"table_pgbench_history_db_postgres_schema_public_idx_blks_read":         -1,
   543  						"table_pgbench_history_db_postgres_schema_public_idx_blks_read_perc":    50,
   544  						"table_pgbench_history_db_postgres_schema_public_idx_scan":              0,
   545  						"table_pgbench_history_db_postgres_schema_public_idx_tup_fetch":         0,
   546  						"table_pgbench_history_db_postgres_schema_public_last_analyze_ago":      377149,
   547  						"table_pgbench_history_db_postgres_schema_public_last_vacuum_ago":       377149,
   548  						"table_pgbench_history_db_postgres_schema_public_n_dead_tup":            0,
   549  						"table_pgbench_history_db_postgres_schema_public_n_dead_tup_perc":       0,
   550  						"table_pgbench_history_db_postgres_schema_public_n_live_tup":            0,
   551  						"table_pgbench_history_db_postgres_schema_public_n_tup_del":             0,
   552  						"table_pgbench_history_db_postgres_schema_public_n_tup_hot_upd":         0,
   553  						"table_pgbench_history_db_postgres_schema_public_n_tup_hot_upd_perc":    0,
   554  						"table_pgbench_history_db_postgres_schema_public_n_tup_ins":             0,
   555  						"table_pgbench_history_db_postgres_schema_public_n_tup_upd":             0,
   556  						"table_pgbench_history_db_postgres_schema_public_seq_scan":              0,
   557  						"table_pgbench_history_db_postgres_schema_public_seq_tup_read":          0,
   558  						"table_pgbench_history_db_postgres_schema_public_tidx_blks_hit":         -1,
   559  						"table_pgbench_history_db_postgres_schema_public_tidx_blks_read":        -1,
   560  						"table_pgbench_history_db_postgres_schema_public_tidx_blks_read_perc":   50,
   561  						"table_pgbench_history_db_postgres_schema_public_toast_blks_hit":        -1,
   562  						"table_pgbench_history_db_postgres_schema_public_toast_blks_read":       -1,
   563  						"table_pgbench_history_db_postgres_schema_public_toast_blks_read_perc":  50,
   564  						"table_pgbench_history_db_postgres_schema_public_total_size":            0,
   565  						"table_pgbench_tellers_db_postgres_schema_public_heap_blks_hit":         491937792,
   566  						"table_pgbench_tellers_db_postgres_schema_public_heap_blks_read":        623828992,
   567  						"table_pgbench_tellers_db_postgres_schema_public_heap_blks_read_perc":   55,
   568  						"table_pgbench_tellers_db_postgres_schema_public_idx_blks_hit":          0,
   569  						"table_pgbench_tellers_db_postgres_schema_public_idx_blks_read":         101433344,
   570  						"table_pgbench_tellers_db_postgres_schema_public_idx_blks_read_perc":    100,
   571  						"table_pgbench_tellers_db_postgres_schema_public_idx_scan":              0,
   572  						"table_pgbench_tellers_db_postgres_schema_public_idx_tup_fetch":         0,
   573  						"table_pgbench_tellers_db_postgres_schema_public_last_analyze_ago":      377149,
   574  						"table_pgbench_tellers_db_postgres_schema_public_last_vacuum_ago":       371719,
   575  						"table_pgbench_tellers_db_postgres_schema_public_n_dead_tup":            0,
   576  						"table_pgbench_tellers_db_postgres_schema_public_n_dead_tup_perc":       0,
   577  						"table_pgbench_tellers_db_postgres_schema_public_n_live_tup":            500,
   578  						"table_pgbench_tellers_db_postgres_schema_public_n_tup_del":             0,
   579  						"table_pgbench_tellers_db_postgres_schema_public_n_tup_hot_upd":         0,
   580  						"table_pgbench_tellers_db_postgres_schema_public_n_tup_hot_upd_perc":    0,
   581  						"table_pgbench_tellers_db_postgres_schema_public_n_tup_ins":             500,
   582  						"table_pgbench_tellers_db_postgres_schema_public_n_tup_upd":             0,
   583  						"table_pgbench_tellers_db_postgres_schema_public_null_columns":          1,
   584  						"table_pgbench_tellers_db_postgres_schema_public_seq_scan":              1,
   585  						"table_pgbench_tellers_db_postgres_schema_public_seq_tup_read":          500,
   586  						"table_pgbench_tellers_db_postgres_schema_public_tidx_blks_hit":         -1,
   587  						"table_pgbench_tellers_db_postgres_schema_public_tidx_blks_read":        -1,
   588  						"table_pgbench_tellers_db_postgres_schema_public_tidx_blks_read_perc":   50,
   589  						"table_pgbench_tellers_db_postgres_schema_public_toast_blks_hit":        -1,
   590  						"table_pgbench_tellers_db_postgres_schema_public_toast_blks_read":       -1,
   591  						"table_pgbench_tellers_db_postgres_schema_public_toast_blks_read_perc":  50,
   592  						"table_pgbench_tellers_db_postgres_schema_public_total_size":            90112,
   593  						"transaction_running_time_hist_bucket_1":                                1,
   594  						"transaction_running_time_hist_bucket_2":                                0,
   595  						"transaction_running_time_hist_bucket_3":                                0,
   596  						"transaction_running_time_hist_bucket_4":                                0,
   597  						"transaction_running_time_hist_bucket_5":                                0,
   598  						"transaction_running_time_hist_bucket_6":                                0,
   599  						"transaction_running_time_hist_bucket_inf":                              7,
   600  						"transaction_running_time_hist_count":                                   8,
   601  						"transaction_running_time_hist_sum":                                     4022,
   602  						"wal_archive_files_done_count":                                          1,
   603  						"wal_archive_files_ready_count":                                         1,
   604  						"wal_recycled_files":                                                    0,
   605  						"wal_writes":                                                            24103144,
   606  						"wal_written_files":                                                     1,
   607  					}
   608  
   609  					assert.Equal(t, expected, mx)
   610  				},
   611  			},
   612  		},
   613  		"Fail when querying the database version returns an error": {
   614  			{
   615  				prepareMock: func(t *testing.T, pg *Postgres, m sqlmock.Sqlmock) {
   616  					mockExpectErr(m, queryServerVersion())
   617  				},
   618  				check: func(t *testing.T, pg *Postgres) {
   619  					mx := pg.Collect()
   620  					var expected map[string]int64
   621  					assert.Equal(t, expected, mx)
   622  				},
   623  			},
   624  		},
   625  		"Fail when querying settings max connections returns an error": {
   626  			{
   627  				prepareMock: func(t *testing.T, pg *Postgres, m sqlmock.Sqlmock) {
   628  					mockExpect(t, m, queryServerVersion(), dataV140004ServerVersionNum)
   629  					mockExpect(t, m, queryIsSuperUser(), dataV140004IsSuperUserTrue)
   630  					mockExpect(t, m, queryPGIsInRecovery(), dataV140004PGIsInRecoveryTrue)
   631  
   632  					mockExpectErr(m, querySettingsMaxConnections())
   633  				},
   634  				check: func(t *testing.T, pg *Postgres) {
   635  					mx := pg.Collect()
   636  					var expected map[string]int64
   637  					assert.Equal(t, expected, mx)
   638  				},
   639  			},
   640  		},
   641  		"Fail when querying the server connections returns an error": {
   642  			{
   643  				prepareMock: func(t *testing.T, pg *Postgres, m sqlmock.Sqlmock) {
   644  					mockExpect(t, m, queryServerVersion(), dataV140004ServerVersionNum)
   645  					mockExpect(t, m, queryIsSuperUser(), dataV140004IsSuperUserTrue)
   646  					mockExpect(t, m, queryPGIsInRecovery(), dataV140004PGIsInRecoveryTrue)
   647  
   648  					mockExpect(t, m, querySettingsMaxConnections(), dataV140004SettingsMaxConnections)
   649  					mockExpect(t, m, querySettingsMaxLocksHeld(), dataV140004SettingsMaxLocksHeld)
   650  
   651  					mockExpectErr(m, queryServerCurrentConnectionsUsed())
   652  				},
   653  				check: func(t *testing.T, pg *Postgres) {
   654  					mx := pg.Collect()
   655  					var expected map[string]int64
   656  					assert.Equal(t, expected, mx)
   657  				},
   658  			},
   659  		},
   660  	}
   661  
   662  	for name, test := range tests {
   663  		t.Run(name, func(t *testing.T) {
   664  			db, mock, err := sqlmock.New(
   665  				sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual),
   666  			)
   667  			require.NoError(t, err)
   668  			pg := New()
   669  			pg.db = db
   670  			defer func() { _ = db.Close() }()
   671  
   672  			require.True(t, pg.Init())
   673  
   674  			for i, step := range test {
   675  				t.Run(fmt.Sprintf("step[%d]", i), func(t *testing.T) {
   676  					step.prepareMock(t, pg, mock)
   677  					step.check(t, pg)
   678  				})
   679  			}
   680  			assert.NoError(t, mock.ExpectationsWereMet())
   681  		})
   682  	}
   683  }
   684  
   685  func mockExpect(t *testing.T, mock sqlmock.Sqlmock, query string, rows []byte) {
   686  	mock.ExpectQuery(query).WillReturnRows(mustMockRows(t, rows)).RowsWillBeClosed()
   687  }
   688  
   689  func mockExpectErr(mock sqlmock.Sqlmock, query string) {
   690  	mock.ExpectQuery(query).WillReturnError(errors.New("mock error"))
   691  }
   692  
   693  func mustMockRows(t *testing.T, data []byte) *sqlmock.Rows {
   694  	rows, err := prepareMockRows(data)
   695  	require.NoError(t, err)
   696  	return rows
   697  }
   698  
   699  func prepareMockRows(data []byte) (*sqlmock.Rows, error) {
   700  	r := bytes.NewReader(data)
   701  	sc := bufio.NewScanner(r)
   702  
   703  	var numColumns int
   704  	var rows *sqlmock.Rows
   705  
   706  	for sc.Scan() {
   707  		s := strings.TrimSpace(sc.Text())
   708  		if s == "" || strings.HasPrefix(s, "---") {
   709  			continue
   710  		}
   711  
   712  		parts := strings.Split(s, "|")
   713  		for i, v := range parts {
   714  			parts[i] = strings.TrimSpace(v)
   715  		}
   716  
   717  		if rows == nil {
   718  			numColumns = len(parts)
   719  			rows = sqlmock.NewRows(parts)
   720  			continue
   721  		}
   722  
   723  		if len(parts) != numColumns {
   724  			return nil, fmt.Errorf("prepareMockRows(): columns != values (%d/%d)", numColumns, len(parts))
   725  		}
   726  
   727  		values := make([]driver.Value, len(parts))
   728  		for i, v := range parts {
   729  			values[i] = v
   730  		}
   731  		rows.AddRow(values...)
   732  	}
   733  
   734  	if rows == nil {
   735  		return nil, errors.New("prepareMockRows(): nil rows result")
   736  	}
   737  
   738  	return rows, nil
   739  }