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
285
286
287
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())
341 assert.NoError(t, br.Close())
342 assert.ErrorIs(t, br.Err(), context.Canceled)
343 })
344 }
345