...

Source file src/github.com/cybertec-postgresql/pgwatch/v3/internal/sources/postgres_test.go

Documentation: github.com/cybertec-postgresql/pgwatch/v3/internal/sources

     1  package sources_test
     2  
     3  import (
     4  	"errors"
     5  	"testing"
     6  
     7  	"github.com/pashagolub/pgxmock/v4"
     8  	"github.com/stretchr/testify/assert"
     9  
    10  	"github.com/cybertec-postgresql/pgwatch/v3/internal/sources"
    11  )
    12  
    13  func TestNewPostgresSourcesReaderWriter(t *testing.T) {
    14  	a := assert.New(t)
    15  	t.Run("ConnectionError", func(*testing.T) {
    16  		pgrw, err := sources.NewPostgresSourcesReaderWriter(ctx, "postgres://user:pass@foohost:5432/db1")
    17  		a.Error(err) // connection error
    18  		a.NotNil(t, pgrw)
    19  	})
    20  	t.Run("InvalidConnStr", func(*testing.T) {
    21  		pgrw, err := sources.NewPostgresSourcesReaderWriter(ctx, "invalid_connstr")
    22  		a.Error(err)
    23  		a.Nil(pgrw)
    24  	})
    25  }
    26  
    27  func TestNewPostgresSourcesReaderWriterConn(t *testing.T) {
    28  	a := assert.New(t)
    29  	conn, err := pgxmock.NewPool()
    30  	a.NoError(err)
    31  	conn.ExpectPing()
    32  
    33  	pgrw, err := sources.NewPostgresSourcesReaderWriterConn(ctx, conn)
    34  	a.NoError(err)
    35  	a.NotNil(t, pgrw)
    36  	a.NoError(conn.ExpectationsWereMet())
    37  }
    38  
    39  func TestGetMonitoredDatabases(t *testing.T) {
    40  	a := assert.New(t)
    41  	conn, err := pgxmock.NewPool()
    42  	a.NoError(err)
    43  	conn.ExpectPing()
    44  	conn.ExpectQuery(`select \/\* pgwatch_generated \*\/`).WillReturnRows(pgxmock.NewRows([]string{
    45  		"name", "group", "dbtype", "connstr", "config", "config_standby", "preset_config",
    46  		"preset_config_standby", "include_pattern", "exclude_pattern",
    47  		"custom_tags", "host_config", "only_if_master", "is_enabled",
    48  	}).AddRow(
    49  		"db1", "group1", sources.Kind("postgres"), "postgres://user:pass@localhost:5432/db1",
    50  		map[string]float64{"metric": 60}, map[string]float64{"standby_metric": 60}, "exhaustive", "exhaustive",
    51  		".*", `\_.+`, map[string]string{"tag": "value"}, nil, true, true,
    52  	))
    53  	pgrw, err := sources.NewPostgresSourcesReaderWriterConn(ctx, conn)
    54  	a.NoError(err)
    55  
    56  	dbs, err := pgrw.GetSources()
    57  	a.NoError(err)
    58  	a.Len(dbs, 1)
    59  	a.NoError(conn.ExpectationsWereMet())
    60  
    61  	// check failed query
    62  	conn.ExpectQuery(`select \/\* pgwatch_generated \*\/`).WillReturnError(errors.New("failed query"))
    63  	dbs, err = pgrw.GetSources()
    64  	a.Error(err)
    65  	a.Nil(dbs)
    66  	a.NoError(conn.ExpectationsWereMet())
    67  }
    68  
    69  func TestDeleteDatabase(t *testing.T) {
    70  	a := assert.New(t)
    71  	conn, err := pgxmock.NewPool()
    72  	a.NoError(err)
    73  	conn.ExpectPing()
    74  	conn.ExpectExec(`delete from pgwatch\.source where name = \$1`).WithArgs("db1").WillReturnResult(pgxmock.NewResult("DELETE", 1))
    75  	pgrw, err := sources.NewPostgresSourcesReaderWriterConn(ctx, conn)
    76  	a.NoError(err)
    77  
    78  	err = pgrw.DeleteSource("db1")
    79  	a.NoError(err)
    80  	a.NoError(conn.ExpectationsWereMet())
    81  }
    82  
    83  func TestUpdateDatabase(t *testing.T) {
    84  	a := assert.New(t)
    85  	conn, err := pgxmock.NewPool()
    86  	a.NoError(err)
    87  
    88  	md := sources.Source{
    89  		Name:           "db1",
    90  		Group:          "group1",
    91  		Kind:           sources.Kind("postgres"),
    92  		ConnStr:        "postgres://user:pass@localhost:5432/db1",
    93  		Metrics:        map[string]float64{"metric": 60},
    94  		MetricsStandby: map[string]float64{"standby_metric": 60},
    95  		IncludePattern: ".*",
    96  		ExcludePattern: `\_.+`,
    97  		CustomTags:     map[string]string{"tag": "value"},
    98  	}
    99  	conn.ExpectPing()
   100  	conn.ExpectExec(`insert into pgwatch\.source`).WithArgs(
   101  		md.Name, md.Group, md.Kind,
   102  		md.ConnStr, `{"metric":60}`, `{"standby_metric":60}`,
   103  		md.PresetMetrics, md.PresetMetricsStandby,
   104  		md.IncludePattern, md.ExcludePattern, `{"tag":"value"}`,
   105  		nil, md.OnlyIfMaster, md.IsEnabled,
   106  	).WillReturnResult(pgxmock.NewResult("UPDATE", 1))
   107  
   108  	pgrw, err := sources.NewPostgresSourcesReaderWriterConn(ctx, conn)
   109  	a.NoError(err)
   110  	err = pgrw.UpdateSource(md)
   111  	a.NoError(err)
   112  	a.NoError(conn.ExpectationsWereMet())
   113  }
   114  
   115  func TestWriteMonitoredDatabases(t *testing.T) {
   116  	var (
   117  		pgrw sources.ReaderWriter
   118  		err  error
   119  	)
   120  	a := assert.New(t)
   121  	conn, err := pgxmock.NewPool()
   122  	a.NoError(err)
   123  	md := sources.Source{
   124  		Name:           "db1",
   125  		Group:          "group1",
   126  		Kind:           sources.Kind("postgres"),
   127  		ConnStr:        "postgres://user:pass@localhost:5432/db1",
   128  		Metrics:        map[string]float64{"metric": 60},
   129  		MetricsStandby: map[string]float64{"standby_metric": 60},
   130  		IncludePattern: ".*",
   131  		ExcludePattern: `\_.+`,
   132  		CustomTags:     map[string]string{"tag": "value"},
   133  	}
   134  	mds := sources.Sources{md}
   135  
   136  	t.Run("happy path", func(*testing.T) {
   137  		conn.ExpectPing()
   138  		conn.ExpectBegin()
   139  		conn.ExpectExec(`truncate pgwatch\.source`).WillReturnResult(pgxmock.NewResult("TRUNCATE", 1))
   140  		conn.ExpectExec(`insert into pgwatch\.source`).WithArgs(
   141  			md.Name, md.Group, md.Kind,
   142  			md.ConnStr, `{"metric":60}`, `{"standby_metric":60}`, md.PresetMetrics, md.PresetMetricsStandby,
   143  			md.IncludePattern, md.ExcludePattern, `{"tag":"value"}`,
   144  			nil, md.OnlyIfMaster, md.IsEnabled,
   145  		).WillReturnResult(pgxmock.NewResult("INSERT", 1))
   146  		conn.ExpectCommit()
   147  		conn.ExpectRollback() // deferred rollback
   148  
   149  		pgrw, err = sources.NewPostgresSourcesReaderWriterConn(ctx, conn)
   150  		a.NoError(err)
   151  		err = pgrw.WriteSources(mds)
   152  		a.NoError(err)
   153  		a.NoError(conn.ExpectationsWereMet())
   154  	})
   155  
   156  	t.Run("failed transaction begin", func(*testing.T) {
   157  		conn.ExpectBegin().WillReturnError(errors.New("failed transaction begin"))
   158  
   159  		err = pgrw.WriteSources(mds)
   160  		a.Error(err)
   161  		a.NoError(conn.ExpectationsWereMet())
   162  	})
   163  
   164  	t.Run("failed truncate", func(*testing.T) {
   165  		conn.ExpectBegin()
   166  		conn.ExpectExec(`truncate pgwatch\.source`).WillReturnError(errors.New("failed truncate"))
   167  
   168  		err = pgrw.WriteSources(mds)
   169  		a.Error(err)
   170  		a.NoError(conn.ExpectationsWereMet())
   171  	})
   172  
   173  	t.Run("failed insert", func(*testing.T) {
   174  		conn.ExpectBegin()
   175  		conn.ExpectExec(`truncate pgwatch\.source`).WillReturnResult(pgxmock.NewResult("TRUNCATE", 1))
   176  		conn.ExpectExec(`insert into pgwatch\.source`).WithArgs(
   177  			md.Name, md.Group, md.Kind,
   178  			md.ConnStr, `{"metric":60}`, `{"standby_metric":60}`, md.PresetMetrics, md.PresetMetricsStandby,
   179  			md.IncludePattern, md.ExcludePattern, `{"tag":"value"}`,
   180  			nil, md.OnlyIfMaster, md.IsEnabled,
   181  		).WillReturnError(errors.New("failed insert"))
   182  		conn.ExpectRollback()
   183  
   184  		err = pgrw.WriteSources(mds)
   185  		a.Error(err)
   186  		a.NoError(conn.ExpectationsWereMet())
   187  	})
   188  }
   189