...

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 TestSyncFromReader(t *testing.T) {
    70  	a := assert.New(t)
    71  	conn, err := pgxmock.NewPool()
    72  	a.NoError(err)
    73  	conn.ExpectPing()
    74  	conn.ExpectQuery(`select \/\* pgwatch_generated \*\/`).WillReturnRows(pgxmock.NewRows([]string{
    75  		"name", "group", "dbtype", "connstr", "config", "config_standby", "preset_config",
    76  		"preset_config_standby", "include_pattern", "exclude_pattern",
    77  		"custom_tags", "host_config", "only_if_master", "is_enabled",
    78  	}).AddRow(
    79  		"db1", "group1", sources.Kind("postgres"), "postgres://user:pass@localhost:5432/db1",
    80  		map[string]float64{"metric": 60}, map[string]float64{"standby_metric": 60}, "exhaustive", "exhaustive",
    81  		".*", `\_.+`, map[string]string{"tag": "value"}, nil, true, true,
    82  	))
    83  	pgrw, err := sources.NewPostgresSourcesReaderWriterConn(ctx, conn)
    84  	a.NoError(err)
    85  
    86  	md := &sources.SourceConn{}
    87  	md.Name = "db1"
    88  	dbs := sources.SourceConns{md}
    89  	dbs, err = dbs.SyncFromReader(pgrw)
    90  	a.NoError(err)
    91  	a.Len(dbs, 1)
    92  	a.NoError(conn.ExpectationsWereMet())
    93  }
    94  
    95  func TestDeleteDatabase(t *testing.T) {
    96  	a := assert.New(t)
    97  	conn, err := pgxmock.NewPool()
    98  	a.NoError(err)
    99  	conn.ExpectPing()
   100  	conn.ExpectExec(`delete from pgwatch\.source where name = \$1`).WithArgs("db1").WillReturnResult(pgxmock.NewResult("DELETE", 1))
   101  	pgrw, err := sources.NewPostgresSourcesReaderWriterConn(ctx, conn)
   102  	a.NoError(err)
   103  
   104  	err = pgrw.DeleteSource("db1")
   105  	a.NoError(err)
   106  	a.NoError(conn.ExpectationsWereMet())
   107  }
   108  
   109  func TestUpdateDatabase(t *testing.T) {
   110  	a := assert.New(t)
   111  	conn, err := pgxmock.NewPool()
   112  	a.NoError(err)
   113  
   114  	md := sources.Source{
   115  		Name:           "db1",
   116  		Group:          "group1",
   117  		Kind:           sources.Kind("postgres"),
   118  		ConnStr:        "postgres://user:pass@localhost:5432/db1",
   119  		Metrics:        map[string]float64{"metric": 60},
   120  		MetricsStandby: map[string]float64{"standby_metric": 60},
   121  		IncludePattern: ".*",
   122  		ExcludePattern: `\_.+`,
   123  		CustomTags:     map[string]string{"tag": "value"},
   124  	}
   125  	conn.ExpectPing()
   126  	conn.ExpectExec(`insert into pgwatch\.source`).WithArgs(
   127  		md.Name, md.Group, md.Kind,
   128  		md.ConnStr, `{"metric":60}`, `{"standby_metric":60}`,
   129  		md.PresetMetrics, md.PresetMetricsStandby,
   130  		md.IncludePattern, md.ExcludePattern, `{"tag":"value"}`,
   131  		nil, md.OnlyIfMaster,
   132  	).WillReturnResult(pgxmock.NewResult("UPDATE", 1))
   133  
   134  	pgrw, err := sources.NewPostgresSourcesReaderWriterConn(ctx, conn)
   135  	a.NoError(err)
   136  	err = pgrw.UpdateSource(md)
   137  	a.NoError(err)
   138  	a.NoError(conn.ExpectationsWereMet())
   139  }
   140  
   141  func TestWriteMonitoredDatabases(t *testing.T) {
   142  	var (
   143  		pgrw sources.ReaderWriter
   144  		err  error
   145  	)
   146  	a := assert.New(t)
   147  	conn, err := pgxmock.NewPool()
   148  	a.NoError(err)
   149  	md := sources.Source{
   150  		Name:           "db1",
   151  		Group:          "group1",
   152  		Kind:           sources.Kind("postgres"),
   153  		ConnStr:        "postgres://user:pass@localhost:5432/db1",
   154  		Metrics:        map[string]float64{"metric": 60},
   155  		MetricsStandby: map[string]float64{"standby_metric": 60},
   156  		IncludePattern: ".*",
   157  		ExcludePattern: `\_.+`,
   158  		CustomTags:     map[string]string{"tag": "value"},
   159  	}
   160  	mds := sources.Sources{md}
   161  
   162  	t.Run("happy path", func(*testing.T) {
   163  		conn.ExpectPing()
   164  		conn.ExpectBegin()
   165  		conn.ExpectExec(`truncate pgwatch\.source`).WillReturnResult(pgxmock.NewResult("TRUNCATE", 1))
   166  		conn.ExpectExec(`insert into pgwatch\.source`).WithArgs(
   167  			md.Name, md.Group, md.Kind,
   168  			md.ConnStr, `{"metric":60}`, `{"standby_metric":60}`, md.PresetMetrics, md.PresetMetricsStandby,
   169  			md.IncludePattern, md.ExcludePattern, `{"tag":"value"}`,
   170  			nil, md.OnlyIfMaster,
   171  		).WillReturnResult(pgxmock.NewResult("INSERT", 1))
   172  		conn.ExpectCommit()
   173  		conn.ExpectRollback() // deferred rollback
   174  
   175  		pgrw, err = sources.NewPostgresSourcesReaderWriterConn(ctx, conn)
   176  		a.NoError(err)
   177  		err = pgrw.WriteSources(mds)
   178  		a.NoError(err)
   179  		a.NoError(conn.ExpectationsWereMet())
   180  	})
   181  
   182  	t.Run("failed transaction begin", func(*testing.T) {
   183  		conn.ExpectBegin().WillReturnError(errors.New("failed transaction begin"))
   184  
   185  		err = pgrw.WriteSources(mds)
   186  		a.Error(err)
   187  		a.NoError(conn.ExpectationsWereMet())
   188  	})
   189  
   190  	t.Run("failed truncate", func(*testing.T) {
   191  		conn.ExpectBegin()
   192  		conn.ExpectExec(`truncate pgwatch\.source`).WillReturnError(errors.New("failed truncate"))
   193  
   194  		err = pgrw.WriteSources(mds)
   195  		a.Error(err)
   196  		a.NoError(conn.ExpectationsWereMet())
   197  	})
   198  
   199  	t.Run("failed insert", func(*testing.T) {
   200  		conn.ExpectBegin()
   201  		conn.ExpectExec(`truncate pgwatch\.source`).WillReturnResult(pgxmock.NewResult("TRUNCATE", 1))
   202  		conn.ExpectExec(`insert into pgwatch\.source`).WithArgs(
   203  			md.Name, md.Group, md.Kind,
   204  			md.ConnStr, `{"metric":60}`, `{"standby_metric":60}`, md.PresetMetrics, md.PresetMetricsStandby,
   205  			md.IncludePattern, md.ExcludePattern, `{"tag":"value"}`,
   206  			nil, md.OnlyIfMaster,
   207  		).WillReturnError(errors.New("failed insert"))
   208  		conn.ExpectRollback()
   209  
   210  		err = pgrw.WriteSources(mds)
   211  		a.Error(err)
   212  		a.NoError(conn.ExpectationsWereMet())
   213  	})
   214  }
   215