...

Source file src/github.com/cybertec-postgresql/pgwatch/v6/internal/testutil/mocks_test.go

Documentation: github.com/cybertec-postgresql/pgwatch/v6/internal/testutil

     1  package testutil_test
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  
     7  	"github.com/cybertec-postgresql/pgwatch/v6/api/pb"
     8  	"github.com/cybertec-postgresql/pgwatch/v6/internal/metrics"
     9  	"github.com/cybertec-postgresql/pgwatch/v6/internal/sources"
    10  	"github.com/cybertec-postgresql/pgwatch/v6/internal/testutil"
    11  	"github.com/jackc/pgx/v5"
    12  	"github.com/stretchr/testify/assert"
    13  	"github.com/stretchr/testify/require"
    14  	"google.golang.org/protobuf/types/known/structpb"
    15  )
    16  
    17  func TestReceiver_UpdateMeasurements(t *testing.T) {
    18  	receiver := &testutil.Receiver{}
    19  	ctx := context.Background()
    20  
    21  	t.Run("valid message", func(t *testing.T) {
    22  		data, err := structpb.NewStruct(map[string]any{"test": "value"})
    23  		require.NoError(t, err)
    24  		msg := &pb.MeasurementEnvelope{
    25  			DBName: "Db",
    26  			Data:   []*structpb.Struct{data},
    27  		}
    28  		reply, err := receiver.UpdateMeasurements(ctx, msg)
    29  		assert.NoError(t, err)
    30  		assert.NotNil(t, reply)
    31  	})
    32  
    33  	t.Run("empty data", func(t *testing.T) {
    34  		msg := &pb.MeasurementEnvelope{
    35  			DBName: "Db",
    36  			Data:   []*structpb.Struct{},
    37  		}
    38  		reply, err := receiver.UpdateMeasurements(ctx, msg)
    39  		assert.Error(t, err)
    40  		assert.Nil(t, reply)
    41  		assert.EqualError(t, err, "empty message")
    42  	})
    43  
    44  	t.Run("invalid db name", func(t *testing.T) {
    45  		data, err := structpb.NewStruct(map[string]any{"test": "value"})
    46  		require.NoError(t, err)
    47  		msg := &pb.MeasurementEnvelope{
    48  			DBName: "WrongDb",
    49  			Data:   []*structpb.Struct{data},
    50  		}
    51  		reply, err := receiver.UpdateMeasurements(ctx, msg)
    52  		assert.Error(t, err)
    53  		assert.Nil(t, reply)
    54  		assert.EqualError(t, err, "invalid message")
    55  	})
    56  }
    57  
    58  func TestReceiver_SyncMetric(t *testing.T) {
    59  	receiver := &testutil.Receiver{}
    60  	ctx := context.Background()
    61  
    62  	t.Run("valid sync request", func(t *testing.T) {
    63  		syncReq := &pb.SyncReq{
    64  			Operation: pb.SyncOp_AddOp,
    65  		}
    66  		reply, err := receiver.SyncMetric(ctx, syncReq)
    67  		assert.NoError(t, err)
    68  		assert.NotNil(t, reply)
    69  	})
    70  
    71  	t.Run("nil sync request", func(t *testing.T) {
    72  		reply, err := receiver.SyncMetric(ctx, nil)
    73  		assert.Error(t, err)
    74  		assert.Nil(t, reply)
    75  		assert.EqualError(t, err, "nil sync request")
    76  	})
    77  
    78  	t.Run("invalid operation", func(t *testing.T) {
    79  		syncReq := &pb.SyncReq{
    80  			Operation: pb.SyncOp_InvalidOp,
    81  		}
    82  		reply, err := receiver.SyncMetric(ctx, syncReq)
    83  		assert.Error(t, err)
    84  		assert.Nil(t, reply)
    85  		assert.EqualError(t, err, "invalid sync request")
    86  	})
    87  }
    88  
    89  func TestReceiver_DefineMetrics(t *testing.T) {
    90  	receiver := &testutil.Receiver{}
    91  	ctx := context.Background()
    92  
    93  	t.Run("valid metrics struct", func(t *testing.T) {
    94  		metricsStruct, err := structpb.NewStruct(map[string]any{
    95  			"metric1": "value1",
    96  		})
    97  		assert.NoError(t, err)
    98  
    99  		reply, err := receiver.DefineMetrics(ctx, metricsStruct)
   100  		assert.NoError(t, err)
   101  		assert.NotNil(t, reply)
   102  		assert.Equal(t, "metrics defined successfully", reply.Logmsg)
   103  	})
   104  
   105  	t.Run("nil metrics struct", func(t *testing.T) {
   106  		reply, err := receiver.DefineMetrics(ctx, nil)
   107  		assert.Error(t, err)
   108  		assert.Nil(t, reply)
   109  		assert.EqualError(t, err, "nil metrics struct")
   110  	})
   111  
   112  	t.Run("empty metrics struct", func(t *testing.T) {
   113  		metricsStruct := &structpb.Struct{}
   114  		reply, err := receiver.DefineMetrics(ctx, metricsStruct)
   115  		assert.Error(t, err)
   116  		assert.Nil(t, reply)
   117  		assert.EqualError(t, err, "empty metrics struct")
   118  	})
   119  }
   120  
   121  func TestMockMetricsReaderWriter(t *testing.T) {
   122  	testData := &metrics.Metrics{
   123  		MetricDefs: map[string]metrics.Metric{"foo": {Description: "bar"}},
   124  		PresetDefs: map[string]metrics.Preset{"foo": {Description: "bar"}},
   125  	}
   126  	called := false
   127  
   128  	mock := testutil.MockMetricsReaderWriter{
   129  		GetMetricsFunc: func() (*metrics.Metrics, error) {
   130  			called = true
   131  			return testData, nil
   132  		},
   133  		WriteMetricsFunc: func(*metrics.Metrics) error {
   134  			called = true
   135  			return nil
   136  		},
   137  		UpdateMetricFunc: func(string, metrics.Metric) error {
   138  			called = true
   139  			return nil
   140  		},
   141  		DeleteMetricFunc: func(string) error {
   142  			called = true
   143  			return nil
   144  		},
   145  		CreateMetricFunc: func(string, metrics.Metric) error {
   146  			called = true
   147  			return nil
   148  		},
   149  		CreatePresetFunc: func(string, metrics.Preset) error {
   150  			called = true
   151  			return nil
   152  		},
   153  		UpdatePresetFunc: func(string, metrics.Preset) error {
   154  			called = true
   155  			return nil
   156  		},
   157  		DeletePresetFunc: func(string) error {
   158  			called = true
   159  			return nil
   160  		},
   161  	}
   162  
   163  	t.Run("GetMetrics", func(t *testing.T) {
   164  		called = false
   165  		metrics, err := mock.GetMetrics()
   166  		assert.NoError(t, err)
   167  		assert.Equal(t, true, called)
   168  		assert.Equal(t, testData, metrics)
   169  	})
   170  
   171  	t.Run("WriteMetrics", func(t *testing.T) {
   172  		called = false
   173  		err := mock.WriteMetrics(testData)
   174  		assert.NoError(t, err)
   175  		assert.Equal(t, true, called)
   176  	})
   177  
   178  	t.Run("UpdateMetric", func(t *testing.T) {
   179  		called = false
   180  		err := mock.UpdateMetric("foo", testData.MetricDefs["foo"])
   181  		assert.NoError(t, err)
   182  		assert.Equal(t, true, called)
   183  	})
   184  
   185  	t.Run("DeleteMetric", func(t *testing.T) {
   186  		called = false
   187  		err := mock.DeleteMetric("foo")
   188  		assert.NoError(t, err)
   189  		assert.Equal(t, true, called)
   190  	})
   191  
   192  	t.Run("CreateMetric", func(t *testing.T) {
   193  		called = false
   194  		err := mock.CreateMetric("foo", testData.MetricDefs["foo"])
   195  		assert.NoError(t, err)
   196  		assert.Equal(t, true, called)
   197  	})
   198  
   199  	t.Run("CreatePreset", func(t *testing.T) {
   200  		called = false
   201  		err := mock.CreatePreset("foo", testData.PresetDefs["foo"])
   202  		assert.NoError(t, err)
   203  		assert.Equal(t, true, called)
   204  	})
   205  
   206  	t.Run("UpdatePreset", func(t *testing.T) {
   207  		called = false
   208  		err := mock.UpdatePreset("foo", testData.PresetDefs["foo"])
   209  		assert.NoError(t, err)
   210  		assert.Equal(t, true, called)
   211  	})
   212  
   213  	t.Run("DeletePreset", func(t *testing.T) {
   214  		called = false
   215  		err := mock.DeletePreset("foo")
   216  		assert.NoError(t, err)
   217  		assert.Equal(t, true, called)
   218  	})
   219  }
   220  
   221  func TestMockSourcesReaderWriter(t *testing.T) {
   222  	testData := sources.Sources{{Name: "foo", ConnStr: "postgres://foo@bar"}}
   223  	called := false
   224  	mock := testutil.MockSourcesReaderWriter{
   225  		GetSourcesFunc: func() (sources.Sources, error) {
   226  			called = true
   227  			return testData, nil
   228  		},
   229  		WriteSourcesFunc: func(sources.Sources) error {
   230  			called = true
   231  			return nil
   232  		},
   233  		UpdateSourceFunc: func(sources.Source) error {
   234  			called = true
   235  			return nil
   236  		},
   237  		CreateSourceFunc: func(sources.Source) error {
   238  			called = true
   239  			return nil
   240  		},
   241  		DeleteSourceFunc: func(string) error {
   242  			called = true
   243  			return nil
   244  		},
   245  	}
   246  
   247  	t.Run("GetSources", func(t *testing.T) {
   248  		called = false
   249  		sources, err := mock.GetSources()
   250  		assert.NoError(t, err)
   251  		assert.Equal(t, true, called)
   252  		assert.Equal(t, testData, sources)
   253  	})
   254  
   255  	t.Run("WriteSources", func(t *testing.T) {
   256  		called = false
   257  		err := mock.WriteSources(testData)
   258  		assert.NoError(t, err)
   259  		assert.Equal(t, true, called)
   260  	})
   261  
   262  	t.Run("UpdateSource", func(t *testing.T) {
   263  		called = false
   264  		err := mock.UpdateSource(testData[0])
   265  		assert.NoError(t, err)
   266  		assert.Equal(t, true, called)
   267  	})
   268  
   269  	t.Run("DeleteSource", func(t *testing.T) {
   270  		called = false
   271  		err := mock.DeleteSource("foo")
   272  		assert.NoError(t, err)
   273  		assert.Equal(t, true, called)
   274  	})
   275  
   276  	t.Run("CreateSource", func(t *testing.T) {
   277  		called = false
   278  		err := mock.CreateSource(testData[0])
   279  		assert.NoError(t, err)
   280  		assert.Equal(t, true, called)
   281  	})
   282  }
   283  
   284  // TestBlockingPool covers the wedged-pool fault injector in mocks.go.
   285  // Every BlockingPool method blocks until ctx.Done(); pre-cancelling the
   286  // context makes each call return immediately with ctx.Err() so the
   287  // assertions are deterministic and finish in microseconds.
   288  func TestBlockingPool(t *testing.T) {
   289  	pool := testutil.BlockingPool{}
   290  
   291  	cancelledCtx := func() context.Context {
   292  		ctx, cancel := context.WithCancel(context.Background())
   293  		cancel()
   294  		return ctx
   295  	}
   296  
   297  	t.Run("Ping", func(t *testing.T) {
   298  		assert.ErrorIs(t, pool.Ping(cancelledCtx()), context.Canceled)
   299  	})
   300  
   301  	t.Run("Query", func(t *testing.T) {
   302  		rows, err := pool.Query(cancelledCtx(), "select 1")
   303  		assert.ErrorIs(t, err, context.Canceled)
   304  		assert.Nil(t, rows)
   305  	})
   306  
   307  	t.Run("QueryRow", func(t *testing.T) {
   308  		var dst any
   309  		assert.ErrorIs(t, pool.QueryRow(cancelledCtx(), "select 1").Scan(&dst), context.Canceled)
   310  	})
   311  
   312  	t.Run("Exec", func(t *testing.T) {
   313  		tag, err := pool.Exec(cancelledCtx(), "x")
   314  		assert.ErrorIs(t, err, context.Canceled)
   315  		assert.Empty(t, tag)
   316  	})
   317  
   318  	t.Run("Acquire", func(t *testing.T) {
   319  		conn, err := pool.Acquire(cancelledCtx())
   320  		assert.ErrorIs(t, err, context.Canceled)
   321  		assert.Nil(t, conn)
   322  	})
   323  
   324  	t.Run("Close", func(t *testing.T) {
   325  		assert.NotPanics(t, func() { pool.Close() })
   326  	})
   327  
   328  	t.Run("SendBatch", func(t *testing.T) {
   329  		br := pool.SendBatch(cancelledCtx(), &pgx.Batch{}).(*testutil.BlockingBatchResults)
   330  
   331  		_, err := br.Query()
   332  		assert.ErrorIs(t, err, context.Canceled)
   333  
   334  		_, err = br.Exec()
   335  		assert.ErrorIs(t, err, context.Canceled)
   336  
   337  		var dst any
   338  		assert.ErrorIs(t, br.QueryRow().Scan(&dst), context.Canceled)
   339  
   340  		assert.NoError(t, br.Err()) // before Close
   341  		assert.NoError(t, br.Close())
   342  		assert.ErrorIs(t, br.Err(), context.Canceled) // after Close
   343  	})
   344  }
   345