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 }